notionsoft-ui 1.0.1 → 1.0.3

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 (46) hide show
  1. package/.storybook/main.ts +19 -0
  2. package/.storybook/preview.css +160 -0
  3. package/.storybook/preview.ts +21 -0
  4. package/.storybook/vitest.setup.ts +7 -0
  5. package/README.md +7 -1
  6. package/cli/index.cjs +1 -1
  7. package/package.json +23 -2
  8. package/src/stories/Button.stories.ts +54 -0
  9. package/src/stories/Button.tsx +37 -0
  10. package/src/stories/Configure.mdx +364 -0
  11. package/src/stories/Header.stories.ts +34 -0
  12. package/src/stories/Header.tsx +56 -0
  13. package/src/stories/Page.stories.ts +33 -0
  14. package/src/stories/Page.tsx +73 -0
  15. package/src/stories/assets/accessibility.png +0 -0
  16. package/src/stories/assets/accessibility.svg +1 -0
  17. package/src/stories/assets/addon-library.png +0 -0
  18. package/src/stories/assets/assets.png +0 -0
  19. package/src/stories/assets/avif-test-image.avif +0 -0
  20. package/src/stories/assets/context.png +0 -0
  21. package/src/stories/assets/discord.svg +1 -0
  22. package/src/stories/assets/docs.png +0 -0
  23. package/src/stories/assets/figma-plugin.png +0 -0
  24. package/src/stories/assets/github.svg +1 -0
  25. package/src/stories/assets/share.png +0 -0
  26. package/src/stories/assets/styling.png +0 -0
  27. package/src/stories/assets/testing.png +0 -0
  28. package/src/stories/assets/theming.png +0 -0
  29. package/src/stories/assets/tutorials.svg +1 -0
  30. package/src/stories/assets/youtube.svg +1 -0
  31. package/src/stories/button.css +30 -0
  32. package/src/stories/header.css +32 -0
  33. package/src/stories/page.css +68 -0
  34. package/src/templates/button/BooleanStatusButton.stories.tsx +90 -0
  35. package/src/templates/button/BooleanStatusButton.tsx +29 -0
  36. package/src/templates/button/Button.stories.tsx +51 -0
  37. package/src/templates/button/button.tsx +31 -20
  38. package/src/templates/sheet/AnimatedSheet.stories.tsx +103 -0
  39. package/src/templates/sheet/AnimatedSheet.tsx +163 -0
  40. package/src/templates/text/ShinyText.stories.tsx +43 -0
  41. package/src/templates/text/ShinyText.tsx +35 -0
  42. package/tsconfig.json +2 -1
  43. package/vite.config.ts +36 -0
  44. package/vitest.shims.d.ts +1 -0
  45. package/src/templates/card/card.tsx +0 -20
  46. package/src/utils/cn.js +0 -15
