notionsoft-ui 1.0.20 → 1.0.21
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/package.json +2 -1
- package/src/notion-ui/button/button.tsx +1 -1
- package/src/notion-ui/date-picker/DatePicker.stories.tsx +99 -0
- package/src/notion-ui/date-picker/date-picker.tsx +273 -0
- package/src/notion-ui/date-picker/index.ts +3 -0
- package/src/notion-ui/input/input.tsx +19 -12
- package/src/notion-ui/multi-date-picker/MultiDatePicker.stories.tsx +94 -0
- package/src/notion-ui/multi-date-picker/index.ts +3 -0
- package/src/notion-ui/multi-date-picker/multi-date-picker.tsx +265 -0
- package/src/notion-ui/multi-select-input/multi-select-input.tsx +202 -54
- package/src/notion-ui/password-input/password-input.tsx +39 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notionsoft-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"description": "A React UI component installer (shadcn-style). Installs components directly into your project.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"notionsoft-ui": "./cli/index.cjs"
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"lucide-react": "^0.554.0",
|
|
27
27
|
"react": "^19.2.0",
|
|
28
28
|
"react-dom": "^19.2.0",
|
|
29
|
+
"react-multi-date-picker": "^4.5.2",
|
|
29
30
|
"tailwind-merge": "^3.4.0"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
@@ -24,7 +24,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
24
24
|
disabled={disabled}
|
|
25
25
|
ref={ref}
|
|
26
26
|
className={cn(
|
|
27
|
-
`rounded-sm flex items-center gap-x-1 cursor-pointer font-medium ltr:text-xs leading-snug li rtl:text-[13px] sm:rtl:text-sm rtl:font-semibold
|
|
27
|
+
`rounded-sm flex items-center justify-center gap-x-1 cursor-pointer font-medium ltr:text-xs leading-snug li rtl:text-[13px] sm:rtl:text-sm rtl:font-semibold
|
|
28
28
|
transition w-fit px-3 py-1.5 duration-200 ease-linear`,
|
|
29
29
|
style,
|
|
30
30
|
disabled &&
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import DateObject from "react-date-object";
|
|
4
|
+
|
|
5
|
+
import DatePicker, { DatePickerProps } from "./date-picker";
|
|
6
|
+
|
|
7
|
+
// Persian
|
|
8
|
+
import persian from "react-date-object/calendars/persian";
|
|
9
|
+
import persian_fa from "react-date-object/locales/persian_fa";
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
title: "Date/DatePicker",
|
|
13
|
+
component: DatePicker,
|
|
14
|
+
parameters: {
|
|
15
|
+
layout: "centered",
|
|
16
|
+
},
|
|
17
|
+
} as Meta<DatePickerProps>;
|
|
18
|
+
|
|
19
|
+
const Wrapper = (args: DatePickerProps) => {
|
|
20
|
+
const [value, setValue] = React.useState<DateObject | undefined>(
|
|
21
|
+
typeof args.value === "string"
|
|
22
|
+
? new DateObject(new Date(args.value))
|
|
23
|
+
: args.value
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div style={{ width: 320 }}>
|
|
28
|
+
<DatePicker
|
|
29
|
+
{...args}
|
|
30
|
+
value={value}
|
|
31
|
+
dateOnComplete={(date) => {
|
|
32
|
+
setValue(date);
|
|
33
|
+
return false;
|
|
34
|
+
}}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/* ------------------------ DEFAULT ------------------------ */
|
|
41
|
+
export const Default: StoryObj<DatePickerProps> = {
|
|
42
|
+
render: (args) => <Wrapper {...args} />,
|
|
43
|
+
args: {
|
|
44
|
+
placeholder: "Select date...",
|
|
45
|
+
required: false,
|
|
46
|
+
label: "Date",
|
|
47
|
+
measurement: "md",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/* ------------------------ PERSIAN ------------------------ */
|
|
52
|
+
export const Persian: StoryObj<DatePickerProps> = {
|
|
53
|
+
render: (args) => <Wrapper {...args} />,
|
|
54
|
+
args: {
|
|
55
|
+
placeholder: "تاریخ را انتخاب کنید",
|
|
56
|
+
label: "تاریخ",
|
|
57
|
+
measurement: "md",
|
|
58
|
+
getLocalizations: () => ({
|
|
59
|
+
calendar: persian,
|
|
60
|
+
locale: persian_fa,
|
|
61
|
+
months: [
|
|
62
|
+
"فروردین",
|
|
63
|
+
"اردیبهشت",
|
|
64
|
+
"خرداد",
|
|
65
|
+
"تیر",
|
|
66
|
+
"مرداد",
|
|
67
|
+
"شهریور",
|
|
68
|
+
"مهر",
|
|
69
|
+
"آبان",
|
|
70
|
+
"آذر",
|
|
71
|
+
"دی",
|
|
72
|
+
"بهمن",
|
|
73
|
+
"اسفند",
|
|
74
|
+
],
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/* ------------------------ WITH ERROR ------------------------ */
|
|
80
|
+
export const WithError: StoryObj<DatePickerProps> = {
|
|
81
|
+
render: (args) => <Wrapper {...args} />,
|
|
82
|
+
args: {
|
|
83
|
+
placeholder: "Select date...",
|
|
84
|
+
label: "Birthday",
|
|
85
|
+
errorMessage: "This field is required",
|
|
86
|
+
required: true,
|
|
87
|
+
requiredHint: "*",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/* ------------------------ READONLY ------------------------ */
|
|
92
|
+
export const ReadOnly: StoryObj<DatePickerProps> = {
|
|
93
|
+
render: (args) => <Wrapper {...args} />,
|
|
94
|
+
args: {
|
|
95
|
+
placeholder: "Read-only field",
|
|
96
|
+
label: "Date",
|
|
97
|
+
readOnly: true,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import DateObject from "react-date-object";
|
|
3
|
+
import type { Calendar, Locale } from "react-date-object";
|
|
4
|
+
import { Calendar as Calendars } from "react-multi-date-picker";
|
|
5
|
+
export type DatePickerSize = "sm" | "md" | "lg";
|
|
6
|
+
import gregorian from "react-date-object/calendars/gregorian";
|
|
7
|
+
import gregorian_en from "react-date-object/locales/gregorian_en";
|
|
8
|
+
import { CalendarDays } from "lucide-react";
|
|
9
|
+
import { createPortal } from "react-dom";
|
|
10
|
+
|
|
11
|
+
import AnimatedItem from "../animated-item";
|
|
12
|
+
import { cn } from "../../utils/cn";
|
|
13
|
+
|
|
14
|
+
export interface DatePickerProps {
|
|
15
|
+
dateOnComplete: (date: DateObject) => boolean | void;
|
|
16
|
+
value: DateObject | undefined | string;
|
|
17
|
+
className?: string;
|
|
18
|
+
classNames?: {
|
|
19
|
+
rootDivClassName?: string;
|
|
20
|
+
};
|
|
21
|
+
getLocalizations?: () => {
|
|
22
|
+
calendar?: Calendar;
|
|
23
|
+
locale?: Locale;
|
|
24
|
+
months?: string[];
|
|
25
|
+
};
|
|
26
|
+
placeholder: string;
|
|
27
|
+
place?: string;
|
|
28
|
+
required?: boolean;
|
|
29
|
+
format?: string;
|
|
30
|
+
requiredHint?: string;
|
|
31
|
+
hintColor?: string;
|
|
32
|
+
label?: string;
|
|
33
|
+
errorMessage?: string;
|
|
34
|
+
readOnly?: boolean;
|
|
35
|
+
measurement?: DatePickerSize;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default function DatePicker(props: DatePickerProps) {
|
|
39
|
+
const {
|
|
40
|
+
dateOnComplete,
|
|
41
|
+
value,
|
|
42
|
+
className,
|
|
43
|
+
classNames,
|
|
44
|
+
placeholder,
|
|
45
|
+
required,
|
|
46
|
+
requiredHint,
|
|
47
|
+
measurement,
|
|
48
|
+
label,
|
|
49
|
+
errorMessage,
|
|
50
|
+
readOnly,
|
|
51
|
+
format = "YYYY-MM-DD",
|
|
52
|
+
getLocalizations,
|
|
53
|
+
} = props;
|
|
54
|
+
const { rootDivClassName } = classNames || {};
|
|
55
|
+
|
|
56
|
+
const [visible, setVisible] = useState(false);
|
|
57
|
+
const [selectedDates, setSelectedDates] = useState<DateObject | undefined>(
|
|
58
|
+
typeof value === "string" ? new DateObject(new Date(value)) : value
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const calendarWrapperRef = useRef<HTMLDivElement | null>(null);
|
|
62
|
+
const calenderParentRef = useRef<HTMLDivElement | null>(null);
|
|
63
|
+
const hasError = !!errorMessage;
|
|
64
|
+
|
|
65
|
+
const [position, setPosition] = useState<{
|
|
66
|
+
top: number;
|
|
67
|
+
left: number;
|
|
68
|
+
} | null>(null);
|
|
69
|
+
|
|
70
|
+
// Close calendar on outside clicks
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
73
|
+
if (
|
|
74
|
+
calendarWrapperRef.current &&
|
|
75
|
+
!calendarWrapperRef.current.contains(event.target as Node) &&
|
|
76
|
+
calenderParentRef.current &&
|
|
77
|
+
!calenderParentRef.current.contains(event.target as Node)
|
|
78
|
+
) {
|
|
79
|
+
setVisible(false);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
84
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
// Update position when calendar becomes visible
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (visible && calenderParentRef.current) {
|
|
90
|
+
const rect = calenderParentRef.current.getBoundingClientRect();
|
|
91
|
+
setPosition({
|
|
92
|
+
top: rect.bottom + window.scrollY,
|
|
93
|
+
left: rect.left + window.scrollX,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}, [visible]);
|
|
97
|
+
|
|
98
|
+
const formatHijriDate = (date?: DateObject) => {
|
|
99
|
+
try {
|
|
100
|
+
if (date) {
|
|
101
|
+
return date
|
|
102
|
+
.convert(localizations.calendar, localizations.locale)
|
|
103
|
+
.format(format);
|
|
104
|
+
}
|
|
105
|
+
} catch (e: any) {
|
|
106
|
+
console.log(e, "DatePicker");
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const handleDateChange = (date: DateObject) => {
|
|
112
|
+
setVisible(false);
|
|
113
|
+
const failed = dateOnComplete(date);
|
|
114
|
+
if (failed) return;
|
|
115
|
+
setSelectedDates(date);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const onVisibilityChange = () => {
|
|
119
|
+
if (!readOnly) setVisible((v) => !v);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const localizations = getLocalizations
|
|
123
|
+
? getLocalizations()
|
|
124
|
+
: {
|
|
125
|
+
calendar: gregorian,
|
|
126
|
+
locale: gregorian_en,
|
|
127
|
+
months: [],
|
|
128
|
+
};
|
|
129
|
+
const heightStyle = useMemo(
|
|
130
|
+
() =>
|
|
131
|
+
measurement == "lg"
|
|
132
|
+
? {
|
|
133
|
+
height: "50px",
|
|
134
|
+
paddingBottom: "pb-[3px]",
|
|
135
|
+
endContent: label
|
|
136
|
+
? "ltr:top-[48px] rtl:top-[54px]-translate-y-1/2"
|
|
137
|
+
: "top-[26px] -translate-y-1/2",
|
|
138
|
+
startContent: label
|
|
139
|
+
? "ltr:top-[48px] rtl:top-[54px] -translate-y-1/2"
|
|
140
|
+
: "top-[26px] -translate-y-1/2",
|
|
141
|
+
required: label ? "ltr:top-[4px] rtl:top-[12px]" : "top-[-19px]",
|
|
142
|
+
}
|
|
143
|
+
: measurement == "md"
|
|
144
|
+
? {
|
|
145
|
+
height: "44px",
|
|
146
|
+
paddingBottom: "pb-[2px]",
|
|
147
|
+
endContent: label
|
|
148
|
+
? "ltr:top-[45px] rtl:top-[51px] -translate-y-1/2"
|
|
149
|
+
: "top-[22px] -translate-y-1/2",
|
|
150
|
+
startContent: label
|
|
151
|
+
? "ltr:top-[45px] rtl:top-[51px] -translate-y-1/2"
|
|
152
|
+
: "top-[22px] -translate-y-1/2",
|
|
153
|
+
required: label ? "ltr:top-[4px] rtl:top-[12px]" : "top-[-19px]",
|
|
154
|
+
}
|
|
155
|
+
: {
|
|
156
|
+
height: "40px",
|
|
157
|
+
paddingBottom: "pb-[2px]",
|
|
158
|
+
endContent: label
|
|
159
|
+
? "ltr:top-[44px] rtl:top-[50px] -translate-y-1/2"
|
|
160
|
+
: "top-[20px] -translate-y-1/2",
|
|
161
|
+
startContent: label
|
|
162
|
+
? "ltr:top-[44px] rtl:top-[50px] -translate-y-1/2"
|
|
163
|
+
: "top-[20px] -translate-y-1/2",
|
|
164
|
+
required: label ? "ltr:top-[4px] rtl:top-[12px]" : "top-[-19px]",
|
|
165
|
+
},
|
|
166
|
+
[measurement, label]
|
|
167
|
+
);
|
|
168
|
+
const readOnlyStyle = readOnly && "opacity-40";
|
|
169
|
+
return (
|
|
170
|
+
<div
|
|
171
|
+
ref={calenderParentRef}
|
|
172
|
+
className={cn("relative", rootDivClassName, readOnlyStyle)}
|
|
173
|
+
>
|
|
174
|
+
{/* Calendar portal */}
|
|
175
|
+
{visible &&
|
|
176
|
+
position &&
|
|
177
|
+
createPortal(
|
|
178
|
+
<div
|
|
179
|
+
ref={calendarWrapperRef}
|
|
180
|
+
style={{
|
|
181
|
+
position: "absolute",
|
|
182
|
+
top: position.top,
|
|
183
|
+
left: position.left,
|
|
184
|
+
zIndex: 9999,
|
|
185
|
+
backgroundColor: "white",
|
|
186
|
+
boxShadow: "0 4px 10px rgba(0,0,0,0.15)",
|
|
187
|
+
borderRadius: "6px",
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
<Calendars
|
|
191
|
+
value={selectedDates}
|
|
192
|
+
onChange={handleDateChange}
|
|
193
|
+
months={localizations.months}
|
|
194
|
+
calendar={localizations.calendar}
|
|
195
|
+
locale={localizations.locale}
|
|
196
|
+
/>
|
|
197
|
+
</div>,
|
|
198
|
+
document.body
|
|
199
|
+
)}
|
|
200
|
+
{/* Required Hint */}
|
|
201
|
+
{requiredHint && (
|
|
202
|
+
<span
|
|
203
|
+
className={cn(
|
|
204
|
+
"absolute font-semibold text-red-600 rtl:text-[13px] ltr:text-[11px] ltr:right-2.5 rtl:left-2.5",
|
|
205
|
+
heightStyle.required
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
208
|
+
{requiredHint}
|
|
209
|
+
</span>
|
|
210
|
+
)}
|
|
211
|
+
|
|
212
|
+
{/* Label */}
|
|
213
|
+
{label && (
|
|
214
|
+
<label
|
|
215
|
+
htmlFor={label}
|
|
216
|
+
className={cn(
|
|
217
|
+
"font-semibold rtl:text-xl-rtl ltr:text-lg-ltr inline-block pb-1"
|
|
218
|
+
)}
|
|
219
|
+
>
|
|
220
|
+
{label}
|
|
221
|
+
</label>
|
|
222
|
+
)}
|
|
223
|
+
{/* Input / trigger div */}
|
|
224
|
+
<div
|
|
225
|
+
style={{
|
|
226
|
+
height: heightStyle.height,
|
|
227
|
+
}}
|
|
228
|
+
className={cn(
|
|
229
|
+
"flex items-center text-start px-3 border select-none rounded-sm rtl:text-lg-rtl ltr:text-lg-ltr",
|
|
230
|
+
className
|
|
231
|
+
)}
|
|
232
|
+
onClick={onVisibilityChange}
|
|
233
|
+
>
|
|
234
|
+
{selectedDates ? (
|
|
235
|
+
<h1 className="flex items-center gap-x-2 text-ellipsis rtl:text-lg-rtl ltr:text-lg-ltr text-primary/80 whitespace-nowrap overflow-hidden">
|
|
236
|
+
<CalendarDays className="size-4 inline-block text-tertiary rtl:ml-2 rtl:mr-2" />
|
|
237
|
+
{formatHijriDate(selectedDates)}
|
|
238
|
+
</h1>
|
|
239
|
+
) : (
|
|
240
|
+
<h1 className="flex items-center gap-x-2 text-ellipsis rtl:text-lg-rtl ltr:text-lg-ltr font-semibold text-primary whitespace-nowrap overflow-hidden">
|
|
241
|
+
<CalendarDays className="size-4 inline-block text-tertiary" />
|
|
242
|
+
{placeholder}
|
|
243
|
+
</h1>
|
|
244
|
+
)}
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
{hasError && (
|
|
248
|
+
<AnimatedItem
|
|
249
|
+
springProps={{
|
|
250
|
+
from: {
|
|
251
|
+
opacity: 0,
|
|
252
|
+
transform: "translateY(-8px)",
|
|
253
|
+
},
|
|
254
|
+
config: {
|
|
255
|
+
mass: 1,
|
|
256
|
+
tension: 210,
|
|
257
|
+
friction: 20,
|
|
258
|
+
},
|
|
259
|
+
to: {
|
|
260
|
+
opacity: 1,
|
|
261
|
+
transform: "translateY(0px)",
|
|
262
|
+
},
|
|
263
|
+
}}
|
|
264
|
+
intersectionArgs={{ once: true, rootMargin: "-5% 0%" }}
|
|
265
|
+
>
|
|
266
|
+
<h1 className="text-red-400 text-start capitalize rtl:text-sm rtl:font-medium ltr:text-sm-ltr">
|
|
267
|
+
{errorMessage}
|
|
268
|
+
</h1>
|
|
269
|
+
</AnimatedItem>
|
|
270
|
+
)}
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
@@ -9,11 +9,13 @@ export type NastranInputSize = "sm" | "md" | "lg";
|
|
|
9
9
|
export interface InputProps
|
|
10
10
|
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
11
11
|
startContent?: React.ReactNode;
|
|
12
|
+
endContent?: React.ReactNode;
|
|
12
13
|
requiredHint?: string;
|
|
13
14
|
label?: string;
|
|
14
|
-
endContent?: React.ReactNode;
|
|
15
15
|
errorMessage?: string;
|
|
16
|
-
|
|
16
|
+
classNames?: {
|
|
17
|
+
rootDivClassName?: string;
|
|
18
|
+
};
|
|
17
19
|
measurement?: NastranInputSize;
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -25,7 +27,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
25
27
|
requiredHint,
|
|
26
28
|
startContent,
|
|
27
29
|
endContent,
|
|
28
|
-
|
|
30
|
+
classNames,
|
|
29
31
|
measurement = "sm",
|
|
30
32
|
errorMessage,
|
|
31
33
|
label,
|
|
@@ -35,6 +37,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
35
37
|
ref
|
|
36
38
|
) => {
|
|
37
39
|
const hasError = !!errorMessage;
|
|
40
|
+
const { rootDivClassName } = classNames || {};
|
|
38
41
|
|
|
39
42
|
const inputPaddingClass = startContent
|
|
40
43
|
? "rtl:pr-[42px] ltr:ps-[42px]"
|
|
@@ -45,7 +48,6 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
45
48
|
measurement == "lg"
|
|
46
49
|
? {
|
|
47
50
|
height: "50px",
|
|
48
|
-
paddingBottom: "pb-[3px]",
|
|
49
51
|
endContent: label
|
|
50
52
|
? "ltr:top-[48px] rtl:top-[54px]-translate-y-1/2"
|
|
51
53
|
: "top-[26px] -translate-y-1/2",
|
|
@@ -57,7 +59,6 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
57
59
|
: measurement == "md"
|
|
58
60
|
? {
|
|
59
61
|
height: "44px",
|
|
60
|
-
paddingBottom: "pb-[2px]",
|
|
61
62
|
endContent: label
|
|
62
63
|
? "ltr:top-[45px] rtl:top-[51px] -translate-y-1/2"
|
|
63
64
|
: "top-[22px] -translate-y-1/2",
|
|
@@ -68,7 +69,6 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
68
69
|
}
|
|
69
70
|
: {
|
|
70
71
|
height: "40px",
|
|
71
|
-
paddingBottom: "pb-[2px]",
|
|
72
72
|
endContent: label
|
|
73
73
|
? "ltr:top-[44px] rtl:top-[50px] -translate-y-1/2"
|
|
74
74
|
: "top-[20px] -translate-y-1/2",
|
|
@@ -79,8 +79,16 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
79
79
|
},
|
|
80
80
|
[measurement, label]
|
|
81
81
|
);
|
|
82
|
+
const readOnlyStyle = readOnly && "opacity-40";
|
|
83
|
+
|
|
82
84
|
return (
|
|
83
|
-
<div
|
|
85
|
+
<div
|
|
86
|
+
className={cn(
|
|
87
|
+
rootDivClassName,
|
|
88
|
+
"flex w-full flex-col justify-end",
|
|
89
|
+
readOnlyStyle
|
|
90
|
+
)}
|
|
91
|
+
>
|
|
84
92
|
<div
|
|
85
93
|
className={cn(
|
|
86
94
|
"relative text-start select-none h-fit rtl:text-lg-rtl ltr:text-lg-ltr"
|
|
@@ -145,20 +153,19 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
145
153
|
height: heightStyle.height,
|
|
146
154
|
}}
|
|
147
155
|
className={cn(
|
|
148
|
-
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30
|
|
156
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 flex w-full min-w-0 rounded-sm border px-3 text-base 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-70",
|
|
149
157
|
"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 focus-visible:shadow-sm focus-visible:ring-offset-0 transition-[border] bg-card
|
|
158
|
+
"appearance-none placeholder:text-primary/60 ltr:text-sm rtl:text-sm rtl:font-semibold focus-visible:ring-0 focus-visible:shadow-sm focus-visible:ring-offset-0 transition-[border] bg-card",
|
|
151
159
|
"focus-visible:border-tertiary/60",
|
|
152
160
|
"[&::-webkit-outer-spin-button]:appearance-none",
|
|
153
161
|
"[&::-webkit-inner-spin-button]:appearance-none",
|
|
154
162
|
"[-moz-appearance:textfield] ",
|
|
155
163
|
inputPaddingClass,
|
|
156
|
-
hasError
|
|
157
|
-
readOnly && "cursor-not-allowed",
|
|
158
|
-
heightStyle.paddingBottom,
|
|
164
|
+
hasError && "border-red-400",
|
|
159
165
|
className
|
|
160
166
|
)}
|
|
161
167
|
{...rest}
|
|
168
|
+
disabled={readOnly}
|
|
162
169
|
/>
|
|
163
170
|
</div>
|
|
164
171
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import DateObject from "react-date-object";
|
|
4
|
+
|
|
5
|
+
import MultiDatePicker, { MultiDatePickerProps } from "./multi-date-picker";
|
|
6
|
+
|
|
7
|
+
// Persian
|
|
8
|
+
import persian from "react-date-object/calendars/persian";
|
|
9
|
+
import persian_fa from "react-date-object/locales/persian_fa";
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
title: "Date/MultiDatePicker",
|
|
13
|
+
component: MultiDatePicker,
|
|
14
|
+
parameters: {
|
|
15
|
+
layout: "centered",
|
|
16
|
+
},
|
|
17
|
+
} as Meta<MultiDatePickerProps>;
|
|
18
|
+
|
|
19
|
+
/* ------------------------ WRAPPER ------------------------ */
|
|
20
|
+
const Wrapper = (args: MultiDatePickerProps) => {
|
|
21
|
+
const [dates, setDates] = React.useState<DateObject[]>(args.value);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div style={{ width: 360 }}>
|
|
25
|
+
<MultiDatePicker
|
|
26
|
+
{...args}
|
|
27
|
+
value={dates}
|
|
28
|
+
dateOnComplete={(selectedDates) => {
|
|
29
|
+
setDates(selectedDates);
|
|
30
|
+
}}
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/* ------------------------ DEFAULT ------------------------ */
|
|
37
|
+
export const Default: StoryObj<MultiDatePickerProps> = {
|
|
38
|
+
render: (args) => <Wrapper {...args} />,
|
|
39
|
+
args: {
|
|
40
|
+
text: { label: "Select dates", placeholder: "Select dates..." },
|
|
41
|
+
measurement: "md",
|
|
42
|
+
value: [],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/* ------------------------ PERSIAN ------------------------ */
|
|
47
|
+
export const Persian: StoryObj<MultiDatePickerProps> = {
|
|
48
|
+
render: (args) => <Wrapper {...args} />,
|
|
49
|
+
args: {
|
|
50
|
+
text: { label: "تاریخها", placeholder: "تاریخها را انتخاب کنید" },
|
|
51
|
+
measurement: "md",
|
|
52
|
+
value: [],
|
|
53
|
+
getLocalizations: () => ({
|
|
54
|
+
calendar: persian,
|
|
55
|
+
locale: persian_fa,
|
|
56
|
+
months: [
|
|
57
|
+
"فروردین",
|
|
58
|
+
"اردیبهشت",
|
|
59
|
+
"خرداد",
|
|
60
|
+
"تیر",
|
|
61
|
+
"مرداد",
|
|
62
|
+
"شهریور",
|
|
63
|
+
"مهر",
|
|
64
|
+
"آبان",
|
|
65
|
+
"آذر",
|
|
66
|
+
"دی",
|
|
67
|
+
"بهمن",
|
|
68
|
+
"اسفند",
|
|
69
|
+
],
|
|
70
|
+
}),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/* ------------------------ WITH ERROR ------------------------ */
|
|
75
|
+
export const WithError: StoryObj<MultiDatePickerProps> = {
|
|
76
|
+
render: (args) => <Wrapper {...args} />,
|
|
77
|
+
args: {
|
|
78
|
+
text: { label: "Dates", requiredHint: "*" },
|
|
79
|
+
value: [],
|
|
80
|
+
measurement: "md",
|
|
81
|
+
errorMessage: "Please select at least one date",
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/* ------------------------ READONLY ------------------------ */
|
|
86
|
+
export const ReadOnly: StoryObj<MultiDatePickerProps> = {
|
|
87
|
+
render: (args) => <Wrapper {...args} />,
|
|
88
|
+
args: {
|
|
89
|
+
text: { label: "Dates" },
|
|
90
|
+
value: [new DateObject(), new DateObject()],
|
|
91
|
+
readOnly: true,
|
|
92
|
+
measurement: "md",
|
|
93
|
+
},
|
|
94
|
+
};
|