Skip to content

Commit 103bbac

Browse files
committed
[Feature] Calendar pops at the top of input
[Feature] `disabled` prop [Feature] Additional prop to overload input class: `inputClassName` [Feature] Additional prop to overload container class: `containerClassName`
1 parent c36cfaa commit 103bbac

File tree

3 files changed

+67
-13
lines changed

3 files changed

+67
-13
lines changed

src/components/Datepicker.tsx

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ interface Props {
4343
separator?: string;
4444
startFrom?: Date | null;
4545
i18n?: string;
46+
disabled?: boolean;
47+
inputClassName?: string | null;
48+
containerClassName?: string | null;
4649
}
4750

4851
const Datepicker: React.FC<Props> = ({
@@ -57,7 +60,10 @@ const Datepicker: React.FC<Props> = ({
5760
placeholder = null,
5861
separator = "~",
5962
startFrom = null,
60-
i18n = "en"
63+
i18n = "en",
64+
disabled = false,
65+
inputClassName = null,
66+
containerClassName = null
6167
}) => {
6268
// Ref
6369
const containerRef = useRef<HTMLDivElement>(null);
@@ -87,14 +93,23 @@ const Datepicker: React.FC<Props> = ({
8793
// Functions
8894
const hideDatepicker = useCallback(() => {
8995
const div = calendarContainerRef.current;
90-
if (div && div.classList.contains("block")) {
96+
const arrow = arrowRef.current;
97+
if (arrow && div && div.classList.contains("block")) {
9198
div.classList.remove("block");
9299
div.classList.remove("translate-y-0");
93100
div.classList.remove("opacity-1");
94101
div.classList.add("translate-y-4");
95102
div.classList.add("opacity-0");
96103
setTimeout(() => {
104+
div.classList.remove("bottom-full");
97105
div.classList.add("hidden");
106+
div.classList.add("mb-2.5");
107+
div.classList.add("mt-2.5");
108+
arrow.classList.remove("-bottom-2");
109+
arrow.classList.remove("border-r");
110+
arrow.classList.remove("border-b");
111+
arrow.classList.add("border-l");
112+
arrow.classList.add("border-t");
98113
}, 300);
99114
}
100115
}, []);
@@ -177,6 +192,9 @@ const Datepicker: React.FC<Props> = ({
177192
const detail = container.getBoundingClientRect();
178193
const screenCenter = window.innerWidth / 2;
179194
const containerCenter = (detail.right - detail.x) / 2 + detail.x;
195+
196+
console.log(detail.bottom, calendarContainer.getBoundingClientRect().top);
197+
180198
if (containerCenter > screenCenter) {
181199
arrow.classList.add("right-0");
182200
arrow.classList.add("mr-3.5");
@@ -224,6 +242,7 @@ const Datepicker: React.FC<Props> = ({
224242
primaryColor: colorPrimary,
225243
configs,
226244
calendarContainer: calendarContainerRef,
245+
arrowContainer: arrowRef,
227246
hideDatepicker,
228247
period,
229248
changePeriod: (newPeriod: Period) => setPeriod(newPeriod),
@@ -237,7 +256,10 @@ const Datepicker: React.FC<Props> = ({
237256
placeholder,
238257
separator,
239258
i18n,
240-
value
259+
value,
260+
disabled,
261+
inputClassName,
262+
containerClassName
241263
};
242264
}, [
243265
asSingle,
@@ -253,12 +275,18 @@ const Datepicker: React.FC<Props> = ({
253275
placeholder,
254276
separator,
255277
showFooter,
256-
value
278+
value,
279+
disabled,
280+
inputClassName,
281+
containerClassName
257282
]);
258283

259284
return (
260285
<DatepickerContext.Provider value={contextValues}>
261-
<div className="relative w-full text-gray-700" ref={containerRef}>
286+
<div
287+
className={`relative w-full text-gray-700 ${containerClassName}`}
288+
ref={containerRef}
289+
>
262290
<Input />
263291

264292
<div
@@ -272,7 +300,7 @@ const Datepicker: React.FC<Props> = ({
272300
{showShortcuts && <Shortcuts />}
273301

274302
<div
275-
className={`flex items-stretch flex-col md:flex-row items-center space-y-4 md:space-y-0 md:space-x-1.5 ${
303+
className={`flex items-stretch flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-1.5 ${
276304
showShortcuts ? "md:pl-2" : "md:pl-1"
277305
} pr-2 lg:pr-1`}
278306
>

src/components/Input.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ const Input: React.FC = () => {
1515
dayHover,
1616
changeDayHover,
1717
calendarContainer,
18+
arrowContainer,
1819
inputText,
1920
changeInputText,
2021
hideDatepicker,
2122
changeDatepickerValue,
2223
asSingle,
2324
placeholder,
24-
separator
25+
separator,
26+
disabled,
27+
inputClassName
2528
} = useContext(DatepickerContext);
2629

2730
// UseRefs
@@ -33,8 +36,9 @@ const Input: React.FC = () => {
3336
const border = BORDER_COLOR.focus[primaryColor as keyof typeof BORDER_COLOR.focus];
3437
const ring =
3538
RING_COLOR["second-focus"][primaryColor as keyof typeof RING_COLOR["second-focus"]];
36-
return `relative transition-all duration-300 py-2.5 pl-4 pr-14 w-full border-gray-300 dark:bg-slate-800 dark:text-white/80 dark:border-slate-600 rounded-lg tracking-wide font-light text-sm placeholder-gray-400 bg-white focus:ring ${border} ${ring}`;
37-
}, [primaryColor]);
39+
const classNameOverload = typeof inputClassName === "string" ? inputClassName : "";
40+
return `relative transition-all duration-300 py-2.5 pl-4 pr-14 w-full border-gray-300 dark:bg-slate-800 dark:text-white/80 dark:border-slate-600 rounded-lg tracking-wide font-light text-sm placeholder-gray-400 bg-white focus:ring disabled:opacity-40 disabled:cursor-not-allowed ${border} ${ring} ${classNameOverload}`;
41+
}, [primaryColor, inputClassName]);
3842

3943
const handleInputChange = useCallback(
4044
(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -118,11 +122,23 @@ const Input: React.FC = () => {
118122
useEffect(() => {
119123
const div = calendarContainer?.current;
120124
const input = inputRef.current;
125+
const arrow = arrowContainer?.current;
121126

122127
function showCalendarContainer() {
123-
if (div && div.classList.contains("hidden")) {
128+
console.log(arrow);
129+
if (arrow && div && div.classList.contains("hidden")) {
124130
div.classList.remove("hidden");
125131
div.classList.add("block");
132+
if (window.screen.height - 100 < div.getBoundingClientRect().bottom) {
133+
div.classList.add("bottom-full");
134+
div.classList.add("mb-2.5");
135+
div.classList.remove("mt-2.5");
136+
arrow.classList.add("-bottom-2");
137+
arrow.classList.add("border-r");
138+
arrow.classList.add("border-b");
139+
arrow.classList.remove("border-l");
140+
arrow.classList.remove("border-t");
141+
}
126142
setTimeout(() => {
127143
div.classList.remove("translate-y-4");
128144
div.classList.remove("opacity-0");
@@ -141,14 +157,15 @@ const Input: React.FC = () => {
141157
input.removeEventListener("focus", showCalendarContainer);
142158
}
143159
};
144-
}, [calendarContainer]);
160+
}, [calendarContainer, arrowContainer]);
145161

146162
return (
147163
<>
148164
<input
149165
ref={inputRef}
150166
type="text"
151167
className={getClassName()}
168+
disabled={disabled}
152169
placeholder={
153170
placeholder
154171
? placeholder
@@ -161,7 +178,8 @@ const Input: React.FC = () => {
161178
<button
162179
type="button"
163180
ref={buttonRef}
164-
className="absolute right-0 h-full px-3 text-gray-400 focus:outline-none"
181+
disabled={disabled}
182+
className="absolute right-0 h-full px-3 text-gray-400 focus:outline-none disabled:opacity-40 disabled:cursor-not-allowed"
165183
>
166184
{inputText ? <CloseIcon className="h-5 w-5" /> : <DateIcon className="h-5 w-5" />}
167185
</button>

src/contexts/DatepickerContext.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ interface DatepickerStore {
1313
primaryColor: string;
1414
configs?: Configs | null;
1515
calendarContainer: React.RefObject<HTMLDivElement> | null;
16+
arrowContainer: React.RefObject<HTMLDivElement> | null;
1617
hideDatepicker: () => void;
1718
period: Period;
1819
changePeriod: (period: Period) => void;
@@ -27,11 +28,15 @@ interface DatepickerStore {
2728
separator?: string;
2829
i18n: string;
2930
value: DateRange;
31+
disabled?: boolean;
32+
inputClassName?: string | null;
33+
containerClassName?: string | null;
3034
}
3135

3236
const DatepickerContext = createContext<DatepickerStore>({
3337
primaryColor: "blue",
3438
calendarContainer: null,
39+
arrowContainer: null,
3540
// eslint-disable-next-line @typescript-eslint/no-empty-function
3641
hideDatepicker: () => {},
3742
period: { start: null, end: null },
@@ -49,7 +54,10 @@ const DatepickerContext = createContext<DatepickerStore>({
4954
changeDatepickerValue: value => {},
5055
showFooter: false,
5156
value: null,
52-
i18n: "en"
57+
i18n: "en",
58+
disabled: false,
59+
inputClassName: "",
60+
containerClassName: ""
5361
});
5462

5563
export default DatepickerContext;

0 commit comments

Comments
 (0)