@@ -0,0 +1,32 @@
1
+ .storybook-header {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ align-items: center;
5
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
6
+ padding: 15px 20px;
7
+ font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
8
+ }
9
+
10
+ .storybook-header svg {
11
+ display: inline-block;
12
+ vertical-align: top;
13
+ }
14
+
15
+ .storybook-header h1 {
16
+ display: inline-block;
17
+ vertical-align: top;
18
+ margin: 6px 0 6px 10px;
19
+ font-weight: 700;
20
+ font-size: 20px;
21
+ line-height: 1;
22
+ }
23
+
24
+ .storybook-header button + button {
25
+ margin-left: 10px;
26
+ }
27
+
28
+ .storybook-header .welcome {
29
+ margin-right: 10px;
30
+ color: #333;
31
+ font-size: 14px;
32
+ }
@@ -0,0 +1,68 @@
1
+ .storybook-page {
2
+ margin: 0 auto;
3
+ padding: 48px 20px;
4
+ max-width: 600px;
5
+ color: #333;
6
+ font-size: 14px;
7
+ line-height: 24px;
8
+ font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
9
+ }
10
+
11
+ .storybook-page h2 {
12
+ display: inline-block;
13
+ vertical-align: top;
14
+ margin: 0 0 4px;
15
+ font-weight: 700;
16
+ font-size: 32px;
17
+ line-height: 1;
18
+ }
19
+
20
+ .storybook-page p {
21
+ margin: 1em 0;
22
+ }
23
+
24
+ .storybook-page a {
25
+ color: inherit;
26
+ }
27
+
28
+ .storybook-page ul {
29
+ margin: 1em 0;
30
+ padding-left: 30px;
31
+ }
32
+
33
+ .storybook-page li {
34
+ margin-bottom: 8px;
35
+ }
36
+
37
+ .storybook-page .tip {
38
+ display: inline-block;
39
+ vertical-align: top;
40
+ margin-right: 10px;
41
+ border-radius: 1em;
42
+ background: #e7fdd8;
43
+ padding: 4px 12px;
44
+ color: #357a14;
45
+ font-weight: 700;
46
+ font-size: 11px;
47
+ line-height: 12px;
48
+ }
49
+
50
+ .storybook-page .tip-wrapper {
51
+ margin-top: 40px;
52
+ margin-bottom: 40px;
53
+ font-size: 13px;
54
+ line-height: 20px;
55
+ }
56
+
57
+ .storybook-page .tip-wrapper svg {
58
+ display: inline-block;
59
+ vertical-align: top;
60
+ margin-top: 3px;
61
+ margin-right: 4px;
62
+ width: 12px;
63
+ height: 12px;
64
+ }
65
+
66
+ .storybook-page .tip-wrapper svg path {
67
+ fill: #1ea7fd;
68
+ }
@@ -0,0 +1,90 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import BooleanStatusButton from "./BooleanStatusButton";
3
+
4
+ // --------------------------------------------
5
+ // Status options used for Storybook showcase
6
+ // --------------------------------------------
7
+ const statusOptions = {
8
+ success: {
9
+ style: "border-green-500 text-green-600",
10
+ value: "Active",
11
+ },
12
+ error: {
13
+ style: "border-red-500 text-red-600",
14
+ value: "Error",
15
+ },
16
+ warning: {
17
+ style: "border-yellow-500 text-yellow-600",
18
+ value: "Warning",
19
+ },
20
+ neutral: {
21
+ style: "border-gray-400 text-gray-600",
22
+ value: "Unknown",
23
+ },
24
+ };
25
+
26
+ // --------------------------------------------
27
+ // WRAPPER COMPONENT with real `status` prop
28
+ // --------------------------------------------
29
+ interface WrapperProps {
30
+ status: keyof typeof statusOptions;
31
+ className?: string;
32
+ }
33
+
34
+ function Wrapper({ status, className }: WrapperProps) {
35
+ return (
36
+ <BooleanStatusButton
37
+ className={className}
38
+ getColor={() => statusOptions[status]}
39
+ />
40
+ );
41
+ }
42
+
43
+ // --------------------------------------------
44
+ // Storybook meta (now uses Wrapper, not the original)
45
+ // --------------------------------------------
46
+ const meta: Meta<typeof Wrapper> = {
47
+ title: "Button/BooleanStatusButton",
48
+ component: Wrapper,
49
+ argTypes: {
50
+ status: {
51
+ control: "select",
52
+ options: Object.keys(statusOptions),
53
+ },
54
+ className: {
55
+ control: "text",
56
+ },
57
+ },
58
+ };
59
+
60
+ export default meta;
61
+
62
+ type Story = StoryObj<typeof Wrapper>;
63
+
64
+ // --------------------------------------------
65
+ // STORIES
66
+ // --------------------------------------------
67
+
68
+ export const Default: Story = {
69
+ args: {
70
+ status: "success",
71
+ },
72
+ };
73
+
74
+ export const Error: Story = {
75
+ args: {
76
+ status: "error",
77
+ },
78
+ };
79
+
80
+ export const Warning: Story = {
81
+ args: {
82
+ status: "warning",
83
+ },
84
+ };
85
+
86
+ export const Neutral: Story = {
87
+ args: {
88
+ status: "neutral",
89
+ },
90
+ };
@@ -0,0 +1,29 @@
1
+ // import { cn } from "../../utils/cn";
2
+ import { cn } from "@/utils/cn";
3
+
4
+ export interface BooleanStatusButtonProps {
5
+ getColor: () => {
6
+ style: string;
7
+ value?: string;
8
+ };
9
+ className?: string;
10
+ }
11
+
12
+ export default function BooleanStatusButton(props: BooleanStatusButtonProps) {
13
+ const { getColor, className } = props;
14
+ const data = getColor();
15
+
16
+ return (
17
+ <div
18
+ className={cn(
19
+ `border-[1px] mx-auto min-w-fit ltr:text-[13px] sm:ltr:text-sm rtl:text-[13px] sm:rtl:text-sm rtl:font-semibold w-fit flex items-center gap-x-2 ltr:py-1 rtl:py-[2px] px-[8px] rounded-full ${data.style}`,
20
+ className
21
+ )}
22
+ >
23
+ <div
24
+ className={`size-[12px] min-h-[12px] min-w-[12px] rounded-full border-[3px] ${data.style}`}
25
+ />
26
+ <h1 className="text-nowrap">{data.value}</h1>
27
+ </div>
28
+ );
29
+ }
@@ -0,0 +1,51 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import Button from "./button";
3
+
4
+ const meta: Meta<typeof Button> = {
5
+ title: "Button/Button",
6
+ component: Button,
7
+ argTypes: {
8
+ variant: {
9
+ control: "select",
10
+ options: ["default", "primary", "secondary", "warning"],
11
+ },
12
+ disabled: {
13
+ control: "boolean",
14
+ },
15
+ children: {
16
+ control: "text",
17
+ },
18
+ onClick: { action: "clicked" },
19
+ },
20
+ };
21
+
22
+ export default meta;
23
+
24
+ type Story = StoryObj<typeof Button>;
25
+
26
+ export const Primary: Story = {
27
+ args: {
28
+ children: "Primary Button",
29
+ variant: "primary",
30
+ },
31
+ };
32
+
33
+ export const Secondary: Story = {
34
+ args: {
35
+ children: "Secondary Button",
36
+ variant: "secondary",
37
+ },
38
+ };
39
+
40
+ export const Warning: Story = {
41
+ args: {
42
+ children: "Warning Button",
43
+ variant: "warning",
44
+ },
45
+ };
46
+ export const Success: Story = {
47
+ args: {
48
+ children: "Success Button",
49
+ variant: "success",
50
+ },
51
+ };
@@ -1,25 +1,36 @@
1
+ import * as React from "react";
2
+ // import { cn } from "../../utils/cn";
1
3
  import { cn } from "@/utils/cn";
