gbs-add-block 0.0.1
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/index.js +108 -0
- package/package.json +28 -0
- package/source/components/button/index.tsx +25 -0
- package/source/components/checkbox/index.tsx +9 -0
- package/source/components/darkmode/index.tsx +48 -0
- package/source/components/datepicker/DatePickerHelper.ts +42 -0
- package/source/components/datepicker/index.tsx +258 -0
- package/source/components/datepicker/types.ts +9 -0
- package/source/components/dialog/index.tsx +44 -0
- package/source/components/dialog/types.ts +12 -0
- package/source/components/grid/FilterPopup.tsx +85 -0
- package/source/components/grid/Grid.tsx +615 -0
- package/source/components/grid/GridHelperFunctions.ts +218 -0
- package/source/components/grid/index.tsx +26 -0
- package/source/components/grid/type.ts +28 -0
- package/source/components/input/index.tsx +105 -0
- package/source/components/input/types.ts +9 -0
- package/source/components/modal/index.tsx +58 -0
- package/source/components/modal/types.ts +11 -0
- package/source/components/multiselect/index.tsx +213 -0
- package/source/components/multiselect/types.ts +14 -0
- package/source/components/select/index.tsx +220 -0
- package/source/components/select/types.ts +13 -0
- package/source/components/spinner/Stroke.tsx +89 -0
- package/source/components/spinner/index.tsx +54 -0
- package/source/components/spinner/types.ts +9 -0
- package/source/components/toast/Toast.tsx +61 -0
- package/source/components/toast/index.tsx +36 -0
- package/source/components/toast/toastAtom.ts +23 -0
- package/source/components/toast/types.ts +24 -0
- package/source/components/toast/useToast.ts +24 -0
- package/source/fallback/index.tsx +11 -0
- package/source/icon/Icon.tsx +55 -0
- package/source/icon/iconPaths.ts +92 -0
- package/source/index.ts +15 -0
- package/source/types.ts +108 -0
- package/source/utils.ts +10 -0
package/index.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs-extra");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const readline = require("readline");
|
|
6
|
+
|
|
7
|
+
const COMPONENTS = [
|
|
8
|
+
"Select",
|
|
9
|
+
"Grid",
|
|
10
|
+
"MultiSelect",
|
|
11
|
+
"Button",
|
|
12
|
+
"DatePicker",
|
|
13
|
+
"Checkbox",
|
|
14
|
+
"DarkMode",
|
|
15
|
+
"Dialog",
|
|
16
|
+
"Input",
|
|
17
|
+
"Modal",
|
|
18
|
+
"Spinner",
|
|
19
|
+
"Toast",
|
|
20
|
+
];
|
|
21
|
+
const SOURCE_PATH = path.join(process.cwd(), "source", "components");
|
|
22
|
+
const DEST_PATH = path.join(process.cwd(), "src", "component-lib");
|
|
23
|
+
|
|
24
|
+
const rl = readline.createInterface({
|
|
25
|
+
input: process.stdin,
|
|
26
|
+
output: process.stdout,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let isFirstCopy = true;
|
|
30
|
+
|
|
31
|
+
function listComponents() {
|
|
32
|
+
console.log("Available components:");
|
|
33
|
+
COMPONENTS.forEach((component, index) => {
|
|
34
|
+
console.log(`${index + 1}. ${component}`);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function copyCommonFiles() {
|
|
39
|
+
// Copy utils.ts
|
|
40
|
+
const utilsSrc = path.join(SOURCE_PATH, "..", "utils.ts");
|
|
41
|
+
const utilsDest = path.join(DEST_PATH, "utils.ts");
|
|
42
|
+
fs.copySync(utilsSrc, utilsDest, { overwrite: true });
|
|
43
|
+
console.log(`utils.ts copied successfully to ${utilsDest}`);
|
|
44
|
+
|
|
45
|
+
// Copy icon folder
|
|
46
|
+
const iconSrc = path.join(SOURCE_PATH, "..", "icon");
|
|
47
|
+
const iconDest = path.join(DEST_PATH, "icon");
|
|
48
|
+
fs.copySync(iconSrc, iconDest, { overwrite: true });
|
|
49
|
+
console.log(`icon folder copied successfully to ${iconDest}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function copyComponent(component) {
|
|
53
|
+
const componentSrc = path.join(SOURCE_PATH, component.toLowerCase());
|
|
54
|
+
const componentDest = path.join(DEST_PATH, component.toLowerCase());
|
|
55
|
+
|
|
56
|
+
if (!fs.existsSync(componentSrc)) {
|
|
57
|
+
console.error(`Component ${component} not found in source directory.`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fs.copySync(componentSrc, componentDest, { overwrite: true });
|
|
62
|
+
console.log(`Component ${component} copied successfully to ${componentDest}`);
|
|
63
|
+
|
|
64
|
+
if (isFirstCopy) {
|
|
65
|
+
copyCommonFiles();
|
|
66
|
+
isFirstCopy = false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function promptForComponent() {
|
|
71
|
+
listComponents();
|
|
72
|
+
rl.question(
|
|
73
|
+
'Enter the number of the component you want to copy (or "q" to quit): ',
|
|
74
|
+
(answer) => {
|
|
75
|
+
if (answer.toLowerCase() === "q") {
|
|
76
|
+
rl.close();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const index = parseInt(answer) - 1;
|
|
81
|
+
if (isNaN(index) || index < 0 || index >= COMPONENTS.length) {
|
|
82
|
+
console.log("Invalid selection. Please try again.");
|
|
83
|
+
promptForComponent();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const selectedComponent = COMPONENTS[index];
|
|
88
|
+
copyComponent(selectedComponent);
|
|
89
|
+
|
|
90
|
+
rl.question(
|
|
91
|
+
"Do you want to copy another component? (y/n): ",
|
|
92
|
+
(answer) => {
|
|
93
|
+
if (answer.toLowerCase() === "y") {
|
|
94
|
+
promptForComponent();
|
|
95
|
+
} else {
|
|
96
|
+
rl.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Ensure the destination directory exists
|
|
105
|
+
fs.ensureDirSync(DEST_PATH);
|
|
106
|
+
|
|
107
|
+
// Start the component selection process
|
|
108
|
+
promptForComponent();
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gbs-add-block",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "React Component Library",
|
|
5
|
+
"files": [
|
|
6
|
+
"index.js",
|
|
7
|
+
"source"
|
|
8
|
+
],
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=17"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"gbs-add-block": "./index.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [],
|
|
19
|
+
"author": "Anandhu Remanan",
|
|
20
|
+
"license": "ISC",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"fs-extra": "^11.2.0",
|
|
23
|
+
"path": "^0.12.7"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/fs-extra": "^11.0.4"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Grampro Business Services and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { ButtonHTMLAttributes } from "react";
|
|
9
|
+
|
|
10
|
+
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
11
|
+
children?: any;
|
|
12
|
+
buttonClass?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Button = ({
|
|
16
|
+
children,
|
|
17
|
+
buttonClass = "px-2 py-1 bg-black text-white rounded-md hover:bg-gray-700",
|
|
18
|
+
...props
|
|
19
|
+
}: ButtonProps) => {
|
|
20
|
+
return (
|
|
21
|
+
<button className={buttonClass} {...props}>
|
|
22
|
+
{children}
|
|
23
|
+
</button>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import Icon from "../icon/Icon";
|
|
3
|
+
import { moon, sun } from "../icon/iconPaths";
|
|
4
|
+
|
|
5
|
+
export const DarkMode = () => {
|
|
6
|
+
const [dark, setDark] = React.useState(false);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
// Check for user's preference in localStorage or system preference
|
|
10
|
+
const isDarkMode =
|
|
11
|
+
localStorage.getItem("darkMode") === "true" ||
|
|
12
|
+
(!("darkMode" in localStorage) &&
|
|
13
|
+
window.matchMedia("(prefers-color-scheme: dark)").matches);
|
|
14
|
+
setDark(isDarkMode);
|
|
15
|
+
updateDarkMode(isDarkMode);
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
const updateDarkMode = (isDark: boolean) => {
|
|
19
|
+
if (isDark) {
|
|
20
|
+
document.documentElement.classList.add("dark");
|
|
21
|
+
} else {
|
|
22
|
+
document.documentElement.classList.remove("dark");
|
|
23
|
+
}
|
|
24
|
+
localStorage.setItem("darkMode", isDark.toString());
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const darkModeHandler = () => {
|
|
28
|
+
const newDarkMode = !dark;
|
|
29
|
+
setDark(newDarkMode);
|
|
30
|
+
updateDarkMode(newDarkMode);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<button onClick={darkModeHandler}>
|
|
35
|
+
{dark ? (
|
|
36
|
+
<Icon
|
|
37
|
+
elements={sun}
|
|
38
|
+
svgClass={"stroke-gray-500 fill-none dark:stroke-white"}
|
|
39
|
+
/>
|
|
40
|
+
) : (
|
|
41
|
+
<Icon
|
|
42
|
+
elements={moon}
|
|
43
|
+
svgClass={"stroke-black fill-none dark:stroke-white"}
|
|
44
|
+
/>
|
|
45
|
+
)}
|
|
46
|
+
</button>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const getDaysInMonth = (year: any, month: any) => {
|
|
2
|
+
return new Date(year, month + 1, 0).getDate();
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const generateCalendarHelper = (year: any, month: any) => {
|
|
6
|
+
const daysInMonth = getDaysInMonth(year, month);
|
|
7
|
+
const firstDay = new Date(year, month, 1).getDay();
|
|
8
|
+
const weeks = [];
|
|
9
|
+
let day = 1;
|
|
10
|
+
|
|
11
|
+
for (let i = 0; i < 6; i++) {
|
|
12
|
+
const week = [];
|
|
13
|
+
for (let j = 0; j < 7; j++) {
|
|
14
|
+
if (i === 0 && j < firstDay) {
|
|
15
|
+
week.push(null);
|
|
16
|
+
} else if (day > daysInMonth) {
|
|
17
|
+
week.push(null);
|
|
18
|
+
} else {
|
|
19
|
+
const date = new Date(year, month, day);
|
|
20
|
+
week.push(date);
|
|
21
|
+
day++;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
weeks.push(week);
|
|
25
|
+
}
|
|
26
|
+
return weeks;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const months = [
|
|
30
|
+
'January',
|
|
31
|
+
'February',
|
|
32
|
+
'March',
|
|
33
|
+
'April',
|
|
34
|
+
'May',
|
|
35
|
+
'June',
|
|
36
|
+
'July',
|
|
37
|
+
'August',
|
|
38
|
+
'September',
|
|
39
|
+
'October',
|
|
40
|
+
'November',
|
|
41
|
+
'December'
|
|
42
|
+
];
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Grampro Business Services and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
9
|
+
import { generateCalendarHelper, months } from "./DatePickerHelper";
|
|
10
|
+
import Icon from "../icon/Icon";
|
|
11
|
+
import { calender, down, leftArrows, rightArrows } from "../icon/iconPaths";
|
|
12
|
+
import type { DatePickerProps } from "./types";
|
|
13
|
+
|
|
14
|
+
export const DatePicker = ({
|
|
15
|
+
placeholder = undefined,
|
|
16
|
+
selectedDate: initialSelectedDate = new Date(),
|
|
17
|
+
minDate = null,
|
|
18
|
+
maxDate = null,
|
|
19
|
+
yearLimitStart = 50,
|
|
20
|
+
yearLimitEnd = 30,
|
|
21
|
+
onDateChange,
|
|
22
|
+
}: DatePickerProps) => {
|
|
23
|
+
const [showDatepicker, setShowDatepicker] = useState(false);
|
|
24
|
+
const [showYearMonthPicker, setShowYearMonthPicker] = useState(false);
|
|
25
|
+
const [currentMonth, setCurrentMonth] = useState(new Date().getMonth());
|
|
26
|
+
const [currentYear, setCurrentYear] = useState(new Date().getFullYear());
|
|
27
|
+
const [days, setDays] = useState<any>([]);
|
|
28
|
+
const [selectedDate, setSelectedDate] = useState(initialSelectedDate);
|
|
29
|
+
|
|
30
|
+
const dateRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
const today = new Date();
|
|
32
|
+
|
|
33
|
+
// This will helps to close the popup when clicked outside of datepicker component
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
function handleClickOutside(event: MouseEvent) {
|
|
36
|
+
if (dateRef.current && !dateRef.current.contains(event.target as Node)) {
|
|
37
|
+
setShowDatepicker(false);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
42
|
+
return () => {
|
|
43
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
44
|
+
};
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
setDays(generateCalendarHelper(currentYear, currentMonth));
|
|
49
|
+
}, [currentYear, currentMonth]);
|
|
50
|
+
|
|
51
|
+
// DatePicker Toggler
|
|
52
|
+
const toggleDatepicker = () => {
|
|
53
|
+
setShowDatepicker(!showDatepicker);
|
|
54
|
+
setShowYearMonthPicker(false);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// YearMonthPicker Toggler
|
|
58
|
+
const toggleYearMonthPicker = () => {
|
|
59
|
+
setShowYearMonthPicker(!showYearMonthPicker);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const selectDate = (date: any) => {
|
|
63
|
+
setSelectedDate(date);
|
|
64
|
+
setShowDatepicker(false);
|
|
65
|
+
if (onDateChange) onDateChange(date);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const selectYearMonth = (year: any, month: any) => {
|
|
69
|
+
setCurrentYear(year);
|
|
70
|
+
setCurrentMonth(month);
|
|
71
|
+
setShowYearMonthPicker(false);
|
|
72
|
+
setDays(generateCalendarHelper(year, month));
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// *** DatePicker navigation starts here
|
|
76
|
+
const prevMonth = () => {
|
|
77
|
+
if (currentMonth === 0) {
|
|
78
|
+
setCurrentMonth(11);
|
|
79
|
+
setCurrentYear(currentYear - 1);
|
|
80
|
+
} else {
|
|
81
|
+
setCurrentMonth(currentMonth - 1);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const nextMonth = () => {
|
|
86
|
+
if (currentMonth === 11) {
|
|
87
|
+
setCurrentMonth(0);
|
|
88
|
+
setCurrentYear(currentYear + 1);
|
|
89
|
+
} else {
|
|
90
|
+
setCurrentMonth(currentMonth + 1);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const goToToday = () => {
|
|
95
|
+
setCurrentMonth(today.getMonth());
|
|
96
|
+
setCurrentYear(today.getFullYear());
|
|
97
|
+
setSelectedDate(new Date());
|
|
98
|
+
if (onDateChange) onDateChange(new Date());
|
|
99
|
+
};
|
|
100
|
+
// DatePicker navigation ends here ***
|
|
101
|
+
|
|
102
|
+
const years = Array.from(
|
|
103
|
+
{ length: yearLimitEnd },
|
|
104
|
+
(_, i) => currentYear - yearLimitStart + i
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div className="relative inline-block w-80 dark:bg-black" ref={dateRef}>
|
|
109
|
+
<button
|
|
110
|
+
onClick={toggleDatepicker}
|
|
111
|
+
className="border p-2 rounded w-full flex gap-2 items-center justify-center dark:text-white"
|
|
112
|
+
>
|
|
113
|
+
<Icon
|
|
114
|
+
elements={calender}
|
|
115
|
+
svgClass={"stroke-black fill-none dark:stroke-white"}
|
|
116
|
+
/>
|
|
117
|
+
{placeholder ? (
|
|
118
|
+
<span className="text-gray-500">{placeholder}</span>
|
|
119
|
+
) : (
|
|
120
|
+
selectedDate.toLocaleDateString()
|
|
121
|
+
)}
|
|
122
|
+
</button>
|
|
123
|
+
|
|
124
|
+
{showDatepicker && (
|
|
125
|
+
<div className="absolute z-10 bg-white border border-gray-300 shadow-lg mt-1 w-full rounded dark:bg-black dark:text-white px-2">
|
|
126
|
+
<div className="flex justify-between items-center p-2">
|
|
127
|
+
<button
|
|
128
|
+
onClick={prevMonth}
|
|
129
|
+
className="text-gray-500 hover:text-gray-700 w-8 h-8 rounded-full flex items-center justify-center hover:bg-gray-100"
|
|
130
|
+
>
|
|
131
|
+
<Icon
|
|
132
|
+
elements={leftArrows}
|
|
133
|
+
svgClass={"stroke-gray-500 fill-none dark:stroke-white"}
|
|
134
|
+
/>
|
|
135
|
+
</button>
|
|
136
|
+
<div className="flex items-center space-x-2">
|
|
137
|
+
<span className="text-md font-semibold">
|
|
138
|
+
{new Date(currentYear, currentMonth).toLocaleString("default", {
|
|
139
|
+
month: "long",
|
|
140
|
+
})}{" "}
|
|
141
|
+
{currentYear}
|
|
142
|
+
</span>
|
|
143
|
+
<button onClick={toggleYearMonthPicker} className="">
|
|
144
|
+
<Icon
|
|
145
|
+
elements={down}
|
|
146
|
+
svgClass={"stroke-black fill-none dark:stroke-white"}
|
|
147
|
+
/>
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
<button
|
|
151
|
+
onClick={nextMonth}
|
|
152
|
+
className="text-gray-500 hover:text-gray-700 w-8 h-8 rounded-full flex items-center justify-center hover:bg-gray-100"
|
|
153
|
+
>
|
|
154
|
+
<Icon
|
|
155
|
+
elements={rightArrows}
|
|
156
|
+
svgClass={"stroke-black fill-none dark:stroke-white"}
|
|
157
|
+
/>
|
|
158
|
+
</button>
|
|
159
|
+
</div>
|
|
160
|
+
{showYearMonthPicker ? (
|
|
161
|
+
<div className="flex p-2 h-[224px] scrollbar">
|
|
162
|
+
<div className="w-1/2 pr-1">
|
|
163
|
+
<h3 className="text-sm font-semibold mb-1">Month</h3>
|
|
164
|
+
<div className="overflow-y-auto h-[200px] scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100 scrollbar">
|
|
165
|
+
{months.map((month, index) => (
|
|
166
|
+
<button
|
|
167
|
+
key={month}
|
|
168
|
+
onClick={() => selectYearMonth(currentYear, index)}
|
|
169
|
+
className={`w-full text-left p-2 cursor-pointer rounded hover:bg-gray-200 transition duration-150 ease-in-out ${
|
|
170
|
+
index === currentMonth
|
|
171
|
+
? "bg-blue-500 text-white hover:bg-blue-600"
|
|
172
|
+
: ""
|
|
173
|
+
}`}
|
|
174
|
+
>
|
|
175
|
+
{month}
|
|
176
|
+
</button>
|
|
177
|
+
))}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
<div className="w-1/2 pl-1">
|
|
181
|
+
<h3 className="text-sm font-semibold mb-1">Year</h3>
|
|
182
|
+
<div className="overflow-y-auto h-[200px] scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100 scrollbar">
|
|
183
|
+
{years.map((year) => (
|
|
184
|
+
<button
|
|
185
|
+
key={year}
|
|
186
|
+
onClick={() => selectYearMonth(year, currentMonth)}
|
|
187
|
+
className={`w-full text-left p-2 cursor-pointer rounded hover:bg-gray-200 transition duration-150 ease-in-out ${
|
|
188
|
+
year === currentYear
|
|
189
|
+
? "bg-blue-500 text-white hover:bg-blue-600"
|
|
190
|
+
: ""
|
|
191
|
+
}`}
|
|
192
|
+
>
|
|
193
|
+
{year}
|
|
194
|
+
</button>
|
|
195
|
+
))}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
) : (
|
|
200
|
+
<div className="grid grid-cols-7 gap-1 p-2">
|
|
201
|
+
{["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => (
|
|
202
|
+
<div key={day} className="text-center font-bold text-sm p-1">
|
|
203
|
+
{day}
|
|
204
|
+
</div>
|
|
205
|
+
))}
|
|
206
|
+
{days.flat().map((day: any, index: any) => {
|
|
207
|
+
if (!day) {
|
|
208
|
+
return (
|
|
209
|
+
<div key={index} className="text-center p-1 w-8 h-8"></div>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
const isDisabled: any =
|
|
213
|
+
(minDate && day < minDate) || (maxDate && day > maxDate);
|
|
214
|
+
const isSelected =
|
|
215
|
+
day.getDate() === selectedDate.getDate() &&
|
|
216
|
+
day.getMonth() === selectedDate.getMonth() &&
|
|
217
|
+
day.getFullYear() === selectedDate.getFullYear();
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<button
|
|
221
|
+
key={index}
|
|
222
|
+
onClick={() => !isDisabled && selectDate(day)}
|
|
223
|
+
disabled={isDisabled}
|
|
224
|
+
className={`text-center p-1 w-8 h-8 cursor-pointer rounded-full hover:bg-gray-200 transition duration-150 ease-in-out ${
|
|
225
|
+
isSelected
|
|
226
|
+
? "bg-blue-500 text-white hover:bg-blue-600"
|
|
227
|
+
: ""
|
|
228
|
+
} ${
|
|
229
|
+
isDisabled
|
|
230
|
+
? "bg-gray-100 text-gray-400 cursor-not-allowed"
|
|
231
|
+
: "hover:bg-gray-200"
|
|
232
|
+
}`}
|
|
233
|
+
>
|
|
234
|
+
{day.getDate()}
|
|
235
|
+
</button>
|
|
236
|
+
);
|
|
237
|
+
})}
|
|
238
|
+
</div>
|
|
239
|
+
)}
|
|
240
|
+
<div className="px-2 py-2 flex justify-between mb-8">
|
|
241
|
+
<button
|
|
242
|
+
onClick={goToToday}
|
|
243
|
+
className="text-blue-500 hover:text-blue-600 transition duration-150 ease-in-out"
|
|
244
|
+
>
|
|
245
|
+
Today
|
|
246
|
+
</button>
|
|
247
|
+
<button
|
|
248
|
+
onClick={toggleDatepicker}
|
|
249
|
+
className="text-gray-500 hover:text-gray-600 transition duration-150 ease-in-out"
|
|
250
|
+
>
|
|
251
|
+
Close
|
|
252
|
+
</button>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
)}
|
|
256
|
+
</div>
|
|
257
|
+
);
|
|
258
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Grampro Business Services and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { twMerge } from "tailwind-merge";
|
|
10
|
+
import type { DialogProps } from "./types";
|
|
11
|
+
|
|
12
|
+
export const Dialog = ({
|
|
13
|
+
showDialog = false,
|
|
14
|
+
dialogMessage = "Are you sure?",
|
|
15
|
+
dialogActionOne = "OK",
|
|
16
|
+
dialogActionTwo = "Cancel",
|
|
17
|
+
onDialogActionOneClick,
|
|
18
|
+
onDialogActionTwoClick,
|
|
19
|
+
dialogClass = "bg-white p-4 rounded-lg shadow-lg w-96",
|
|
20
|
+
dialogContentClass = "text-lg",
|
|
21
|
+
dialogActionOneStyle = "bg-black hover:bg-black text-white font-bold px-2 py-1 rounded mt-4",
|
|
22
|
+
dialogActionTwoStyle = "border text-black font-bold px-2 py-1 rounded mt-4 ml-2",
|
|
23
|
+
}: DialogProps) => {
|
|
24
|
+
if (!showDialog) return null;
|
|
25
|
+
return (
|
|
26
|
+
<div
|
|
27
|
+
className={twMerge(
|
|
28
|
+
dialogClass,
|
|
29
|
+
"fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
|
30
|
+
)}
|
|
31
|
+
role="dialog"
|
|
32
|
+
aria-modal="true"
|
|
33
|
+
aria-labelledby="dialog-title"
|
|
34
|
+
>
|
|
35
|
+
<p className={dialogContentClass}>{dialogMessage}</p>
|
|
36
|
+
<button className={dialogActionOneStyle} onClick={onDialogActionOneClick}>
|
|
37
|
+
{dialogActionOne}
|
|
38
|
+
</button>
|
|
39
|
+
<button className={dialogActionTwoStyle} onClick={onDialogActionTwoClick}>
|
|
40
|
+
{dialogActionTwo}
|
|
41
|
+
</button>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type DialogProps = {
|
|
2
|
+
showDialog?: boolean;
|
|
3
|
+
dialogMessage?: string;
|
|
4
|
+
dialogActionOne?: string;
|
|
5
|
+
dialogActionTwo?: string;
|
|
6
|
+
onDialogActionOneClick?: () => void;
|
|
7
|
+
onDialogActionTwoClick?: () => void;
|
|
8
|
+
dialogClass?: string;
|
|
9
|
+
dialogContentClass?: string;
|
|
10
|
+
dialogActionOneStyle?: string;
|
|
11
|
+
dialogActionTwoStyle?: string;
|
|
12
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
4
|
+
export default function FilterPopup({ show, columnHeader, filterAction }: any) {
|
|
5
|
+
const [filterValue, setFilterValue] = useState("");
|
|
6
|
+
const [filterType, setFilterType] = useState("contains");
|
|
7
|
+
const [isFilterActive, setIsFilterActive] = useState(false);
|
|
8
|
+
|
|
9
|
+
const handleFilterInput = (e: any) => {
|
|
10
|
+
const filterKeyword = e.target.value;
|
|
11
|
+
setFilterValue(filterKeyword);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const onCancel = () => {
|
|
15
|
+
filterAction({ type: "cancel" });
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const applyFilter = () => {
|
|
19
|
+
filterAction({
|
|
20
|
+
type: "applyFilter",
|
|
21
|
+
filterValue,
|
|
22
|
+
filterType,
|
|
23
|
+
columnHeader,
|
|
24
|
+
});
|
|
25
|
+
show = false;
|
|
26
|
+
setIsFilterActive(true);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const clearFilter = () => {
|
|
30
|
+
setIsFilterActive(false);
|
|
31
|
+
filterAction({ type: "clearFilter", columnHeader });
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
show && (
|
|
36
|
+
<div
|
|
37
|
+
className="absolute bg-gray-100 p-2 border z-50 mt-48 flex items-end flex-col shadow-lg gap-2 rounded-md dark:bg-gray-700"
|
|
38
|
+
role="dialog"
|
|
39
|
+
>
|
|
40
|
+
<select
|
|
41
|
+
name={`${columnHeader}-filter`}
|
|
42
|
+
id={`${columnHeader}-filter-id`}
|
|
43
|
+
className="w-full p-2 rounded-lg dark:bg-gray-600"
|
|
44
|
+
value={filterType}
|
|
45
|
+
onChange={(e: any) => {
|
|
46
|
+
setFilterType(e.target.value);
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
<option value="contains">Contains</option>
|
|
50
|
+
<option value="starts_with">Starts With</option>
|
|
51
|
+
<option value="ends_with">Ends With</option>
|
|
52
|
+
</select>
|
|
53
|
+
<input
|
|
54
|
+
type="text"
|
|
55
|
+
placeholder="Enter filter value"
|
|
56
|
+
className="rounded-lg p-2 text-sm dark:bg-gray-600"
|
|
57
|
+
value={filterValue}
|
|
58
|
+
onInput={handleFilterInput}
|
|
59
|
+
/>
|
|
60
|
+
<div className="mt-2">
|
|
61
|
+
<button
|
|
62
|
+
className="text-xs bg-red-600 text-white p-1 rounded-lg hover:bg-red-600"
|
|
63
|
+
onClick={onCancel}
|
|
64
|
+
>
|
|
65
|
+
Cancel
|
|
66
|
+
</button>
|
|
67
|
+
{isFilterActive && (
|
|
68
|
+
<button
|
|
69
|
+
className="text-xs bg-black text-white p-1 rounded-lg dark:bg-white dark:text-white"
|
|
70
|
+
onClick={clearFilter}
|
|
71
|
+
>
|
|
72
|
+
Clear Filter
|
|
73
|
+
</button>
|
|
74
|
+
)}
|
|
75
|
+
<button
|
|
76
|
+
className="text-xs bg-black text-white p-1 rounded-lg dark:bg-white dark:text-black"
|
|
77
|
+
onClick={applyFilter}
|
|
78
|
+
>
|
|
79
|
+
Apply Filter
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
}
|