notionsoft-ui 1.0.6 → 1.0.8
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/cli/index.cjs +3 -3
- package/package.json +1 -1
- package/src/notion-ui/animated-item/animated-item.tsx +54 -0
- package/src/notion-ui/animated-item/index.ts +3 -0
- package/src/{templates → notion-ui}/boolean-status-button/BooleanStatusButton.stories.tsx +1 -1
- package/src/{templates → notion-ui}/boolean-status-button/BooleanStatusButton.tsx +1 -1
- package/src/notion-ui/boolean-status-button/index.ts +3 -0
- package/src/notion-ui/button/index.ts +3 -0
- package/src/{templates → notion-ui}/button-spinner/ButtonSpinner.stories.tsx +1 -1
- package/src/{templates → notion-ui}/button-spinner/button-spinner.tsx +1 -1
- package/src/notion-ui/button-spinner/index.ts +3 -0
- package/src/{templates → notion-ui}/circle-loader/CircleLoader.stories.tsx +1 -1
- package/src/{templates → notion-ui}/circle-loader/circle-loader.tsx +1 -1
- package/src/notion-ui/circle-loader/index.ts +3 -0
- package/src/notion-ui/input/Input.stories.tsx +100 -0
- package/src/notion-ui/input/index.ts +3 -0
- package/src/notion-ui/input/input.tsx +193 -0
- package/src/{templates → notion-ui}/sheet/AnimatedSheet.stories.tsx +1 -1
- package/src/{templates → notion-ui}/sheet/AnimatedSheet.tsx +1 -1
- package/src/notion-ui/sheet/index.ts +3 -0
- package/src/notion-ui/shining-text/index.ts +3 -0
- package/tsconfig.json +1 -0
- /package/src/{templates → notion-ui}/button/Button.stories.tsx +0 -0
- /package/src/{templates → notion-ui}/button/button.tsx +0 -0
- /package/src/{templates → notion-ui}/shining-text/shining-text.stories.tsx +0 -0
- /package/src/{templates → notion-ui}/shining-text/shining-text.tsx +0 -0
package/cli/index.cjs
CHANGED
|
@@ -10,10 +10,10 @@ const { execSync } = require("child_process");
|
|
|
10
10
|
Helper: get template path
|
|
11
11
|
------------------------------- */
|
|
12
12
|
function getTemplateFile(component) {
|
|
13
|
-
// Library template: src/
|
|
13
|
+
// Library template: src/notion-ui/button/button.tsx
|
|
14
14
|
return path.join(
|
|
15
15
|
__dirname,
|
|
16
|
-
"../src/
|
|
16
|
+
"../src/notion-ui",
|
|
17
17
|
component,
|
|
18
18
|
component + ".tsx"
|
|
19
19
|
);
|
|
@@ -153,7 +153,7 @@ program
|
|
|
153
153
|
.command("list")
|
|
154
154
|
.description("List available components")
|
|
155
155
|
.action(() => {
|
|
156
|
-
const templatesDir = path.join(__dirname, "../src/
|
|
156
|
+
const templatesDir = path.join(__dirname, "../src/notion-ui");
|
|
157
157
|
const components = fs.readdirSync(templatesDir).filter((folder) => {
|
|
158
158
|
const file = path.join(templatesDir, folder, folder + ".tsx");
|
|
159
159
|
return fs.existsSync(file);
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useInView,
|
|
3
|
+
animated,
|
|
4
|
+
type IntersectionArgs,
|
|
5
|
+
SpringValue,
|
|
6
|
+
type AnimationResult,
|
|
7
|
+
Controller,
|
|
8
|
+
} from "@react-spring/web";
|
|
9
|
+
import { useCallback, useState } from "react";
|
|
10
|
+
|
|
11
|
+
export interface AnimatedItemProps {
|
|
12
|
+
springProps: {
|
|
13
|
+
from?: object;
|
|
14
|
+
to?: object | object[];
|
|
15
|
+
delay?: number;
|
|
16
|
+
immediate?: boolean;
|
|
17
|
+
reset?: boolean;
|
|
18
|
+
reverse?: boolean;
|
|
19
|
+
pause?: boolean;
|
|
20
|
+
onStart?: (
|
|
21
|
+
result: AnimationResult,
|
|
22
|
+
spring: Controller | SpringValue,
|
|
23
|
+
item?: any
|
|
24
|
+
) => void;
|
|
25
|
+
config?: {
|
|
26
|
+
mass: number;
|
|
27
|
+
friction: number;
|
|
28
|
+
tension: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
intersectionArgs: IntersectionArgs;
|
|
32
|
+
children: React.ReactNode | ((inView: boolean) => React.ReactNode);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function AnimatedItem(props: AnimatedItemProps) {
|
|
36
|
+
const [inView, setInView] = useState(false);
|
|
37
|
+
const { springProps, intersectionArgs, children } = props;
|
|
38
|
+
const defaultOnStart = useCallback(
|
|
39
|
+
() => {
|
|
40
|
+
setInView(true);
|
|
41
|
+
},
|
|
42
|
+
[] // no dependencies, memoized once
|
|
43
|
+
);
|
|
44
|
+
const composedOnStart = springProps.onStart ?? defaultOnStart;
|
|
45
|
+
|
|
46
|
+
const springConfig = { ...springProps, onStart: composedOnStart };
|
|
47
|
+
const [ref, springs] = useInView(() => springConfig, intersectionArgs);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<animated.div ref={ref} style={springs}>
|
|
51
|
+
{typeof children === "function" ? children(inView) : children}
|
|
52
|
+
</animated.div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { BooleanStatusButton } from "./BooleanStatusButton";
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import BooleanStatusButton from "./BooleanStatusButton";
|
|
3
3
|
|
|
4
4
|
// --------------------------------------------
|
|
5
5
|
// Status options used for Storybook showcase
|
|
@@ -9,7 +9,7 @@ export interface BooleanStatusButtonProps {
|
|
|
9
9
|
className?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export function BooleanStatusButton(props: BooleanStatusButtonProps) {
|
|
13
13
|
const { getColor, className } = props;
|
|
14
14
|
const data = getColor();
|
|
15
15
|
|
|
@@ -7,7 +7,7 @@ export interface IButtonSpinnerProps {
|
|
|
7
7
|
className?: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export
|
|
10
|
+
export function ButtonSpinner(props: IButtonSpinnerProps) {
|
|
11
11
|
const { loading, children, className } = props;
|
|
12
12
|
return (
|
|
13
13
|
<>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import CircleLoader from "../circle-loader/circle-loader"; // Adjust the import path as needed
|
|
1
|
+
import { CircleLoader } from "../circle-loader/circle-loader"; // Adjust the import path as needed
|
|
2
2
|
import { CircleLoaderProps } from "./circle-loader";
|
|
3
3
|
|
|
4
4
|
// Meta information for Storybook
|
|
@@ -8,7 +8,7 @@ export interface CircleLoaderProps {
|
|
|
8
8
|
parentClassName?: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export
|
|
11
|
+
export function CircleLoader(props: CircleLoaderProps) {
|
|
12
12
|
const { label, className, labelclassname, parentClassName, ...restProps } =
|
|
13
13
|
props;
|
|
14
14
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Input } from "./input";
|
|
3
|
+
import { Search, AlertTriangle } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Input> = {
|
|
6
|
+
title: "Form/Input",
|
|
7
|
+
component: Input,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "centered",
|
|
10
|
+
},
|
|
11
|
+
argTypes: {
|
|
12
|
+
label: { control: "text" },
|
|
13
|
+
requiredHint: { control: "text" },
|
|
14
|
+
errorMessage: { control: "text" },
|
|
15
|
+
readOnly: { control: "boolean" },
|
|
16
|
+
measurement: {
|
|
17
|
+
control: "select",
|
|
18
|
+
options: ["sm", "md", "lg", undefined],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj<typeof Input>;
|
|
25
|
+
|
|
26
|
+
const Template = (args) => (
|
|
27
|
+
<div className="w-[350px]">
|
|
28
|
+
<Input {...args} />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const Default: Story = {
|
|
33
|
+
render: Template,
|
|
34
|
+
args: {
|
|
35
|
+
label: "Username",
|
|
36
|
+
placeholder: "Enter your username",
|
|
37
|
+
measurement: "md",
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const WithStartIcon: Story = {
|
|
42
|
+
render: Template,
|
|
43
|
+
args: {
|
|
44
|
+
label: "Search",
|
|
45
|
+
placeholder: "Search …",
|
|
46
|
+
startContent: <Search size={18} />,
|
|
47
|
+
measurement: "md",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const WithEndIcon: Story = {
|
|
52
|
+
render: Template,
|
|
53
|
+
args: {
|
|
54
|
+
label: "Email",
|
|
55
|
+
placeholder: "you@example.com",
|
|
56
|
+
endContent: <AlertTriangle size={20} color="orange" />,
|
|
57
|
+
measurement: "md",
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const RequiredField: Story = {
|
|
62
|
+
render: Template,
|
|
63
|
+
args: {
|
|
64
|
+
label: "Password",
|
|
65
|
+
placeholder: "Enter password",
|
|
66
|
+
required: true,
|
|
67
|
+
requiredHint: "* Required",
|
|
68
|
+
measurement: "md",
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const WithErrorMessage: Story = {
|
|
73
|
+
render: Template,
|
|
74
|
+
args: {
|
|
75
|
+
label: "Email",
|
|
76
|
+
placeholder: "example@mail.com",
|
|
77
|
+
errorMessage: "Invalid email format",
|
|
78
|
+
measurement: "md",
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const ReadOnly: Story = {
|
|
83
|
+
render: Template,
|
|
84
|
+
args: {
|
|
85
|
+
label: "Read Only Field",
|
|
86
|
+
value: "Can't change this",
|
|
87
|
+
readOnly: true,
|
|
88
|
+
measurement: "md",
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const SizesShowcase: Story = {
|
|
93
|
+
render: () => (
|
|
94
|
+
<div className="space-y-5 w-[350px]">
|
|
95
|
+
<Input label="Small" placeholder="Small size" measurement="sm" />
|
|
96
|
+
<Input label="Medium" placeholder="Medium size" measurement="md" />
|
|
97
|
+
<Input label="Large" placeholder="Large size" measurement="lg" />
|
|
98
|
+
</div>
|
|
99
|
+
),
|
|
100
|
+
};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
// import { cn } from "@/utils/cn";
|
|
3
|
+
import { cn } from "../../utils/cn";
|
|
4
|
+
import AnimatedItem from "@/components/animated-item";
|
|
5
|
+
// import AnimatedItem from "../animated-item";
|
|
6
|
+
|
|
7
|
+
export type NastranInputSize = "sm" | "md" | "lg";
|
|
8
|
+
|
|
9
|
+
export interface InputProps
|
|
10
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
11
|
+
startContent?: React.ReactNode;
|
|
12
|
+
requiredHint?: string;
|
|
13
|
+
label?: string;
|
|
14
|
+
endContent?: React.ReactNode;
|
|
15
|
+
errorMessage?: string;
|
|
16
|
+
parentClassName?: string;
|
|
17
|
+
measurement: NastranInputSize;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
21
|
+
(
|
|
22
|
+
{
|
|
23
|
+
className,
|
|
24
|
+
type,
|
|
25
|
+
requiredHint,
|
|
26
|
+
startContent,
|
|
27
|
+
endContent,
|
|
28
|
+
parentClassName = "",
|
|
29
|
+
measurement = "md",
|
|
30
|
+
errorMessage,
|
|
31
|
+
label,
|
|
32
|
+
readOnly,
|
|
33
|
+
...rest
|
|
34
|
+
},
|
|
35
|
+
ref
|
|
36
|
+
) => {
|
|
37
|
+
const hasError = !!errorMessage;
|
|
38
|
+
|
|
39
|
+
const inputPaddingClass = startContent
|
|
40
|
+
? "rtl:pr-[42px] ltr:ps-[42px]"
|
|
41
|
+
: "rtl:pr-[12px] ltr:ps-[12px]";
|
|
42
|
+
|
|
43
|
+
const heightStyle = useMemo(
|
|
44
|
+
() =>
|
|
45
|
+
measurement == "lg"
|
|
46
|
+
? {
|
|
47
|
+
height: "50px",
|
|
48
|
+
paddingBottom: "pb-[3px]",
|
|
49
|
+
endContent: label
|
|
50
|
+
? "top-12 -translate-y-1/2"
|
|
51
|
+
: "top-[26px] -translate-y-1/2",
|
|
52
|
+
startContent: label
|
|
53
|
+
? "top-12 -translate-y-1/2"
|
|
54
|
+
: "top-[26px] -translate-y-1/2",
|
|
55
|
+
required: "top-[2px]",
|
|
56
|
+
}
|
|
57
|
+
: measurement == "md"
|
|
58
|
+
? {
|
|
59
|
+
height: "44px",
|
|
60
|
+
paddingBottom: "pb-[2px]",
|
|
61
|
+
endContent: label
|
|
62
|
+
? "top-[45px] -translate-y-1/2"
|
|
63
|
+
: "top-[22px] -translate-y-1/2",
|
|
64
|
+
startContent: label
|
|
65
|
+
? "top-[45px] -translate-y-1/2"
|
|
66
|
+
: "top-[22px] -translate-y-1/2",
|
|
67
|
+
required: "top-[4px]",
|
|
68
|
+
}
|
|
69
|
+
: {
|
|
70
|
+
height: "40px",
|
|
71
|
+
paddingBottom: "pb-[2px]",
|
|
72
|
+
endContent: label
|
|
73
|
+
? "top-[44px] -translate-y-1/2"
|
|
74
|
+
: "top-[20px] -translate-y-1/2",
|
|
75
|
+
startContent: label
|
|
76
|
+
? "top-[44px] -translate-y-1/2"
|
|
77
|
+
: "top-[20px] -translate-y-1/2",
|
|
78
|
+
required: "top-[4px]",
|
|
79
|
+
},
|
|
80
|
+
[]
|
|
81
|
+
);
|
|
82
|
+
return (
|
|
83
|
+
<div className={cn(parentClassName, "flex flex-col justify-end")}>
|
|
84
|
+
<div
|
|
85
|
+
className={cn(
|
|
86
|
+
"relative select-none h-fit rtl:text-lg-rtl ltr:text-lg-ltr"
|
|
87
|
+
)}
|
|
88
|
+
>
|
|
89
|
+
{/* Start Content */}
|
|
90
|
+
{startContent && (
|
|
91
|
+
<span
|
|
92
|
+
className={cn(
|
|
93
|
+
"absolute flex items-center ltr:left-[12px] rtl:right-[12px]",
|
|
94
|
+
heightStyle.startContent
|
|
95
|
+
)}
|
|
96
|
+
>
|
|
97
|
+
{startContent}
|
|
98
|
+
</span>
|
|
99
|
+
)}
|
|
100
|
+
|
|
101
|
+
{/* End Content */}
|
|
102
|
+
{endContent && (
|
|
103
|
+
<span
|
|
104
|
+
className={cn(
|
|
105
|
+
"absolute flex items-center ltr:right-[5px] rtl:left-[5px]",
|
|
106
|
+
heightStyle.endContent
|
|
107
|
+
)}
|
|
108
|
+
>
|
|
109
|
+
{endContent}
|
|
110
|
+
</span>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
{/* Required Hint */}
|
|
114
|
+
{requiredHint && (
|
|
115
|
+
<span
|
|
116
|
+
className={cn(
|
|
117
|
+
"absolute font-semibold text-red-600 rtl:text-[13px] ltr:text-[11px] ltr:right-[10px] rtl:left-[10px]",
|
|
118
|
+
heightStyle.required
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
{requiredHint}
|
|
122
|
+
</span>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
{/* Label */}
|
|
126
|
+
{label && (
|
|
127
|
+
<label
|
|
128
|
+
htmlFor={label}
|
|
129
|
+
className={cn(
|
|
130
|
+
"font-semibold rtl:text-xl-rtl ltr:text-lg-ltr inline-block pb-1"
|
|
131
|
+
)}
|
|
132
|
+
>
|
|
133
|
+
{label}
|
|
134
|
+
</label>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{/* Input Field */}
|
|
138
|
+
|
|
139
|
+
<input
|
|
140
|
+
ref={ref}
|
|
141
|
+
type={type}
|
|
142
|
+
data-slot="input"
|
|
143
|
+
readOnly={readOnly}
|
|
144
|
+
style={{
|
|
145
|
+
height: heightStyle.height,
|
|
146
|
+
}}
|
|
147
|
+
className={cn(
|
|
148
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent px-3 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
149
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
150
|
+
"appearance-none placeholder:text-primary/60 ltr:text-sm rtl:text-sm rtl:font-semibold focus-visible:ring-0 rounded focus-visible:shadow-sm focus-visible:ring-offset-0 transition-[border] bg-card dark:bg-black/30",
|
|
151
|
+
"focus-visible:border-fourth/60",
|
|
152
|
+
"[&::-webkit-outer-spin-button]:appearance-none",
|
|
153
|
+
"[&::-webkit-inner-spin-button]:appearance-none",
|
|
154
|
+
"[-moz-appearance:textfield]",
|
|
155
|
+
inputPaddingClass,
|
|
156
|
+
hasError ? "border-red-400 border" : "border-primary/25",
|
|
157
|
+
readOnly && "cursor-not-allowed",
|
|
158
|
+
heightStyle.paddingBottom,
|
|
159
|
+
className
|
|
160
|
+
)}
|
|
161
|
+
{...rest}
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
{/* Error Message */}
|
|
166
|
+
{hasError && (
|
|
167
|
+
<AnimatedItem
|
|
168
|
+
springProps={{
|
|
169
|
+
from: {
|
|
170
|
+
opacity: 0,
|
|
171
|
+
transform: "translateY(-8px)",
|
|
172
|
+
},
|
|
173
|
+
config: {
|
|
174
|
+
mass: 1,
|
|
175
|
+
tension: 210,
|
|
176
|
+
friction: 20,
|
|
177
|
+
},
|
|
178
|
+
to: {
|
|
179
|
+
opacity: 1,
|
|
180
|
+
transform: "translateY(0px)",
|
|
181
|
+
},
|
|
182
|
+
}}
|
|
183
|
+
intersectionArgs={{ once: true, rootMargin: "-5% 0%" }}
|
|
184
|
+
>
|
|
185
|
+
<h1 className="text-red-400 text-start capitalize rtl:text-sm rtl:font-medium ltr:text-sm-ltr">
|
|
186
|
+
{errorMessage}
|
|
187
|
+
</h1>
|
|
188
|
+
</AnimatedItem>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
);
|
package/tsconfig.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|