2
- import React from "react";
3
4
 
4
5
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
5
- variant?: "default" | "primary" | "secondary";
6
+ variant?: "primary" | "secondary" | "warning" | "success";
6
7
  }
7
-
8
- const Button: React.FC<ButtonProps> = ({
9
- variant = "default",
10
- className,
11
- ...props
12
- }) => {
13
- return (
14
- <button
15
- className={cn(
16
- "px-4 py-2 rounded-md font-medium transition-colors",
17
- variant === "primary" && "bg-blue-500 text-white",
18
- variant === "secondary" && "bg-gray-200 text-black",
19
- className
20
- )}
21
- {...props}
22
- />
23
- );
24
- };
8
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
9
+ (props, ref: any) => {
10
+ const { className, children, variant, ...rest } = props;
11
+ const style =
12
+ variant == "secondary"
13
+ ? "border hover:bg-primary hover:text-primary-foreground"
14
+ : variant == "warning"
15
+ ? "bg-red-500 text-primary-foreground"
16
+ : variant == "success"
17
+ ? "bg-green-500 text-primary-foreground"
18
+ : "bg-primary hover:bg-primary shadow shadow-primary/50 text-primary-foreground/80 hover:opacity-90 hover:text-primary-foreground";
19
+ return (
20
+ <button
21
+ {...rest}
22
+ ref={ref}
23
+ className={cn(
24
+ `rounded-sm cursor-pointer ltr:text-[13px] sm:ltr:text-sm rtl:text-[13px] sm:rtl:text-sm rtl:font-semibold
25
+ hover:shadow transition w-fit
26
+ sm:px-4 py-[6px] leading-normal duration-200 ease-linear`,
27
+ style,
28
+ className
29
+ )}
30
+ >
31
+ {children}
32
+ </button>
33
+ );
34
+ }
35
+ );
25
36
  export default Button;
