@neoptocom/neopto-ui 0.9.0 → 0.9.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.
package/dist/index.cjs CHANGED
@@ -147,6 +147,86 @@ function BackgroundBlur({
147
147
  }
148
148
  );
149
149
  }
150
+ function Card({
151
+ children,
152
+ className = "",
153
+ style,
154
+ showDecorations = true,
155
+ ...props
156
+ }) {
157
+ const mergedStyle = {
158
+ borderRadius: "30px",
159
+ backgroundColor: "color-mix(in srgb, var(--surface) 85%, transparent)",
160
+ backdropFilter: "blur(75px)",
161
+ WebkitBackdropFilter: "blur(75px)",
162
+ // Safari support
163
+ color: "var(--fg)",
164
+ position: "relative",
165
+ overflow: "hidden",
166
+ transition: "background-color 0.3s ease, color 0.3s ease",
167
+ ...style
168
+ };
169
+ return /* @__PURE__ */ jsxRuntime.jsxs(
170
+ "div",
171
+ {
172
+ className: `p-6 ${className}`,
173
+ style: mergedStyle,
174
+ ...props,
175
+ children: [
176
+ showDecorations && /* @__PURE__ */ jsxRuntime.jsxs(
177
+ "svg",
178
+ {
179
+ style: {
180
+ position: "absolute",
181
+ top: 0,
182
+ left: 0,
183
+ width: "100%",
184
+ height: "100%",
185
+ pointerEvents: "none",
186
+ zIndex: 0
187
+ },
188
+ viewBox: "0 0 967 745",
189
+ fill: "none",
190
+ xmlns: "http://www.w3.org/2000/svg",
191
+ preserveAspectRatio: "none",
192
+ children: [
193
+ /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
194
+ /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "paint0_linear_card", x1: "109", y1: "744.5", x2: "855", y2: "744.5", gradientUnits: "userSpaceOnUse", children: [
195
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { stopColor: "#4BDD74", stopOpacity: "0" }),
196
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0.5", stopColor: "#4BDD74" }),
197
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "1", stopColor: "#4BDD74", stopOpacity: "0" })
198
+ ] }),
199
+ /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "paint1_linear_card", x1: "967.5", y1: "10", x2: "967.5", y2: "652", gradientUnits: "userSpaceOnUse", children: [
200
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { stopColor: "#4BDD74", stopOpacity: "0" }),
201
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0.5", stopColor: "#4BDD74" }),
202
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "1", stopColor: "#4BDD74", stopOpacity: "0" })
203
+ ] }),
204
+ /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "paint2_linear_card", x1: "877", y1: "0.5", x2: "90", y2: "0.5", gradientUnits: "userSpaceOnUse", children: [
205
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { stopColor: "#4BDD74", stopOpacity: "0" }),
206
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0.5", stopColor: "#4BDD74" }),
207
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "1", stopColor: "#4BDD74", stopOpacity: "0" })
208
+ ] }),
209
+ /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "paint3_linear_card", x1: "0.5", y1: "34.5136", x2: "0.5", y2: "731.595", gradientUnits: "userSpaceOnUse", children: [
210
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { stopColor: "#4BDD74", stopOpacity: "0" }),
211
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0.5", stopColor: "#4BDD74" }),
212
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "1", stopColor: "#4BDD74", stopOpacity: "0" })
213
+ ] })
214
+ ] }),
215
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { clipPath: "url(#clip0_card)", children: [
216
+ /* @__PURE__ */ jsxRuntime.jsx("line", { opacity: "0.8", x1: "855", y1: "744.5", x2: "109", y2: "744.5", stroke: "url(#paint0_linear_card)" }),
217
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "965.5", y1: "652", x2: "965.5", y2: "10", stroke: "url(#paint1_linear_card)" })
218
+ ] }),
219
+ /* @__PURE__ */ jsxRuntime.jsx("line", { opacity: "0.6", x1: "90", y1: "0.5", x2: "877", y2: "0.5", stroke: "url(#paint2_linear_card)" }),
220
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "0.5", y1: "731.595", x2: "0.500027", y2: "34.5136", stroke: "url(#paint3_linear_card)" }),
221
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "clip0_card", children: /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "966", height: "744", rx: "10", transform: "matrix(-1 0 0 1 967 1)", fill: "white" }) }) })
222
+ ]
223
+ }
224
+ ),
225
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", zIndex: 1 }, children })
226
+ ]
227
+ }
228
+ );
229
+ }
150
230
  var Input = React2__namespace.forwardRef(
151
231
  ({ className, disabled, ...props }, ref) => {
152
232
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -179,10 +259,28 @@ function Modal({
179
259
  open,
180
260
  onClose,
181
261
  title,
182
- closeOnOverlay = true,
183
- children
262
+ closeOnBackdrop = true,
263
+ children,
264
+ className = "",
265
+ zIndex = 50,
266
+ showDecorations = true
184
267
  }) {
185
268
  const [mounted, setMounted] = React2__namespace.useState(false);
269
+ const [isDark, setIsDark] = React2__namespace.useState(false);
270
+ React2__namespace.useEffect(() => {
271
+ const checkDarkMode = () => {
272
+ const hasDarkClass = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark") || document.querySelector(".dark") !== null;
273
+ setIsDark(hasDarkClass);
274
+ };
275
+ checkDarkMode();
276
+ const observer = new MutationObserver(checkDarkMode);
277
+ observer.observe(document.documentElement, {
278
+ attributes: true,
279
+ attributeFilter: ["class"],
280
+ subtree: true
281
+ });
282
+ return () => observer.disconnect();
283
+ }, []);
186
284
  useIsomorphicLayoutEffect(() => {
187
285
  setMounted(true);
188
286
  if (!open) return;
@@ -201,40 +299,30 @@ function Modal({
201
299
  return () => window.removeEventListener("keydown", onKey);
202
300
  }, [open, onClose]);
203
301
  if (!mounted) return null;
204
- if (!open) return null;
205
- const overlay = /* @__PURE__ */ jsxRuntime.jsxs(
206
- "div",
302
+ const modal = /* @__PURE__ */ jsxRuntime.jsx("div", { className: isDark ? "dark" : "", children: /* @__PURE__ */ jsxRuntime.jsx(
303
+ BackgroundBlur,
207
304
  {
208
- className: "fixed inset-0 z-50 flex items-center justify-center",
209
- "aria-labelledby": "modal-title",
210
- role: "dialog",
211
- "aria-modal": "true",
212
- children: [
213
- /* @__PURE__ */ jsxRuntime.jsx(
214
- "div",
215
- {
216
- className: "absolute inset-0 bg-black/50",
217
- onClick: () => closeOnOverlay && onClose?.()
218
- }
219
- ),
220
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative z-10 w-full max-w-lg rounded-[var(--radius-lg)] bg-[var(--surface)] text-[var(--fg)] p-6 shadow-xl", children: [
221
- title ? /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "modal-title", className: "mb-2 text-lg font-semibold", children: title }) : null,
222
- /* @__PURE__ */ jsxRuntime.jsx("div", { children }),
223
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex justify-end gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
224
- "button",
225
- {
226
- onClick: onClose,
227
- className: "btn btn-outline",
228
- type: "button",
229
- children: "Close"
230
- }
231
- ) })
232
- ] })
233
- ]
305
+ open,
306
+ onClose: closeOnBackdrop ? onClose : void 0,
307
+ zIndex,
308
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
309
+ Card,
310
+ {
311
+ className: `w-full max-w-lg ${className}`,
312
+ role: "dialog",
313
+ "aria-modal": "true",
314
+ "aria-labelledby": title ? "modal-title" : void 0,
315
+ showDecorations,
316
+ children: [
317
+ title && /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "modal-title", className: "mb-4 text-xl font-semibold", children: title }),
318
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children })
319
+ ]
320
+ }
321
+ )
234
322
  }
