@isma91/react-scheduler 4.0.0
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/.github/workflows/publish.yml +29 -0
- package/.github/workflows/tests.yml +35 -0
- package/.gitignore +32 -0
- package/.husky/pre-commit +2 -0
- package/.prettierignore +1 -0
- package/.prettierrc.json +7 -0
- package/.yarnrc.yml +1 -0
- package/LICENSE +24 -0
- package/README.md +172 -0
- package/dist/LICENSE +24 -0
- package/dist/README.md +172 -0
- package/dist/SchedulerComponent.d.ts +3 -0
- package/dist/components/common/Cell.d.ts +13 -0
- package/dist/components/common/LocaleArrow.d.ts +8 -0
- package/dist/components/common/ResourceHeader.d.ts +6 -0
- package/dist/components/common/Tabs.d.ts +16 -0
- package/dist/components/common/TodayTypo.d.ts +8 -0
- package/dist/components/common/WithResources.d.ts +6 -0
- package/dist/components/events/Actions.d.ts +8 -0
- package/dist/components/events/AgendaEventsList.d.ts +7 -0
- package/dist/components/events/CurrentTimeBar.d.ts +11 -0
- package/dist/components/events/EmptyAgenda.d.ts +2 -0
- package/dist/components/events/EventItem.d.ts +10 -0
- package/dist/components/events/EventItemPopover.d.ts +9 -0
- package/dist/components/events/MonthEvents.d.ts +13 -0
- package/dist/components/events/TodayEvents.d.ts +16 -0
- package/dist/components/hoc/DateProvider.d.ts +5 -0
- package/dist/components/inputs/DatePicker.d.ts +14 -0
- package/dist/components/inputs/Input.d.ts +19 -0
- package/dist/components/inputs/SelectInput.d.ts +22 -0
- package/dist/components/month/MonthTable.d.ts +8 -0
- package/dist/components/nav/DayDateBtn.d.ts +6 -0
- package/dist/components/nav/MonthDateBtn.d.ts +6 -0
- package/dist/components/nav/Navigation.d.ts +3 -0
- package/dist/components/nav/WeekDateBtn.d.ts +8 -0
- package/dist/components/week/WeekTable.d.ts +11 -0
- package/dist/helpers/constants.d.ts +4 -0
- package/dist/helpers/generals.d.ts +78 -0
- package/dist/hooks/useArrowDisable.d.ts +5 -0
- package/dist/hooks/useCellAttributes.d.ts +18 -0
- package/dist/hooks/useDragAttributes.d.ts +10 -0
- package/dist/hooks/useEventPermissions.d.ts +7 -0
- package/dist/hooks/useStore.d.ts +2 -0
- package/dist/hooks/useSyncScroll.d.ts +8 -0
- package/dist/hooks/useWindowResize.d.ts +4 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2853 -0
- package/dist/package.json +65 -0
- package/dist/positionManger/context.d.ts +14 -0
- package/dist/positionManger/provider.d.ts +5 -0
- package/dist/positionManger/usePosition.d.ts +4 -0
- package/dist/store/context.d.ts +2 -0
- package/dist/store/default.d.ts +245 -0
- package/dist/store/provider.d.ts +7 -0
- package/dist/store/types.d.ts +27 -0
- package/dist/styles/styles.d.ts +30 -0
- package/dist/types.d.ts +372 -0
- package/dist/views/Day.d.ts +2 -0
- package/dist/views/DayAgenda.d.ts +7 -0
- package/dist/views/Editor.d.ts +11 -0
- package/dist/views/Month.d.ts +2 -0
- package/dist/views/MonthAgenda.d.ts +7 -0
- package/dist/views/Week.d.ts +2 -0
- package/dist/views/WeekAgenda.d.ts +8 -0
- package/eslint.config.js +79 -0
- package/index.html +41 -0
- package/jest.config.ts +194 -0
- package/package.json +137 -0
- package/public/favicon.ico +0 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/scripts/post-pack.js +34 -0
- package/src/App.tsx +25 -0
- package/src/Page1.tsx +67 -0
- package/src/events.tsx +227 -0
- package/src/index.tsx +21 -0
- package/src/lib/SchedulerComponent.tsx +78 -0
- package/src/lib/__tests__/index.test.tsx +24 -0
- package/src/lib/components/common/Cell.tsx +52 -0
- package/src/lib/components/common/LocaleArrow.tsx +38 -0
- package/src/lib/components/common/ResourceHeader.tsx +73 -0
- package/src/lib/components/common/Tabs.tsx +119 -0
- package/src/lib/components/common/TodayTypo.tsx +44 -0
- package/src/lib/components/common/WithResources.tsx +98 -0
- package/src/lib/components/events/Actions.tsx +65 -0
- package/src/lib/components/events/AgendaEventsList.tsx +115 -0
- package/src/lib/components/events/CurrentTimeBar.tsx +59 -0
- package/src/lib/components/events/EmptyAgenda.tsx +27 -0
- package/src/lib/components/events/EventItem.tsx +180 -0
- package/src/lib/components/events/EventItemPopover.tsx +179 -0
- package/src/lib/components/events/MonthEvents.tsx +141 -0
- package/src/lib/components/events/TodayEvents.tsx +99 -0
- package/src/lib/components/hoc/DateProvider.tsx +19 -0
- package/src/lib/components/inputs/DatePicker.tsx +95 -0
- package/src/lib/components/inputs/Input.tsx +113 -0
- package/src/lib/components/inputs/SelectInput.tsx +164 -0
- package/src/lib/components/month/MonthTable.tsx +207 -0
- package/src/lib/components/nav/DayDateBtn.tsx +77 -0
- package/src/lib/components/nav/MonthDateBtn.tsx +80 -0
- package/src/lib/components/nav/Navigation.tsx +201 -0
- package/src/lib/components/nav/WeekDateBtn.tsx +89 -0
- package/src/lib/components/week/WeekTable.tsx +229 -0
- package/src/lib/helpers/constants.ts +4 -0
- package/src/lib/helpers/generals.tsx +354 -0
- package/src/lib/hooks/useArrowDisable.ts +26 -0
- package/src/lib/hooks/useCellAttributes.ts +67 -0
- package/src/lib/hooks/useDragAttributes.ts +31 -0
- package/src/lib/hooks/useEventPermissions.ts +42 -0
- package/src/lib/hooks/useStore.ts +8 -0
- package/src/lib/hooks/useSyncScroll.ts +31 -0
- package/src/lib/hooks/useWindowResize.ts +37 -0
- package/src/lib/index.tsx +14 -0
- package/src/lib/positionManger/context.ts +14 -0
- package/src/lib/positionManger/provider.tsx +113 -0
- package/src/lib/positionManger/usePosition.ts +8 -0
- package/src/lib/store/context.ts +5 -0
- package/src/lib/store/default.ts +157 -0
- package/src/lib/store/provider.tsx +211 -0
- package/src/lib/store/types.ts +33 -0
- package/src/lib/styles/styles.ts +256 -0
- package/src/lib/types.ts +423 -0
- package/src/lib/views/Day.tsx +265 -0
- package/src/lib/views/DayAgenda.tsx +57 -0
- package/src/lib/views/Editor.tsx +258 -0
- package/src/lib/views/Month.tsx +82 -0
- package/src/lib/views/MonthAgenda.tsx +84 -0
- package/src/lib/views/Week.tsx +92 -0
- package/src/lib/views/WeekAgenda.tsx +81 -0
- package/src/vite-env.d.ts +3 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +27 -0
- package/vite.config.js +40 -0
package/src/events.tsx
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { datetime, RRule } from "rrule";
|
|
2
|
+
import { ProcessedEvent } from "./lib/types";
|
|
3
|
+
|
|
4
|
+
export const EVENTS: ProcessedEvent[] = [
|
|
5
|
+
{
|
|
6
|
+
event_id: 1,
|
|
7
|
+
title: "Event 1 (Disabled)",
|
|
8
|
+
subtitle: "This event is disabled",
|
|
9
|
+
start: new Date(new Date(new Date().setHours(9)).setMinutes(0)),
|
|
10
|
+
end: new Date(new Date(new Date().setHours(10)).setMinutes(0)),
|
|
11
|
+
disabled: true,
|
|
12
|
+
admin_id: [1, 2, 3, 4],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
event_id: 2,
|
|
16
|
+
title: "Event 2",
|
|
17
|
+
subtitle: "This event is draggable",
|
|
18
|
+
start: new Date(new Date(new Date().setHours(10)).setMinutes(0)),
|
|
19
|
+
end: new Date(new Date(new Date().setHours(12)).setMinutes(0)),
|
|
20
|
+
admin_id: 2,
|
|
21
|
+
color: "#50b500",
|
|
22
|
+
agendaAvatar: "E",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
event_id: 3,
|
|
26
|
+
title: "Event 3",
|
|
27
|
+
subtitle: "This event is not editable",
|
|
28
|
+
start: new Date(new Date(new Date().setHours(11)).setMinutes(0)),
|
|
29
|
+
end: new Date(new Date(new Date().setHours(12)).setMinutes(0)),
|
|
30
|
+
admin_id: 1,
|
|
31
|
+
editable: false,
|
|
32
|
+
deletable: false,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
event_id: 4,
|
|
36
|
+
title: "Event 4",
|
|
37
|
+
start: new Date(
|
|
38
|
+
new Date(new Date(new Date().setHours(9)).setMinutes(30)).setDate(new Date().getDate() - 2)
|
|
39
|
+
),
|
|
40
|
+
end: new Date(
|
|
41
|
+
new Date(new Date(new Date().setHours(11)).setMinutes(0)).setDate(new Date().getDate() - 2)
|
|
42
|
+
),
|
|
43
|
+
admin_id: [2, 3],
|
|
44
|
+
color: "#900000",
|
|
45
|
+
allDay: true,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
event_id: 5,
|
|
49
|
+
title: "Event 5",
|
|
50
|
+
subtitle: "This event is editable",
|
|
51
|
+
start: new Date(
|
|
52
|
+
new Date(new Date(new Date().setHours(10)).setMinutes(30)).setDate(new Date().getDate() - 2)
|
|
53
|
+
),
|
|
54
|
+
end: new Date(
|
|
55
|
+
new Date(new Date(new Date().setHours(14)).setMinutes(0)).setDate(new Date().getDate() - 2)
|
|
56
|
+
),
|
|
57
|
+
admin_id: 2,
|
|
58
|
+
editable: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
event_id: 6,
|
|
62
|
+
title: "Event 6",
|
|
63
|
+
subtitle: "This event is all day",
|
|
64
|
+
start: new Date(
|
|
65
|
+
new Date(new Date(new Date().setHours(20)).setMinutes(30)).setDate(new Date().getDate() - 3)
|
|
66
|
+
),
|
|
67
|
+
end: new Date(new Date(new Date().setHours(23)).setMinutes(0)),
|
|
68
|
+
admin_id: 2,
|
|
69
|
+
allDay: true,
|
|
70
|
+
sx: { color: "purple" },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
event_id: 7,
|
|
74
|
+
title: "Event 7 (Not draggable)",
|
|
75
|
+
subtitle: "This event is not draggable",
|
|
76
|
+
start: new Date(
|
|
77
|
+
new Date(new Date(new Date().setHours(10)).setMinutes(30)).setDate(new Date().getDate() - 3)
|
|
78
|
+
),
|
|
79
|
+
end: new Date(
|
|
80
|
+
new Date(new Date(new Date().setHours(14)).setMinutes(30)).setDate(new Date().getDate() - 3)
|
|
81
|
+
),
|
|
82
|
+
admin_id: 1,
|
|
83
|
+
draggable: false,
|
|
84
|
+
color: "#8000cc",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
event_id: 8,
|
|
88
|
+
title: "Event 8",
|
|
89
|
+
subtitle: "This event has a custom color",
|
|
90
|
+
start: new Date(
|
|
91
|
+
new Date(new Date(new Date().setHours(10)).setMinutes(30)).setDate(new Date().getDate() + 30)
|
|
92
|
+
),
|
|
93
|
+
end: new Date(
|
|
94
|
+
new Date(new Date(new Date().setHours(14)).setMinutes(30)).setDate(new Date().getDate() + 30)
|
|
95
|
+
),
|
|
96
|
+
admin_id: 1,
|
|
97
|
+
color: "#8000cc",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
event_id: 9,
|
|
101
|
+
title: "Event 9",
|
|
102
|
+
subtitle: `This event is a recurring weekly until ${new Date(
|
|
103
|
+
new Date().setMonth(
|
|
104
|
+
new Date(
|
|
105
|
+
new Date(new Date(new Date().setHours(11)).setMinutes(0)).setDate(
|
|
106
|
+
new Date().getDate() + 1
|
|
107
|
+
)
|
|
108
|
+
).getMonth() + 1
|
|
109
|
+
)
|
|
110
|
+
).toDateString()}`,
|
|
111
|
+
start: new Date(
|
|
112
|
+
new Date(new Date(new Date().setHours(10)).setMinutes(0)).setDate(new Date().getDate() + 1)
|
|
113
|
+
),
|
|
114
|
+
end: new Date(
|
|
115
|
+
new Date(new Date(new Date().setHours(11)).setMinutes(0)).setDate(new Date().getDate() + 1)
|
|
116
|
+
),
|
|
117
|
+
recurring: new RRule({
|
|
118
|
+
freq: RRule.WEEKLY,
|
|
119
|
+
dtstart: convertDateToRRuleDate(
|
|
120
|
+
new Date(
|
|
121
|
+
new Date(new Date(new Date().setHours(10)).setMinutes(0)).setDate(
|
|
122
|
+
new Date().getDate() - 20
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
),
|
|
126
|
+
until: new Date(
|
|
127
|
+
new Date().setMonth(
|
|
128
|
+
new Date(
|
|
129
|
+
new Date(new Date(new Date().setHours(11)).setMinutes(0)).setDate(
|
|
130
|
+
new Date().getDate() + 1
|
|
131
|
+
)
|
|
132
|
+
).getMonth() + 1
|
|
133
|
+
)
|
|
134
|
+
),
|
|
135
|
+
}),
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
event_id: 10,
|
|
139
|
+
title: "Event 10",
|
|
140
|
+
subtitle: "This event is a recurring hourly 3 times",
|
|
141
|
+
start: new Date(new Date(new Date().setHours(14)).setMinutes(15)),
|
|
142
|
+
end: new Date(new Date(new Date().setHours(14)).setMinutes(45)),
|
|
143
|
+
recurring: new RRule({
|
|
144
|
+
freq: RRule.HOURLY,
|
|
145
|
+
count: 3,
|
|
146
|
+
dtstart: convertDateToRRuleDate(new Date(new Date(new Date().setHours(14)).setMinutes(15))),
|
|
147
|
+
}),
|
|
148
|
+
color: "#dc4552",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
event_id: 11,
|
|
152
|
+
title: "Event 11",
|
|
153
|
+
start: new Date(
|
|
154
|
+
new Date(new Date(new Date().setHours(9)).setMinutes(0)).setDate(new Date().getDate() - 2)
|
|
155
|
+
),
|
|
156
|
+
end: new Date(
|
|
157
|
+
new Date(new Date(new Date().setHours(2)).setMinutes(0)).setDate(new Date().getDate() + 2)
|
|
158
|
+
),
|
|
159
|
+
color: "#079000ff",
|
|
160
|
+
allDay: false,
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
export const RESOURCES = [
|
|
165
|
+
{
|
|
166
|
+
admin_id: 1,
|
|
167
|
+
title: "One",
|
|
168
|
+
mobile: "555666777",
|
|
169
|
+
avatar: "",
|
|
170
|
+
color: "#ab2d2d",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
admin_id: 2,
|
|
174
|
+
title: "Two is very long name",
|
|
175
|
+
mobile: "555666777",
|
|
176
|
+
avatar: "https://picsum.photos/200/300",
|
|
177
|
+
color: "#58ab2d",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
admin_id: 3,
|
|
181
|
+
title: "Three",
|
|
182
|
+
mobile: "555666777",
|
|
183
|
+
avatar: "https://picsum.photos/200/300",
|
|
184
|
+
color: "#a001a2",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
admin_id: 4,
|
|
188
|
+
title: "Four",
|
|
189
|
+
mobile: "555666777",
|
|
190
|
+
avatar: "https://picsum.photos/200/300",
|
|
191
|
+
color: "#08c5bd",
|
|
192
|
+
},
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
export const generateRandomEvents = (total = 300) => {
|
|
196
|
+
const events = [];
|
|
197
|
+
for (let i = 0; i < total; i++) {
|
|
198
|
+
const day = Math.round(i % 15);
|
|
199
|
+
events.push({
|
|
200
|
+
event_id: Math.random(),
|
|
201
|
+
title: "Event " + (i + 1),
|
|
202
|
+
start: new Date(
|
|
203
|
+
new Date(new Date(new Date().setHours(10)).setMinutes(30)).setDate(
|
|
204
|
+
new Date().getDate() + day
|
|
205
|
+
)
|
|
206
|
+
),
|
|
207
|
+
end: new Date(
|
|
208
|
+
new Date(new Date(new Date().setHours(14)).setMinutes(0)).setDate(
|
|
209
|
+
new Date().getDate() + day
|
|
210
|
+
)
|
|
211
|
+
),
|
|
212
|
+
// allDay: Math.random() > 0.5,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return events;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
function convertDateToRRuleDate(date: Date) {
|
|
220
|
+
return datetime(
|
|
221
|
+
date.getFullYear(),
|
|
222
|
+
date.getMonth() + 1,
|
|
223
|
+
date.getDate(),
|
|
224
|
+
date.getHours(),
|
|
225
|
+
date.getMinutes()
|
|
226
|
+
);
|
|
227
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { CssBaseline, ThemeProvider, createTheme } from "@mui/material";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import App from "./App";
|
|
5
|
+
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
|
6
|
+
import Page1 from "./Page1";
|
|
7
|
+
|
|
8
|
+
const root = createRoot(document.getElementById("root") as HTMLElement);
|
|
9
|
+
root.render(
|
|
10
|
+
<React.StrictMode>
|
|
11
|
+
<ThemeProvider theme={createTheme()}>
|
|
12
|
+
<CssBaseline />
|
|
13
|
+
<BrowserRouter>
|
|
14
|
+
<Routes>
|
|
15
|
+
<Route path="/" element={<App />} />
|
|
16
|
+
<Route path="/1" element={<Page1 />} />
|
|
17
|
+
</Routes>
|
|
18
|
+
</BrowserRouter>
|
|
19
|
+
</ThemeProvider>
|
|
20
|
+
</React.StrictMode>
|
|
21
|
+
);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Week } from "./views/Week";
|
|
2
|
+
import { Navigation } from "./components/nav/Navigation";
|
|
3
|
+
import Editor from "./views/Editor";
|
|
4
|
+
import { CircularProgress, Typography } from "@mui/material";
|
|
5
|
+
import { Month } from "./views/Month";
|
|
6
|
+
import { Day } from "./views/Day";
|
|
7
|
+
import { Table, Wrapper } from "./styles/styles";
|
|
8
|
+
import { forwardRef, useMemo } from "react";
|
|
9
|
+
import useStore from "./hooks/useStore";
|
|
10
|
+
import { SchedulerRef } from "./types";
|
|
11
|
+
import { PositionProvider } from "./positionManger/provider";
|
|
12
|
+
|
|
13
|
+
const SchedulerComponent = forwardRef<SchedulerRef, unknown>(function SchedulerComponent(_, ref) {
|
|
14
|
+
const store = useStore();
|
|
15
|
+
const { view, dialog, loading, loadingComponent, resourceViewMode, resources, translations } =
|
|
16
|
+
store;
|
|
17
|
+
|
|
18
|
+
const Views = useMemo(() => {
|
|
19
|
+
switch (view) {
|
|
20
|
+
case "month":
|
|
21
|
+
return <Month />;
|
|
22
|
+
case "week":
|
|
23
|
+
return <Week />;
|
|
24
|
+
case "day":
|
|
25
|
+
return <Day />;
|
|
26
|
+
default:
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
}, [view]);
|
|
30
|
+
|
|
31
|
+
const LoadingComp = useMemo(() => {
|
|
32
|
+
return (
|
|
33
|
+
<div className="rs__table_loading">
|
|
34
|
+
{loadingComponent || (
|
|
35
|
+
<div className="rs__table_loading_internal">
|
|
36
|
+
<span>
|
|
37
|
+
<CircularProgress size={50} />
|
|
38
|
+
<Typography align="center">{translations.loading}</Typography>
|
|
39
|
+
</span>
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}, [loadingComponent, translations.loading]);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Wrapper
|
|
48
|
+
dialog={dialog ? 1 : 0}
|
|
49
|
+
data-testid="rs-wrapper"
|
|
50
|
+
ref={(el) => {
|
|
51
|
+
const calendarRef = ref as any;
|
|
52
|
+
if (calendarRef) {
|
|
53
|
+
calendarRef.current = {
|
|
54
|
+
el,
|
|
55
|
+
scheduler: store,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{loading ? LoadingComp : null}
|
|
61
|
+
<Navigation />
|
|
62
|
+
<Table
|
|
63
|
+
resource_count={resourceViewMode === "default" ? resources.length : 1}
|
|
64
|
+
// Temp resources/default `sticky` wontfix
|
|
65
|
+
sx={{
|
|
66
|
+
overflowX: resourceViewMode === "default" && resources.length > 1 ? "auto" : undefined,
|
|
67
|
+
flexDirection: resourceViewMode === "vertical" ? "column" : undefined,
|
|
68
|
+
}}
|
|
69
|
+
data-testid="grid"
|
|
70
|
+
>
|
|
71
|
+
<PositionProvider>{Views}</PositionProvider>
|
|
72
|
+
</Table>
|
|
73
|
+
{dialog && <Editor />}
|
|
74
|
+
</Wrapper>
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export default SchedulerComponent;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { Scheduler } from "..";
|
|
3
|
+
import "@testing-library/jest-dom";
|
|
4
|
+
|
|
5
|
+
describe("Render scheduler", () => {
|
|
6
|
+
it("Default render", () => {
|
|
7
|
+
render(<Scheduler />);
|
|
8
|
+
|
|
9
|
+
// Scheduler component
|
|
10
|
+
const scheduler = screen.getByTestId("rs-wrapper");
|
|
11
|
+
expect(scheduler).toBeInTheDocument();
|
|
12
|
+
|
|
13
|
+
// Navigators
|
|
14
|
+
const viewNavigator = screen.getByTestId("view-navigator");
|
|
15
|
+
expect(viewNavigator).toBeInTheDocument();
|
|
16
|
+
|
|
17
|
+
const dateNavigator = screen.getByTestId("date-navigator");
|
|
18
|
+
expect(dateNavigator).toBeInTheDocument();
|
|
19
|
+
expect(dateNavigator.getElementsByTagName("button").length).toEqual(3);
|
|
20
|
+
|
|
21
|
+
const grid = screen.getByTestId("grid");
|
|
22
|
+
expect(grid).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Button } from "@mui/material";
|
|
2
|
+
import { useCellAttributes } from "../../hooks/useCellAttributes";
|
|
3
|
+
import { CellRenderedProps } from "../../types";
|
|
4
|
+
|
|
5
|
+
interface CellProps {
|
|
6
|
+
day: Date;
|
|
7
|
+
start: Date;
|
|
8
|
+
height: number;
|
|
9
|
+
end: Date;
|
|
10
|
+
resourceKey: string;
|
|
11
|
+
resourceVal: string | number;
|
|
12
|
+
cellRenderer?(props: CellRenderedProps): React.ReactNode;
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const Cell = ({
|
|
17
|
+
day,
|
|
18
|
+
start,
|
|
19
|
+
end,
|
|
20
|
+
resourceKey,
|
|
21
|
+
resourceVal,
|
|
22
|
+
cellRenderer,
|
|
23
|
+
height,
|
|
24
|
+
children,
|
|
25
|
+
}: CellProps) => {
|
|
26
|
+
const props = useCellAttributes({ start, end, resourceKey, resourceVal });
|
|
27
|
+
|
|
28
|
+
if (cellRenderer) {
|
|
29
|
+
return cellRenderer({
|
|
30
|
+
day,
|
|
31
|
+
start,
|
|
32
|
+
end,
|
|
33
|
+
height,
|
|
34
|
+
...props,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Button
|
|
40
|
+
fullWidth
|
|
41
|
+
aria-label={`${start.toLocaleString("en", {
|
|
42
|
+
dateStyle: "full",
|
|
43
|
+
timeStyle: "long",
|
|
44
|
+
})} - ${end.toLocaleString("en", { dateStyle: "full", timeStyle: "long" })}`}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
{children}
|
|
48
|
+
</Button>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default Cell;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import NavigateBeforeRoundedIcon from "@mui/icons-material/NavigateBeforeRounded";
|
|
2
|
+
import NavigateNextRoundedIcon from "@mui/icons-material/NavigateNextRounded";
|
|
3
|
+
import { IconButton, IconButtonProps } from "@mui/material";
|
|
4
|
+
import { MouseEvent } from "react";
|
|
5
|
+
import useStore from "../../hooks/useStore";
|
|
6
|
+
|
|
7
|
+
interface LocaleArrowProps extends Omit<IconButtonProps, "type"> {
|
|
8
|
+
type: "prev" | "next";
|
|
9
|
+
onClick?(e?: MouseEvent): void;
|
|
10
|
+
}
|
|
11
|
+
const LocaleArrow = ({ type, onClick, ...props }: LocaleArrowProps) => {
|
|
12
|
+
const { direction } = useStore();
|
|
13
|
+
|
|
14
|
+
let Arrow = NavigateNextRoundedIcon;
|
|
15
|
+
if (type === "prev") {
|
|
16
|
+
Arrow = direction === "rtl" ? NavigateNextRoundedIcon : NavigateBeforeRoundedIcon;
|
|
17
|
+
} else if (type === "next") {
|
|
18
|
+
Arrow = direction === "rtl" ? NavigateBeforeRoundedIcon : NavigateNextRoundedIcon;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<IconButton
|
|
23
|
+
style={{ padding: 2 }}
|
|
24
|
+
onClick={onClick}
|
|
25
|
+
onDragOver={(e) => {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
if (onClick) {
|
|
28
|
+
onClick();
|
|
29
|
+
}
|
|
30
|
+
}}
|
|
31
|
+
{...props}
|
|
32
|
+
>
|
|
33
|
+
<Arrow />
|
|
34
|
+
</IconButton>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { LocaleArrow };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Avatar,
|
|
3
|
+
ListItem,
|
|
4
|
+
ListItemAvatar,
|
|
5
|
+
ListItemText,
|
|
6
|
+
Typography,
|
|
7
|
+
useTheme,
|
|
8
|
+
} from "@mui/material";
|
|
9
|
+
import { DefaultResource } from "../../types";
|
|
10
|
+
import useStore from "../../hooks/useStore";
|
|
11
|
+
|
|
12
|
+
interface ResourceHeaderProps {
|
|
13
|
+
resource: DefaultResource;
|
|
14
|
+
}
|
|
15
|
+
const ResourceHeader = ({ resource }: ResourceHeaderProps) => {
|
|
16
|
+
const { resourceHeaderComponent, resourceFields, direction, resourceViewMode } = useStore();
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
|
|
19
|
+
const text = resource[resourceFields.textField];
|
|
20
|
+
const subtext = resource[resourceFields.subTextField || ""];
|
|
21
|
+
const avatar = resource[resourceFields.avatarField || ""];
|
|
22
|
+
const color = resource[resourceFields.colorField || ""];
|
|
23
|
+
|
|
24
|
+
if (resourceHeaderComponent instanceof Function) {
|
|
25
|
+
return resourceHeaderComponent(resource);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<ListItem
|
|
30
|
+
sx={{
|
|
31
|
+
padding: "2px 10px",
|
|
32
|
+
textAlign: direction === "rtl" ? "right" : "left",
|
|
33
|
+
...(resourceViewMode === "tabs"
|
|
34
|
+
? {}
|
|
35
|
+
: resourceViewMode === "vertical"
|
|
36
|
+
? {
|
|
37
|
+
display: "block",
|
|
38
|
+
textAlign: "center",
|
|
39
|
+
position: "sticky",
|
|
40
|
+
top: 4,
|
|
41
|
+
}
|
|
42
|
+
: {
|
|
43
|
+
borderColor: theme.palette.grey[300],
|
|
44
|
+
borderStyle: "solid",
|
|
45
|
+
borderWidth: 1,
|
|
46
|
+
}),
|
|
47
|
+
}}
|
|
48
|
+
component="div"
|
|
49
|
+
>
|
|
50
|
+
<ListItemAvatar>
|
|
51
|
+
<Avatar sx={{ background: color, margin: "auto" }} alt={text} src={avatar} />
|
|
52
|
+
</ListItemAvatar>
|
|
53
|
+
<ListItemText
|
|
54
|
+
primary={
|
|
55
|
+
<Typography variant="body2" noWrap={resourceViewMode !== "vertical"}>
|
|
56
|
+
{text}
|
|
57
|
+
</Typography>
|
|
58
|
+
}
|
|
59
|
+
secondary={
|
|
60
|
+
<Typography
|
|
61
|
+
variant="caption"
|
|
62
|
+
color="textSecondary"
|
|
63
|
+
noWrap={resourceViewMode !== "vertical"}
|
|
64
|
+
>
|
|
65
|
+
{subtext}
|
|
66
|
+
</Typography>
|
|
67
|
+
}
|
|
68
|
+
/>
|
|
69
|
+
</ListItem>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export { ResourceHeader };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { CSSProperties } from "react";
|
|
2
|
+
import { Tabs, Tab } from "@mui/material";
|
|
3
|
+
import { styled } from "@mui/material/styles";
|
|
4
|
+
import { Theme } from "@mui/system";
|
|
5
|
+
|
|
6
|
+
interface TabPanelProps {
|
|
7
|
+
value: string | number;
|
|
8
|
+
index: string | number;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
function TabPanel(props: TabPanelProps) {
|
|
12
|
+
const { children, value, index } = props;
|
|
13
|
+
return value === index ? <>{children}</> : <></>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function a11yProps(index: string | number) {
|
|
17
|
+
return {
|
|
18
|
+
id: `scrollable-auto-tab-${index}`,
|
|
19
|
+
"aria-controls": `scrollable-auto-tabpanel-${index}`,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const StyledTaps = styled("div")(({ theme }: { theme: Theme }) => ({
|
|
24
|
+
flexGrow: 1,
|
|
25
|
+
width: "100%",
|
|
26
|
+
backgroundColor: theme.palette.background.paper,
|
|
27
|
+
alignSelf: "center",
|
|
28
|
+
"& .tabs": {
|
|
29
|
+
borderColor: theme.palette.grey[300],
|
|
30
|
+
borderStyle: "solid",
|
|
31
|
+
borderWidth: 1,
|
|
32
|
+
"& button.MuiTab-root": {
|
|
33
|
+
borderColor: theme.palette.grey[300],
|
|
34
|
+
borderRightStyle: "solid",
|
|
35
|
+
borderWidth: 1,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
"& .primary": {
|
|
39
|
+
background: theme.palette.primary.main,
|
|
40
|
+
},
|
|
41
|
+
"& .secondary": {
|
|
42
|
+
background: theme.palette.secondary.main,
|
|
43
|
+
},
|
|
44
|
+
"& .error": {
|
|
45
|
+
background: theme.palette.error.main,
|
|
46
|
+
},
|
|
47
|
+
"& .info": {
|
|
48
|
+
background: theme.palette.info.dark,
|
|
49
|
+
},
|
|
50
|
+
"& .text_primary": {
|
|
51
|
+
color: theme.palette.primary.main,
|
|
52
|
+
},
|
|
53
|
+
"& .text_secondary": {
|
|
54
|
+
color: theme.palette.secondary.main,
|
|
55
|
+
},
|
|
56
|
+
"& .text_error": {
|
|
57
|
+
color: theme.palette.error.main,
|
|
58
|
+
},
|
|
59
|
+
"& .text_info": {
|
|
60
|
+
color: theme.palette.info.dark,
|
|
61
|
+
},
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
export type ButtonTabProps = {
|
|
65
|
+
id: string | number;
|
|
66
|
+
label: string | React.ReactNode;
|
|
67
|
+
component: React.ReactNode;
|
|
68
|
+
};
|
|
69
|
+
interface ButtonTabsProps {
|
|
70
|
+
tabs: ButtonTabProps[];
|
|
71
|
+
tab: string | number;
|
|
72
|
+
setTab(tab: string | number): void;
|
|
73
|
+
variant?: "scrollable" | "standard" | "fullWidth";
|
|
74
|
+
indicator?: "primary" | "secondary" | "info" | "error";
|
|
75
|
+
style?: CSSProperties;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const ButtonTabs = ({
|
|
79
|
+
tabs,
|
|
80
|
+
variant = "scrollable",
|
|
81
|
+
tab,
|
|
82
|
+
setTab,
|
|
83
|
+
indicator = "primary",
|
|
84
|
+
style,
|
|
85
|
+
}: ButtonTabsProps) => {
|
|
86
|
+
return (
|
|
87
|
+
<StyledTaps style={style}>
|
|
88
|
+
<Tabs
|
|
89
|
+
value={tab}
|
|
90
|
+
variant={variant}
|
|
91
|
+
scrollButtons
|
|
92
|
+
className="tabs"
|
|
93
|
+
classes={{ indicator: indicator }}
|
|
94
|
+
>
|
|
95
|
+
{tabs.map((tab: ButtonTabProps, i: number) => (
|
|
96
|
+
<Tab
|
|
97
|
+
key={tab.id || i}
|
|
98
|
+
label={tab.label}
|
|
99
|
+
sx={{ flex: 1, flexBasis: 200, flexShrink: 0 }}
|
|
100
|
+
value={tab.id}
|
|
101
|
+
{...a11yProps(tab.id)}
|
|
102
|
+
onClick={() => setTab(tab.id)}
|
|
103
|
+
onDragEnter={() => setTab(tab.id)}
|
|
104
|
+
/>
|
|
105
|
+
))}
|
|
106
|
+
</Tabs>
|
|
107
|
+
{tabs.map(
|
|
108
|
+
(t: ButtonTabProps, i: number) =>
|
|
109
|
+
t.component && (
|
|
110
|
+
<TabPanel key={i} value={tab} index={t.id}>
|
|
111
|
+
{t.component}
|
|
112
|
+
</TabPanel>
|
|
113
|
+
)
|
|
114
|
+
)}
|
|
115
|
+
</StyledTaps>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export { ButtonTabs };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Typography } from "@mui/material";
|
|
2
|
+
import { format, Locale } from "date-fns";
|
|
3
|
+
import { isTimeZonedToday } from "../../helpers/generals";
|
|
4
|
+
import useStore from "../../hooks/useStore";
|
|
5
|
+
|
|
6
|
+
interface TodayTypoProps {
|
|
7
|
+
date: Date;
|
|
8
|
+
onClick?(day: Date): void;
|
|
9
|
+
locale: Locale;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const TodayTypo = ({ date, onClick, locale }: TodayTypoProps) => {
|
|
13
|
+
const { timeZone } = useStore();
|
|
14
|
+
const today = isTimeZonedToday({ dateLeft: date, timeZone });
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div>
|
|
18
|
+
<Typography
|
|
19
|
+
style={{
|
|
20
|
+
fontWeight: today ? "bold" : "inherit",
|
|
21
|
+
}}
|
|
22
|
+
color={today ? "primary" : "inherit"}
|
|
23
|
+
className={onClick ? "rs__hover__op" : ""}
|
|
24
|
+
onClick={(e) => {
|
|
25
|
+
e.stopPropagation();
|
|
26
|
+
if (onClick) onClick(date);
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
{format(date, "dd", { locale })}
|
|
30
|
+
</Typography>
|
|
31
|
+
<Typography
|
|
32
|
+
color={today ? "primary" : "inherit"}
|
|
33
|
+
style={{
|
|
34
|
+
fontWeight: today ? "bold" : "inherit",
|
|
35
|
+
fontSize: 11,
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
{format(date, "eee", { locale })}
|
|
39
|
+
</Typography>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default TodayTypo;
|