@@ -0,0 +1,103 @@
1
+ import Button from "../button/button";
2
+ import AnimatedSheet from "./AnimatedSheet";
3
+ import type { Meta, StoryObj } from "@storybook/react";
4
+ import { useState } from "react";
5
+
6
+ const meta: Meta<typeof AnimatedSheet> = {
7
+ title: "Sheet/AnimatedSheet",
8
+ component: AnimatedSheet,
9
+ parameters: {
10
+ layout: "fullscreen",
11
+ },
12
+ };
13
+
14
+ export default meta;
15
+
16
+ type Story = StoryObj<typeof AnimatedSheet>;
17
+
18
+ // ----------------------------------------------------
19
+ // WRAPPER (controls open/close since AnimatedSheet
20
+ // returns `button` when closed)
21
+ // ----------------------------------------------------
22
+ function SheetWrapper(args: any) {
23
+ const [open, setOpen] = useState(false);
24
+
25
+ return (
26
+ <AnimatedSheet
27
+ {...args}
28
+ open={open}
29
+ onClose={() => setOpen(false)}
30
+ button={
31
+ <Button variant="primary" onClick={() => setOpen(true)}>
32
+ Open Sheet
33
+ </Button>
34
+ }
35
+ >
36
+ <div className="grid gap-x-4 gap-y-6 p-5">
37
+ <div className="mb-12 pb-4">
38
+ <h1 className="rtl:text-3xl-rtl ltr:text-2xl-ltr text-fourth">
39
+ Account password
40
+ </h1>
41
+ </div>
42
+ </div>
43
+ </AnimatedSheet>
44
+ );
45
+ }
46
+
47
+ // ----------------------------------------------------
48
+ // STORIES
49
+ // ----------------------------------------------------
50
+
51
+ // 🔵 DEFAULT — also acts as alias for Right (fixes HMR)
52
+ export const Default: Story = {
53
+ render: (args) => <SheetWrapper {...args} />,
54
+ args: {
55
+ position: "right",
56
+ animate: "right",
57
+ },
58
+ };
59
+
60
+ // ▶️ Right
61
+ export const Right: Story = {
62
+ render: (args) => <SheetWrapper {...args} />,
63
+ args: {
64
+ position: "right",
65
+ animate: "right",
66
+ },
67
+ };
68
+
69
+ // ◀️ Left
70
+ export const Left: Story = {
71
+ render: (args) => <SheetWrapper {...args} />,
72
+ args: {
73
+ position: "left",
74
+ animate: "left",
75
+ },
76
+ };
77
+
78
+ // 🔼 Top
79
+ export const Top: Story = {
80
+ render: (args) => <SheetWrapper {...args} />,
81
+ args: {
82
+ position: "top",
83
+ animate: "top",
84
+ },
85
+ };
86
+
87
+ // 🔽 Bottom
88
+ export const Bottom: Story = {
89
+ render: (args) => <SheetWrapper {...args} />,
90
+ args: {
91
+ position: "bottom",
92
+ animate: "bottom",
93
+ },
94
+ };
95
+
96
+ // ⭕ Center
97
+ export const Center: Story = {
98
+ render: (args) => <SheetWrapper {...args} />,
99
+ args: {
100
+ position: "center",
101
+ animate: "bottom", // slide-up looks best for modal center
102
+ },
103
+ };
@@ -0,0 +1,163 @@
1
+ // import { cn } from "../../utils/cn";
2
+ import { cn } from "@/utils/cn";
3
+
4
+ import { useTransition, animated } from "@react-spring/web";
5
+ import { X } from "lucide-react";
6
+ import { useEffect, useMemo, useState, type ReactNode } from "react";
7
+ import ReactDOM from "react-dom";
8
+
9
+ type PositionType = "center" | "left" | "top" | "bottom" | "right";
10
+ type AnimateType = "left" | "top" | "right" | "bottom";
11
+ type AnimatedSheetProps = {
12
+ open?: boolean;
13
+ button?: ReactNode;
14
+ children?: ReactNode;
15
+ onClose?: (open: boolean) => void;
16
+ style?: {
17
+ rootContainerClassName?: string;
18
+ mainContainerClassName?: string;
19
+ iconClassName?: string;
20
+ };
21
+ position: PositionType;
22
+ animate: AnimateType;
23
+ };
24
+
25
+ export default function AnimatedSheet({
26
+ open,
27
+ onClose,
28
+ children,
29
+ button,
30
+ style,
31
+ position,
32
+ animate,
33
+ }: AnimatedSheetProps) {
34
+ const { rootContainerClassName, mainContainerClassName, iconClassName } =
35
+ style || {};
36
+ const [status, setStatus] = useState<boolean | undefined>(false);
37
+
38
+ useEffect(() => {
39
+ setStatus(open);
40
+ }, [open]);
41
+ const itemsPosition = useMemo(() => {
42
+ const animation =
43
+ animate == "left"
44
+ ? "translateX(-100%)"
45
+ : animate == "right"
46
+ ? "translateX(100%)"
47
+ : animate == "top"
48
+ ? "translateY(-100%)"
49
+ : "translateY(100%)";
50
+
51
+ return position == "left"
52
+ ? {
53
+ rootContainer: "rtl:left-0 ltr:right-0 top-0",
54
+ icon: "",
55
+ transitionFrom: animation,
56
+ transitionEnter: "translateX(0%)",
57
+ }
58
+ : position == "right"
59
+ ? {
60
+ rootContainer: "rtl:right-0 ltr:left-0 top-0",
61
+ icon: "",
62
+ transitionFrom: animation,
63
+ transitionEnter: "translateX(0%)",
64
+ }
65
+ : position == "center"
66
+ ? {
67
+ rootContainer:
68
+ "rtl:left-1/2 ltr:right-1/2 top-0 rtl:-translate-x-1/2 ltr:translate-x-1/2",
69
+ icon: "rtl:order-1",
70
+ transitionFrom: animation,
71
+ transitionEnter: "translateX(0%)",
72
+ }
73
+ : position == "top"
74
+ ? {
75
+ rootContainer:
76
+ "rtl:left-1/2 ltr:right-1/2 top-0 rtl:-translate-x-1/2 ltr:translate-x-1/2",
77
+ icon: "order-1",
78
+ transitionFrom: animation,
79
+ transitionEnter: "translateY(0%)",
80
+ }
81
+ : {
82
+ rootContainer:
83
+ "rtl:left-1/2 ltr:right-1/2 top-0 rtl:-translate-x-1/2 ltr:translate-x-1/2",
84
+ icon: "order-1",
85
+ transitionFrom: animation,
86
+ transitionEnter: "translateY(0%)",
87
+ };
88
+ }, [position]);
89
+
90
+ const transitions = useTransition(status, {
91
+ from: {
92
+ opacity: 0,
93
+ transform: itemsPosition.transitionFrom,
94
+ },
95
+ enter: { opacity: 1, transform: itemsPosition.transitionEnter },
96
+ config: { tension: 250, friction: 30 },
97
+ });
98
+
99
+ const overlayTransitions = useTransition(status, {
100
+ from: { opacity: 0 },
101
+ enter: { opacity: 0.5 },
102
+ config: { tension: 250, friction: 30 },
103
+ });
104
+
105
+ const onCloseSheet = () => {
106
+ if (onClose) onClose(!status);
107
+ setStatus((prev) => !prev);
108
+ };
109
+
110
+ if (!status) return button;
111
+
112
+ return ReactDOM.createPortal(
113
+ <>
114
+ {overlayTransitions(
115
+ (style, item) =>
116
+ item && (
117
+ <animated.div
118
+ style={style}
119
+ onClick={onCloseSheet}
120
+ className="fixed bg-card inset-0 z-50"
121
+ aria-hidden="true"
122
+ />
123
+ )
124
+ )}
125
+
126
+ {transitions(
127
+ (style, item) =>
128
+ item && (
129
+ <div
130
+ onClick={onCloseSheet}
131
+ className={cn(
132
+ "fixed flex w-screen xxl:min-w-[400px] xxl:w-fit z-50 h-screen justify-end",
133
+ itemsPosition.rootContainer,
134
+ rootContainerClassName
135
+ )}
136
+ >
137
+ <X
138
+ onClick={onCloseSheet}
139
+ className={cn(
140
+ "fixed xxl:static z-[51] text-primary-foreground bg-primary cursor-pointer rounded-full h-6 w-7 p-1 sm:m-4 m-2 hover:bg-primary/90 xxl:hover:bg-primary/90 transition-colors",
141
+ itemsPosition.icon,
142
+ iconClassName
143
+ )}
144
+ />
145
+ <animated.div
146
+ style={style}
147
+ className={cn(
148
+ "h-full w-full z-50 backdrop-blur-lg bg-white/10 border border-white/20 shadow-lg",
149
+ mainContainerClassName
150
+ )}
151
+ role="dialog"
152
+ aria-modal="true"
153
+ onClick={(e) => e.stopPropagation()}
154
+ >
155
+ {children}
156
+ </animated.div>
157
+ </div>
158
+ )
159
+ )}
160
+ </>,
161
+ document.body
162
+ );
163
+ }
@@ -0,0 +1,43 @@
1
+ import { ShiningText } from "./ShinyText";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+
4
+ const meta: Meta<typeof ShiningText> = {
5
+ title: "Components/ShiningText",
6
+ component: ShiningText,
7
+ args: {
8
+ text: "Loading...",
9
+ },
10
+ };
11
+
12
+ export default meta;
13
+
14
+ type Story = StoryObj<typeof ShiningText>;
15
+
16
+ export const Default: Story = {
17
+ args: {
18
+ text: "Loading...",
19
+ className: "text-4xl font-bold",
20
+ },
21
+ };
22
+
23
+ export const Large: Story = {
24
+ args: {
25
+ text: "Loading...",
26
+ className: "text-6xl from-black via-gray-100 to-black font-extrabold",
27
+ },
28
+ };
29
+
30
+ export const CustomGradient: Story = {
31
+ args: {
32
+ text: "Custom Gradient",
33
+ className:
34
+ "text-5xl font-semibold from-black via-white to-blue-400 font-extrabold",
35
+ },
36
+ };
37
+
38
+ export const Small: Story = {
39
+ args: {
40
+ text: "Loading...",
41
+ className: "text-md font-medium from-black via-gray-100 to-black",
42
+ },
43
+ };
@@ -0,0 +1,35 @@
1
+ import * as React from "react";
2
+ import { useSpring, animated } from "@react-spring/web";
3
+ // import { cn } from "../../utils/cn";
4
+ import { cn } from "@/utils/cn";
5
+
6
+ interface ShiningTextProps extends React.HTMLAttributes<HTMLSpanElement> {
7
+ text: string;
8
+ }
9
+
10
+ export function ShiningText({ text, className, ...props }: ShiningTextProps) {
11
+ // Animate strictly left → right
12
+ const styles = useSpring({
13
+ from: { backgroundPosition: "-100% 0%" }, // start offscreen left
14
+ to: { backgroundPosition: "100% 0%" }, // end offscreen right
15
+ loop: true,
16
+ config: { duration: 2000 },
17
+ });
18
+
19
+ return (
20
+ <animated.span
21
+ style={{
22
+ backgroundSize: "200% 50%",
23
+ ...styles,
24
+ }}
25
+ className={cn(
26
+ "bg-gradient-to-r from-gray-300 via-white to-gray-300", // left→right gradient
27
+ "bg-clip-text text-transparent",
28
+ className
29
+ )}
30
+ {...props}
31
+ >
32
+ {text}
33
+ </animated.span>
34
+ );
35
+ }
package/tsconfig.json CHANGED
@@ -6,7 +6,8 @@
6
6
  "baseUrl": "src",
7
7
  "ignoreDeprecations": "6.0",
8
8
  "paths": {
9
- "@/utils/*": ["utils/*"]
9
+ "@/utils/*": ["utils/*"],
10
+ "@/*": ["*"]
10
11
  }
11
12
  }
12
13
  }