235
- );
323
+ ) });
236
324
  const container = document.body;
237
- return reactDom.createPortal(overlay, container);
325
+ return reactDom.createPortal(modal, container);
238
326
  }
239
327
  var typoStyles = {
240
328
  "display-lg": { fontSize: "57px", lineHeight: "64px", letterSpacing: "-0.25px" },
@@ -1211,6 +1299,7 @@ exports.Avatar = Avatar;
1211
1299
  exports.AvatarGroup = AvatarGroup;
1212
1300
  exports.BackgroundBlur = BackgroundBlur;
1213
1301
  exports.Button = Button;
1302
+ exports.Card = Card;
1214
1303
  exports.ChatButton = ChatButton_default;
1215
1304
  exports.Chip = Chip;
1216
1305
  exports.Counter = Counter;
package/dist/index.d.cts CHANGED
@@ -40,18 +40,38 @@ type BackgroundBlurProps = {
40
40
  };
41
41
  declare function BackgroundBlur({ open, children, onClose, zIndex, className, }: BackgroundBlurProps): react_jsx_runtime.JSX.Element | null;
42
42
 
43
+ type CardProps = React.HTMLAttributes<HTMLDivElement> & {
44
+ /** Content to render inside the card */
45
+ children: React.ReactNode;
46
+ /** Additional CSS classes */
47
+ className?: string;
48
+ /** Show decorative elements (default: true) */
49
+ showDecorations?: boolean;
50
+ };
51
+ declare function Card({ children, className, style, showDecorations, ...props }: CardProps): react_jsx_runtime.JSX.Element;
52
+
43
53
  type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>;
44
54
  declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
45
55
 
46
56
  type ModalProps = {
57
+ /** Whether the modal is open */
47
58
  open: boolean;
59
+ /** Callback when modal should close */
48
60
  onClose?: () => void;
61
+ /** Modal content */
49
62
  children?: React.ReactNode;
63
+ /** Optional title (rendered as heading) */
50
64
  title?: string;
51
- /** When true, closes when the overlay is clicked */
52
- closeOnOverlay?: boolean;
65
+ /** When true, closes when the backdrop is clicked (default: true) */
66
+ closeOnBackdrop?: boolean;
67
+ /** Custom className for the Card */
68
+ className?: string;
69
+ /** z-index for the modal (default: 50) */
70
+ zIndex?: number;
71
+ /** Show decorative elements on the Card (default: true) */
72
+ showDecorations?: boolean;
53
73
  };
54
- declare function Modal({ open, onClose, title, closeOnOverlay, children }: ModalProps): React.ReactPortal | null;
74
+ declare function Modal({ open, onClose, title, closeOnBackdrop, children, className, zIndex, showDecorations, }: ModalProps): React.ReactPortal | null;
55
75
 
56
76
  type TypoVariant = "display-lg" | "display-md" | "display-sm" | "headline-lg" | "headline-md" | "headline-sm" | "title-lg" | "title-md" | "title-sm" | "label-lg" | "label-md" | "label-sm" | "body-lg" | "body-md" | "body-sm" | "button";
57
77
  type TypoWeight = "normal" | "medium" | "semibold" | "bold";
@@ -273,4 +293,4 @@ interface AnimatedBgRectangleProps {
273
293
  }
274
294
  declare const AnimatedBgRectangle: ({ colors, delay }: AnimatedBgRectangleProps) => react_jsx_runtime.JSX.Element;
275
295
 
276
- export { AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Button, type ButtonProps, ChatButton, type ChatButtonProps, Chip, type ChipProps, Counter, type CounterProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, Modal, type ModalProps, Search, type SearchOption, type SearchProps, Skeleton, type SkeletonProps, Typo, type TypoProps, type TypoVariant, type TypoWeight, index as assets };
296
+ export { AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Button, type ButtonProps, Card, type CardProps, ChatButton, type ChatButtonProps, Chip, type ChipProps, Counter, type CounterProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, Modal, type ModalProps, Search, type SearchOption, type SearchProps, Skeleton, type SkeletonProps, Typo, type TypoProps, type TypoVariant, type TypoWeight, index as assets };
package/dist/index.d.ts CHANGED
@@ -40,18 +40,38 @@ type BackgroundBlurProps = {
40
40
  };
41
41
  declare function BackgroundBlur({ open, children, onClose, zIndex, className, }: BackgroundBlurProps): react_jsx_runtime.JSX.Element | null;
42
42
 
43
+ type CardProps = React.HTMLAttributes<HTMLDivElement> & {
44
+ /** Content to render inside the card */
45
+ children: React.ReactNode;
46
+ /** Additional CSS classes */
47
+ className?: string;
48
+ /** Show decorative elements (default: true) */
49
+ showDecorations?: boolean;
50
+ };
51
+ declare function Card({ children, className, style, showDecorations, ...props }: CardProps): react_jsx_runtime.JSX.Element;
52
+
43
53
  type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>;
44
54
  declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
45
55
 
46
56
  type ModalProps = {
57
+ /** Whether the modal is open */
47
58
  open: boolean;
59
+ /** Callback when modal should close */
48
60
  onClose?: () => void;
61
+ /** Modal content */
49
62
  children?: React.ReactNode;
63
+ /** Optional title (rendered as heading) */
50
64
  title?: string;
51
- /** When true, closes when the overlay is clicked */
52
- closeOnOverlay?: boolean;
65
+ /** When true, closes when the backdrop is clicked (default: true) */
66
+ closeOnBackdrop?: boolean;
67
+ /** Custom className for the Card */
68
+ className?: string;
69
+ /** z-index for the modal (default: 50) */
70
+ zIndex?: number;
71
+ /** Show decorative elements on the Card (default: true) */
72
+ showDecorations?: boolean;
53
73
  };
54
- declare function Modal({ open, onClose, title, closeOnOverlay, children }: ModalProps): React.ReactPortal | null;
74
+ declare function Modal({ open, onClose, title, closeOnBackdrop, children, className, zIndex, showDecorations, }: ModalProps): React.ReactPortal | null;
55
75
 
56
76
  type TypoVariant = "display-lg" | "display-md" | "display-sm" | "headline-lg" | "headline-md" | "headline-sm" | "title-lg" | "title-md" | "title-sm" | "label-lg" | "label-md" | "label-sm" | "body-lg" | "body-md" | "body-sm" | "button";
57
77
  type TypoWeight = "normal" | "medium" | "semibold" | "bold";
@@ -273,4 +293,4 @@ interface AnimatedBgRectangleProps {
273
293
  }
274
294
  declare const AnimatedBgRectangle: ({ colors, delay }: AnimatedBgRectangleProps) => react_jsx_runtime.JSX.Element;
275
295
 
276
- export { AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Button, type ButtonProps, ChatButton, type ChatButtonProps, Chip, type ChipProps, Counter, type CounterProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, Modal, type ModalProps, Search, type SearchOption, type SearchProps, Skeleton, type SkeletonProps, Typo, type TypoProps, type TypoVariant, type TypoWeight, index as assets };
296
+ export { AnimatedBgCircle, AnimatedBgRectangle, AppBackground, type AppBackgroundProps, Autocomplete, type AutocompleteOption, type AutocompleteProps, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, BackgroundBlur, type BackgroundBlurProps, Button, type ButtonProps, Card, type CardProps, ChatButton, type ChatButtonProps, Chip, type ChipProps, Counter, type CounterProps, Icon, IconButton, type IconButtonProps, type IconProps, Input, type InputProps, Modal, type ModalProps, Search, type SearchOption, type SearchProps, Skeleton, type SkeletonProps, Typo, type TypoProps, type TypoVariant, type TypoWeight, index as assets };
package/dist/index.js CHANGED
@@ -126,6 +126,86 @@ function BackgroundBlur({
126
126
  }
127
127
  );
128
128
  }
129
+ function Card({
130
+ children,
131
+ className = "",
132
+ style,
133
+ showDecorations = true,
134
+ ...props
135
+ }) {
136
+ const mergedStyle = {
137
+ borderRadius: "30px",
138
+ backgroundColor: "color-mix(in srgb, var(--surface) 85%, transparent)",
139
+ backdropFilter: "blur(75px)",
140
+ WebkitBackdropFilter: "blur(75px)",
141
+ // Safari support
142
+ color: "var(--fg)",
143
+ position: "relative",
144
+ overflow: "hidden",
145
+ transition: "background-color 0.3s ease, color 0.3s ease",
146
+ ...style
147
+ };
148
+ return /* @__PURE__ */ jsxs(
149
+ "div",
150
+ {
151
+ className: `p-6 ${className}`,
152
+ style: mergedStyle,
153
+ ...props,
154
+ children: [
155
+ showDecorations && /* @__PURE__ */ jsxs(
156
+ "svg",
157
+ {
158
+ style: {
159
+ position: "absolute",
160
+ top: 0,
161
+ left: 0,
162
+ width: "100%",
163
+ height: "100%",
164
+ pointerEvents: "none",
165
+ zIndex: 0
166
+ },
167
+ viewBox: "0 0 967 745",
168
+ fill: "none",
169
+ xmlns: "http://www.w3.org/2000/svg",
170
+ preserveAspectRatio: "none",
171
+ children: [
172
+ /* @__PURE__ */ jsxs("defs", { children: [
173
+ /* @__PURE__ */ jsxs("linearGradient", { id: "paint0_linear_card", x1: "109", y1: "744.5", x2: "855", y2: "744.5", gradientUnits: "userSpaceOnUse", children: [
174
+ /* @__PURE__ */ jsx("stop", { stopColor: "#4BDD74", stopOpacity: "0" }),
175
+ /* @__PURE__ */ jsx("stop", { offset: "0.5", stopColor: "#4BDD74" }),
176
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#4BDD74", stopOpacity: "0" })
177
+ ] }),
178
+ /* @__PURE__ */ jsxs("linearGradient", { id: "paint1_linear_card", x1: "967.5", y1: "10", x2: "967.5", y2: "652", gradientUnits: "userSpaceOnUse", children: [
179
+ /* @__PURE__ */ jsx("stop", { stopColor: "#4BDD74", stopOpacity: "0" }),
180
+ /* @__PURE__ */ jsx("stop", { offset: "0.5", stopColor: "#4BDD74" }),
181
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#4BDD74", stopOpacity: "0" })
182
+ ] }),
183
+ /* @__PURE__ */ jsxs("linearGradient", { id: "paint2_linear_card", x1: "877", y1: "0.5", x2: "90", y2: "0.5", gradientUnits: "userSpaceOnUse", children: [
184
+ /* @__PURE__ */ jsx("stop", { stopColor: "#4BDD74", stopOpacity: "0" }),
185
+ /* @__PURE__ */ jsx("stop", { offset: "0.5", stopColor: "#4BDD74" }),
186
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#4BDD74", stopOpacity: "0" })
187
+ ] }),
188
+ /* @__PURE__ */ jsxs("linearGradient", { id: "paint3_linear_card", x1: "0.5", y1: "34.5136", x2: "0.5", y2: "731.595", gradientUnits: "userSpaceOnUse", children: [
189
+ /* @__PURE__ */ jsx("stop", { stopColor: "#4BDD74", stopOpacity: "0" }),
190
+ /* @__PURE__ */ jsx("stop", { offset: "0.5", stopColor: "#4BDD74" }),
191
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#4BDD74", stopOpacity: "0" })
192
+ ] })
193
+ ] }),
194
+ /* @__PURE__ */ jsxs("g", { clipPath: "url(#clip0_card)", children: [
195
+ /* @__PURE__ */ jsx("line", { opacity: "0.8", x1: "855", y1: "744.5", x2: "109", y2: "744.5", stroke: "url(#paint0_linear_card)" }),
196
+ /* @__PURE__ */ jsx("line", { x1: "965.5", y1: "652", x2: "965.5", y2: "10", stroke: "url(#paint1_linear_card)" })
197
+ ] }),
198
+ /* @__PURE__ */ jsx("line", { opacity: "0.6", x1: "90", y1: "0.5", x2: "877", y2: "0.5", stroke: "url(#paint2_linear_card)" }),
199
+ /* @__PURE__ */ jsx("line", { x1: "0.5", y1: "731.595", x2: "0.500027", y2: "34.5136", stroke: "url(#paint3_linear_card)" }),
200
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("clipPath", { id: "clip0_card", children: /* @__PURE__ */ jsx("rect", { width: "966", height: "744", rx: "10", transform: "matrix(-1 0 0 1 967 1)", fill: "white" }) }) })
201
+ ]
202
+ }
203
+ ),
204
+ /* @__PURE__ */ jsx("div", { style: { position: "relative", zIndex: 1 }, children })
205
+ ]
206
+ }
207
+ );
208
+ }
129
209
  var Input = React2.forwardRef(
130
210
  ({ className, disabled, ...props }, ref) => {
131
211
  return /* @__PURE__ */ jsx(
@@ -158,10 +238,28 @@ function Modal({
158
238
  open,
159
239
  onClose,
160
240
  title,
161
- closeOnOverlay = true,
162
- children
241
+ closeOnBackdrop = true,
242
+ children,
243
+ className = "",
244
+ zIndex = 50,
245
+ showDecorations = true
163
246
  }) {
164
247
  const [mounted, setMounted] = React2.useState(false);
248
+ const [isDark, setIsDark] = React2.useState(false);
249
+ React2.useEffect(() => {
250
+ const checkDarkMode = () => {
251
+ const hasDarkClass = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark") || document.querySelector(".dark") !== null;
252
+ setIsDark(hasDarkClass);
253
+ };
254
+ checkDarkMode();
255
+ const observer = new MutationObserver(checkDarkMode);
256
+ observer.observe(document.documentElement, {
257
+ attributes: true,
258
+ attributeFilter: ["class"],
259
+ subtree: true
260
+ });
261
+ return () => observer.disconnect();
262
+ }, []);
165
263
  useIsomorphicLayoutEffect(() => {
166
264
  setMounted(true);
167
265
  if (!open) return;
@@ -180,40 +278,30 @@ function Modal({
180
278
  return () => window.removeEventListener("keydown", onKey);
181
279
  }, [open, onClose]);
182
280
  if (!mounted) return null;
183
- if (!open) return null;
184
- const overlay = /* @__PURE__ */ jsxs(
185
- "div",
281
+ const modal = /* @__PURE__ */ jsx("div", { className: isDark ? "dark" : "", children: /* @__PURE__ */ jsx(
282
+ BackgroundBlur,
186
283
  {
187
- className: "fixed inset-0 z-50 flex items-center justify-center",
188
- "aria-labelledby": "modal-title",
189
- role: "dialog",
190
- "aria-modal": "true",
191
- children: [
192
- /* @__PURE__ */ jsx(
193
- "div",
194
- {
195
- className: "absolute inset-0 bg-black/50",
196
- onClick: () => closeOnOverlay && onClose?.()
197
- }
198
- ),
199
- /* @__PURE__ */ jsxs("div", { className: "relative z-10 w-full max-w-lg rounded-[var(--radius-lg)] bg-[var(--surface)] text-[var(--fg)] p-6 shadow-xl", children: [
200
- title ? /* @__PURE__ */ jsx("h2", { id: "modal-title", className: "mb-2 text-lg font-semibold", children: title }) : null,
201
- /* @__PURE__ */ jsx("div", { children }),
202
- /* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-end gap-2", children: /* @__PURE__ */ jsx(
203
- "button",
204
- {
205
- onClick: onClose,
206
- className: "btn btn-outline",
207
- type: "button",
208
- children: "Close"
209
- }
210
- ) })
211
- ] })
212
- ]
284
+ open,
285
+ onClose: closeOnBackdrop ? onClose : void 0,
286
+ zIndex,
287
+ children: /* @__PURE__ */ jsxs(
288
+ Card,
289
+ {
290
+ className: `w-full max-w-lg ${className}`,
291
+ role: "dialog",
292
+ "aria-modal": "true",
293
+ "aria-labelledby": title ? "modal-title" : void 0,
294
+ showDecorations,
295
+ children: [
296
+ title && /* @__PURE__ */ jsx("h2", { id: "modal-title", className: "mb-4 text-xl font-semibold", children: title }),
297
+ /* @__PURE__ */ jsx("div", { children })
298
+ ]
299
+ }
300
+ )
213
301
  }
214
- );
302
+ ) });
215
303
  const container = document.body;
216
- return createPortal(overlay, container);
304
+ return createPortal(modal, container);
217
305
  }
218
306
  var typoStyles = {
219
307
  "display-lg": { fontSize: "57px", lineHeight: "64px", letterSpacing: "-0.25px" },
@@ -1182,4 +1270,4 @@ var ChatButton = ({
1182
1270
  };
1183
1271
  var ChatButton_default = ChatButton;
1184
1272
 
1185
- export { AnimatedBgCircle_default as AnimatedBgCircle, AnimatedBgRectangle_default as AnimatedBgRectangle, AppBackground, Autocomplete, Avatar, AvatarGroup, BackgroundBlur, Button, ChatButton_default as ChatButton, Chip, Counter, Icon, IconButton, Input, Modal, Search, Skeleton, Typo, assets_exports as assets };
1273
+ export { AnimatedBgCircle_default as AnimatedBgCircle, AnimatedBgRectangle_default as AnimatedBgRectangle, AppBackground, Autocomplete, Avatar, AvatarGroup, BackgroundBlur, Button, Card, ChatButton_default as ChatButton, Chip, Counter, Icon, IconButton, Input, Modal, Search, Skeleton, Typo, assets_exports as assets };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neoptocom/neopto-ui",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "private": false,
5
5
  "description": "A modern React component library built with Tailwind CSS v4 and TypeScript. Features dark mode, design tokens, and comprehensive Storybook documentation. Requires Tailwind v4+.",
6
6
  "keywords": [
@@ -0,0 +1,93 @@
1
+ import * as React from "react";
2
+
3
+ export type CardProps = React.HTMLAttributes<HTMLDivElement> & {
4
+ /** Content to render inside the card */
5
+ children: React.ReactNode;
6
+ /** Additional CSS classes */
7
+ className?: string;
8
+ /** Show decorative elements (default: true) */
9
+ showDecorations?: boolean;
10
+ };
11
+
12
+ export function Card({
13
+ children,
14
+ className = "",
15
+ style,
16
+ showDecorations = true,
17
+ ...props
18
+ }: CardProps) {
19
+ // Merge user styles with card styles
20
+ const mergedStyle: React.CSSProperties = {
21
+ borderRadius: "30px",
22
+ backgroundColor: "color-mix(in srgb, var(--surface) 85%, transparent)",
23
+ backdropFilter: "blur(75px)",
24
+ WebkitBackdropFilter: "blur(75px)", // Safari support
25
+ color: "var(--fg)",
26
+ position: "relative",
27
+ overflow: "hidden",
28
+ transition: "background-color 0.3s ease, color 0.3s ease",
29
+ ...style,
30
+ };
31
+
32
+ return (
33
+ <div
34
+ className={`p-6 ${className}`}
35
+ style={mergedStyle}
36
+ {...props}
37
+ >
38
+ {showDecorations && (
39
+ <svg
40
+ style={{
41
+ position: "absolute",
42
+ top: 0,
43
+ left: 0,
44
+ width: "100%",
45
+ height: "100%",
46
+ pointerEvents: "none",
47
+ zIndex: 0,
48
+ }}
49
+ viewBox="0 0 967 745"
50
+ fill="none"
51
+ xmlns="http://www.w3.org/2000/svg"
52
+ preserveAspectRatio="none"
53
+ >
54
+ <defs>
55
+ <linearGradient id="paint0_linear_card" x1="109" y1="744.5" x2="855" y2="744.5" gradientUnits="userSpaceOnUse">
56
+ <stop stopColor="#4BDD74" stopOpacity="0"/>
57
+ <stop offset="0.5" stopColor="#4BDD74"/>
58
+ <stop offset="1" stopColor="#4BDD74" stopOpacity="0"/>
59
+ </linearGradient>
60
+ <linearGradient id="paint1_linear_card" x1="967.5" y1="10" x2="967.5" y2="652" gradientUnits="userSpaceOnUse">
61
+ <stop stopColor="#4BDD74" stopOpacity="0"/>
62
+ <stop offset="0.5" stopColor="#4BDD74"/>
63
+ <stop offset="1" stopColor="#4BDD74" stopOpacity="0"/>
64
+ </linearGradient>
65
+ <linearGradient id="paint2_linear_card" x1="877" y1="0.5" x2="90" y2="0.5" gradientUnits="userSpaceOnUse">
66
+ <stop stopColor="#4BDD74" stopOpacity="0"/>
67
+ <stop offset="0.5" stopColor="#4BDD74"/>
68
+ <stop offset="1" stopColor="#4BDD74" stopOpacity="0"/>
69
+ </linearGradient>
70
+ <linearGradient id="paint3_linear_card" x1="0.5" y1="34.5136" x2="0.5" y2="731.595" gradientUnits="userSpaceOnUse">
71
+ <stop stopColor="#4BDD74" stopOpacity="0"/>
72
+ <stop offset="0.5" stopColor="#4BDD74"/>
73
+ <stop offset="1" stopColor="#4BDD74" stopOpacity="0"/>
74
+ </linearGradient>
75
+ </defs>
76
+ <g clipPath="url(#clip0_card)"><line opacity="0.8" x1="855" y1="744.5" x2="109" y2="744.5" stroke="url(#paint0_linear_card)"/>
77
+ <line x1="965.5" y1="652" x2="965.5" y2="10" stroke="url(#paint1_linear_card)"/>
78
+ </g>
79
+ <line opacity="0.6" x1="90" y1="0.5" x2="877" y2="0.5" stroke="url(#paint2_linear_card)"/>
80
+ <line x1="0.5" y1="731.595" x2="0.500027" y2="34.5136" stroke="url(#paint3_linear_card)"/>
81
+ <defs>
82
+ <clipPath id="clip0_card">
83
+ <rect width="966" height="744" rx="10" transform="matrix(-1 0 0 1 967 1)" fill="white"/>
84
+ </clipPath>
85
+ </defs>
86
+ </svg>
87
+ )}
88
+ <div style={{ position: "relative", zIndex: 1 }}>
89
+ {children}
90
+ </div>
91
+ </div>
92
+ );
93
+ }
@@ -1,13 +1,25 @@
1
1
  import * as React from "react";
2
2
  import { createPortal } from "react-dom";
3
+ import { BackgroundBlur } from "./BackgroundBlur";
4
+ import { Card } from "./Card";
3
5
 
4
6
  export type ModalProps = {
7
+ /** Whether the modal is open */
5
8
  open: boolean;
9
+ /** Callback when modal should close */
6
10
  onClose?: () => void;
11
+ /** Modal content */
7
12
  children?: React.ReactNode;
13
+ /** Optional title (rendered as heading) */
8
14
  title?: string;
9
- /** When true, closes when the overlay is clicked */
10
- closeOnOverlay?: boolean;
15
+ /** When true, closes when the backdrop is clicked (default: true) */
16
+ closeOnBackdrop?: boolean;
17
+ /** Custom className for the Card */
18
+ className?: string;
19
+ /** z-index for the modal (default: 50) */
20
+ zIndex?: number;
21
+ /** Show decorative elements on the Card (default: true) */
22
+ showDecorations?: boolean;
11
23
  };
12
24
 
13
25
  function useIsomorphicLayoutEffect(effect: React.EffectCallback, deps?: React.DependencyList) {
@@ -19,10 +31,36 @@ export function Modal({
19
31
  open,
20
32
  onClose,
21
33
  title,
22
- closeOnOverlay = true,
23
- children
34
+ closeOnBackdrop = true,
35
+ children,
36
+ className = "",
37
+ zIndex = 50,
38
+ showDecorations = true,
24
39
  }: ModalProps) {
25
40
  const [mounted, setMounted] = React.useState(false);
41
+ const [isDark, setIsDark] = React.useState(false);
42
+
43
+ // Detect dark mode
44
+ React.useEffect(() => {
45
+ const checkDarkMode = () => {
46
+ const hasDarkClass = document.documentElement.classList.contains("dark") ||
47
+ document.body.classList.contains("dark") ||
48
+ document.querySelector(".dark") !== null;
49
+ setIsDark(hasDarkClass);
50
+ };
51
+
52
+ checkDarkMode();
53
+
54
+ // Listen for changes to dark mode
55
+ const observer = new MutationObserver(checkDarkMode);
56
+ observer.observe(document.documentElement, {
57
+ attributes: true,
58
+ attributeFilter: ["class"],
59
+ subtree: true,
60
+ });
61
+
62
+ return () => observer.disconnect();
63
+ }, []);
26
64
 
27
65
  // Prevent body scroll when open
28
66
  useIsomorphicLayoutEffect(() => {
@@ -35,6 +73,7 @@ export function Modal({
35
73
  };
36
74
  }, [open]);
37
75
 
76
+ // ESC key to close
38
77
  React.useEffect(() => {
39
78
  if (!open) return;
40
79
  const onKey = (e: KeyboardEvent) => {
@@ -45,39 +84,32 @@ export function Modal({
45
84
  }, [open, onClose]);
46
85
 
47
86
  if (!mounted) return null;
48
- if (!open) return null;
49
87
 
50
- const overlay = (
51
- <div
52
- className="fixed inset-0 z-50 flex items-center justify-center"
53
- aria-labelledby="modal-title"
54
- role="dialog"
55
- aria-modal="true"
56
- >
57
- <div
58
- className="absolute inset-0 bg-black/50"
59
- onClick={() => closeOnOverlay && onClose?.()}
60
- />
61
- <div className="relative z-10 w-full max-w-lg rounded-[var(--radius-lg)] bg-[var(--surface)] text-[var(--fg)] p-6 shadow-xl">
62
- {title ? (
63
- <h2 id="modal-title" className="mb-2 text-lg font-semibold">
88
+ const modal = (
89
+ <div className={isDark ? "dark" : ""}>
90
+ <BackgroundBlur
91
+ open={open}
92
+ onClose={closeOnBackdrop ? onClose : undefined}
93
+ zIndex={zIndex}
94
+ >
95
+ <Card
96
+ className={`w-full max-w-lg ${className}`}
97
+ role="dialog"
98
+ aria-modal="true"
99
+ aria-labelledby={title ? "modal-title" : undefined}
100
+ showDecorations={showDecorations}
101
+ >
102
+ {title && (
103
+ <h2 id="modal-title" className="mb-4 text-xl font-semibold">
64
104
  {title}
65
105
  </h2>
66
- ) : null}
106
+ )}
67
107
  <div>{children}</div>
68
- <div className="mt-4 flex justify-end gap-2">
69
- <button
70
- onClick={onClose}
71
- className="btn btn-outline"
72
- type="button"
73
- >
74
- Close
75
- </button>
76
- </div>
77
- </div>
108
+ </Card>
109
+ </BackgroundBlur>
78
110
  </div>
79
111
  );
80
112
 
81
113
  const container = document.body;
82
- return createPortal(overlay, container);
114
+ return createPortal(modal, container);
83
115
  }
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export * as assets from "./assets";
4
4
  // Components
5
5
  export { AppBackground } from "./components/AppBackground";
6
6
  export { BackgroundBlur } from "./components/BackgroundBlur";
7
+ export { Card } from "./components/Card";
7
8
  export * from "./components/Input";
8
9
  export * from "./components/Modal";
9
10
  export { default as Typo } from "./components/Typo";
@@ -22,6 +23,7 @@ export * from "./components/Chat";
22
23
  // Types
23
24
  export type { AppBackgroundProps } from "./components/AppBackground";
24
25
  export type { BackgroundBlurProps } from "./components/BackgroundBlur";
26
+ export type { CardProps } from "./components/Card";
25
27
  export type { InputProps } from "./components/Input";
26
28
  export type { ModalProps } from "./components/Modal";
27
29
  export type { TypoProps, TypoVariant, TypoWeight } from "./components/Typo";
@@ -0,0 +1,275 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { useState } from "react";
3
+ import { Modal } from "../components/Modal";
4
+ import { Button } from "../components/Button";
5
+ import Typo from "../components/Typo";
6
+
7
+ const meta = {
8
+ title: "Components/Modal",
9
+ component: Modal,
10
+ parameters: {
11
+ layout: "fullscreen",
12
+ },
13
+ tags: ["autodocs"],
14
+ } satisfies Meta<typeof Modal>;
15
+
16
+ export default meta;
17
+ type Story = StoryObj<typeof meta>;
18
+
19
+ // Helper component to manage modal state
20
+ function ModalDemo(props: Partial<React.ComponentProps<typeof Modal>>) {
21
+ const [open, setOpen] = useState(false);
22
+
23
+ return (
24
+ <div className="p-8">
25
+ <Button onClick={() => setOpen(true)}>Open Modal</Button>
26
+ <Modal open={open} onClose={() => setOpen(false)} {...props}>
27
+ {props.children}
28
+ </Modal>
29
+ </div>
30
+ );
31
+ }
32
+
33
+ export const Default: Story = {
34
+ args: { open: false },
35
+ render: () => (
36
+ <ModalDemo>
37
+ <Typo variant="headline-md">Welcome!</Typo>
38
+ <Typo variant="body-md" className="mt-4">
39
+ This is a simple modal with custom content. Click outside or press ESC to close.
40
+ </Typo>
41
+ </ModalDemo>
42
+ ),
43
+ };
44
+
45
+ export const WithTitle: Story = {
46
+ args: { open: false },
47
+ render: () => (
48
+ <ModalDemo title="Modal Title">
49
+ <Typo variant="body-md">
50
+ This modal includes a title heading. You can still add any content you want below it.
51
+ </Typo>
52
+ <Typo variant="body-sm" className="mt-4 text-[var(--muted-fg)]">
53
+ Try clicking the backdrop to close, or press ESC.
54
+ </Typo>
55
+ </ModalDemo>
56
+ ),
57
+ };
58
+
59
+ export const NoBackdropClose: Story = {
60
+ args: { open: false },
61
+ render: () => (
62
+ <ModalDemo
63
+ title="Important Notice"
64
+ closeOnBackdrop={false}
65
+ >
66
+ <Typo variant="body-md">
67
+ This modal cannot be closed by clicking the backdrop.
68
+ </Typo>
69
+ <Typo variant="body-md" className="mt-4">
70
+ You must use the button below or press ESC to close.
71
+ </Typo>
72
+ <div className="mt-6">
73
+ <Button onClick={() => {}}>Acknowledge</Button>
74
+ </div>
75
+ </ModalDemo>
76
+ ),
77
+ };
78
+
79
+ export const CustomStyling: Story = {
80
+ args: { open: false },
81
+ render: () => (
82
+ <ModalDemo
83
+ title="Large Modal"
84
+ className="max-w-2xl p-12"
85
+ >
86
+ <Typo variant="body-md">
87
+ This modal has custom styling with a larger max-width and more padding.
88
+ </Typo>
89
+ <div className="mt-6 grid grid-cols-2 gap-4">
90
+ <div className="p-4 bg-[var(--muted)] rounded-2xl">
91
+ <Typo variant="label-lg" bold="semibold">Feature 1</Typo>
92
+ <Typo variant="body-sm" className="mt-2">Description here</Typo>
93
+ </div>
94
+ <div className="p-4 bg-[var(--muted)] rounded-2xl">
95
+ <Typo variant="label-lg" bold="semibold">Feature 2</Typo>
96
+ <Typo variant="body-sm" className="mt-2">Description here</Typo>
97
+ </div>
98
+ </div>
99
+ </ModalDemo>
100
+ ),
101
+ };
102
+
103
+ export const FormModal: Story = {
104
+ args: { open: false },
105
+ render: () => {
106
+ const [open, setOpen] = useState(false);
107
+ const [name, setName] = useState("");
108
+ const [email, setEmail] = useState("");
109
+
110
+ const handleSubmit = (e: React.FormEvent) => {
111
+ e.preventDefault();
112
+ alert(`Submitted: ${name}, ${email}`);
113
+ setOpen(false);
114
+ };
115
+
116
+ return (
117
+ <div className="p-8">
118
+ <Button onClick={() => setOpen(true)}>Open Form Modal</Button>
119
+ <Modal
120
+ open={open}
121
+ onClose={() => setOpen(false)}
122
+ title="Contact Form"
123
+ >
124
+ <form onSubmit={handleSubmit}>
125
+ <div className="space-y-4">
126
+ <div>
127
+ <label className="block mb-2 text-sm font-medium">Name</label>
128
+ <input
129
+ type="text"
130
+ value={name}
131
+ onChange={(e) => setName(e.target.value)}
132
+ className="w-full px-4 py-2 rounded-full border border-[var(--border)] bg-transparent"
133
+ placeholder="Enter your name"
134
+ required
135
+ />
136
+ </div>
137
+ <div>
138
+ <label className="block mb-2 text-sm font-medium">Email</label>
139
+ <input
140
+ type="email"
141
+ value={email}
142
+ onChange={(e) => setEmail(e.target.value)}
143
+ className="w-full px-4 py-2 rounded-full border border-[var(--border)] bg-transparent"
144
+ placeholder="Enter your email"
145
+ required
146
+ />
147
+ </div>
148
+ <div className="flex gap-3 justify-end pt-4">
149
+ <button
150
+ type="button"
151
+ onClick={() => setOpen(false)}
152
+ className="px-6 py-2 rounded-full border border-[var(--border)] hover:bg-[var(--muted)] transition-colors"
153
+ >
154
+ Cancel
155
+ </button>
156
+ <Button type="submit">Submit</Button>
157
+ </div>
158
+ </div>
159
+ </form>
160
+ </Modal>
161
+ </div>
162
+ );
163
+ },
164
+ };
165
+
166
+ export const ConfirmationDialog: Story = {
167
+ args: { open: false },
168
+ render: () => {
169
+ const [open, setOpen] = useState(false);
170
+
171
+ const handleConfirm = () => {
172
+ alert("Action confirmed!");
173
+ setOpen(false);
174
+ };
175
+
176
+ return (
177
+ <div className="p-8">
178
+ <Button variant="primary" onClick={() => setOpen(true)}>
179
+ Delete Item
180
+ </Button>
181
+ <Modal
182
+ open={open}
183
+ onClose={() => setOpen(false)}
184
+ title="Confirm Deletion"
185
+ closeOnBackdrop={false}
186
+ >
187
+ <Typo variant="body-md">
188
+ Are you sure you want to delete this item? This action cannot be undone.
189
+ </Typo>
190
+ <div className="flex gap-3 justify-end mt-6">
191
+ <button
192
+ onClick={() => setOpen(false)}
193
+ className="px-6 py-2 rounded-full border border-[var(--border)] hover:bg-[var(--muted)] transition-colors"
194
+ >
195
+ Cancel
196
+ </button>
197
+ <Button variant="primary" onClick={handleConfirm}>
198
+ Delete
199
+ </Button>
200
+ </div>
201
+ </Modal>
202
+ </div>
203
+ );
204
+ },
205
+ };
206
+
207
+ /**
208
+ * This story demonstrates the Card component's decorative elements.
209
+ * The Card inside the Modal includes subtle ellipse gradients and border accents
210
+ * that can be toggled on/off via the `showDecorations` prop.
211
+ */
212
+ export const WithCardDecorations: Story = {
213
+ args: { open: false },
214
+ render: () => {
215
+ const [open, setOpen] = useState(false);
216
+ const [showDecorations, setShowDecorations] = useState(true);
217
+
218
+ const ModalDemo = ({ children, ...props }: any) => (
219
+ <>
220
+ <button
221
+ onClick={() => setOpen(true)}
222
+ className="px-6 py-3 rounded-full bg-[var(--primary)] text-white hover:opacity-90 transition-opacity"
223
+ type="button"
224
+ >
225
+ Open Modal with Card Decorations
226
+ </button>
227
+ <label className="flex items-center gap-2 mt-4">
228
+ <input
229
+ type="checkbox"
230
+ checked={showDecorations}
231
+ onChange={(e) => setShowDecorations(e.target.checked)}
232
+ />
233
+ <span>Show Card Decorations</span>
234
+ </label>
235
+ <Modal
236
+ open={open}
237
+ onClose={() => setOpen(false)}
238
+ showDecorations={showDecorations}
239
+ {...props}
240
+ />
241
+ </>
242
+ );
243
+
244
+ return (
245
+ <ModalDemo title="Card with Decorative Elements">
246
+ <div className="space-y-4">
247
+ <Typo variant="body-md">
248
+ The Card component now includes decorative SVG elements from your Figma design:
249
+ </Typo>
250
+ <ul className="list-disc pl-6 space-y-2">
251
+ <li>
252
+ <Typo variant="body-sm">
253
+ <strong>Ellipse gradients</strong> - Subtle blue and white ellipses that add depth
254
+ </Typo>
255
+ </li>
256
+ <li>
257
+ <Typo variant="body-sm">
258
+ <strong>Gradient borders</strong> - Green gradient lines on all four sides
259
+ </Typo>
260
+ </li>
261
+ <li>
262
+ <Typo variant="body-sm">
263
+ <strong>Toggle option</strong> - Use the checkbox above to toggle decorations on/off
264
+ </Typo>
265
+ </li>
266
+ </ul>
267
+ <Typo variant="body-sm" className="text-gray-500">
268
+ Note: The current Modal is using showDecorations={showDecorations.toString()}
269
+ </Typo>
270
+ </div>
271
+ </ModalDemo>
272
+ );
273
+ },
274
+ };
275
+