@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.
Files changed (134) hide show
  1. package/.github/workflows/publish.yml +29 -0
  2. package/.github/workflows/tests.yml +35 -0
  3. package/.gitignore +32 -0
  4. package/.husky/pre-commit +2 -0
  5. package/.prettierignore +1 -0
  6. package/.prettierrc.json +7 -0
  7. package/.yarnrc.yml +1 -0
  8. package/LICENSE +24 -0
  9. package/README.md +172 -0
  10. package/dist/LICENSE +24 -0
  11. package/dist/README.md +172 -0
  12. package/dist/SchedulerComponent.d.ts +3 -0
  13. package/dist/components/common/Cell.d.ts +13 -0
  14. package/dist/components/common/LocaleArrow.d.ts +8 -0
  15. package/dist/components/common/ResourceHeader.d.ts +6 -0
  16. package/dist/components/common/Tabs.d.ts +16 -0
  17. package/dist/components/common/TodayTypo.d.ts +8 -0
  18. package/dist/components/common/WithResources.d.ts +6 -0
  19. package/dist/components/events/Actions.d.ts +8 -0
  20. package/dist/components/events/AgendaEventsList.d.ts +7 -0
  21. package/dist/components/events/CurrentTimeBar.d.ts +11 -0
  22. package/dist/components/events/EmptyAgenda.d.ts +2 -0
  23. package/dist/components/events/EventItem.d.ts +10 -0
  24. package/dist/components/events/EventItemPopover.d.ts +9 -0
  25. package/dist/components/events/MonthEvents.d.ts +13 -0
  26. package/dist/components/events/TodayEvents.d.ts +16 -0
  27. package/dist/components/hoc/DateProvider.d.ts +5 -0
  28. package/dist/components/inputs/DatePicker.d.ts +14 -0
  29. package/dist/components/inputs/Input.d.ts +19 -0
  30. package/dist/components/inputs/SelectInput.d.ts +22 -0
  31. package/dist/components/month/MonthTable.d.ts +8 -0
  32. package/dist/components/nav/DayDateBtn.d.ts +6 -0
  33. package/dist/components/nav/MonthDateBtn.d.ts +6 -0
  34. package/dist/components/nav/Navigation.d.ts +3 -0
  35. package/dist/components/nav/WeekDateBtn.d.ts +8 -0
  36. package/dist/components/week/WeekTable.d.ts +11 -0
  37. package/dist/helpers/constants.d.ts +4 -0
  38. package/dist/helpers/generals.d.ts +78 -0
  39. package/dist/hooks/useArrowDisable.d.ts +5 -0
  40. package/dist/hooks/useCellAttributes.d.ts +18 -0
  41. package/dist/hooks/useDragAttributes.d.ts +10 -0
  42. package/dist/hooks/useEventPermissions.d.ts +7 -0
  43. package/dist/hooks/useStore.d.ts +2 -0
  44. package/dist/hooks/useSyncScroll.d.ts +8 -0
  45. package/dist/hooks/useWindowResize.d.ts +4 -0
  46. package/dist/index.d.ts +3 -0
  47. package/dist/index.js +2853 -0
  48. package/dist/package.json +65 -0
  49. package/dist/positionManger/context.d.ts +14 -0
  50. package/dist/positionManger/provider.d.ts +5 -0
  51. package/dist/positionManger/usePosition.d.ts +4 -0
  52. package/dist/store/context.d.ts +2 -0
  53. package/dist/store/default.d.ts +245 -0
  54. package/dist/store/provider.d.ts +7 -0
  55. package/dist/store/types.d.ts +27 -0
  56. package/dist/styles/styles.d.ts +30 -0
  57. package/dist/types.d.ts +372 -0
  58. package/dist/views/Day.d.ts +2 -0
  59. package/dist/views/DayAgenda.d.ts +7 -0
  60. package/dist/views/Editor.d.ts +11 -0
  61. package/dist/views/Month.d.ts +2 -0
  62. package/dist/views/MonthAgenda.d.ts +7 -0
  63. package/dist/views/Week.d.ts +2 -0
  64. package/dist/views/WeekAgenda.d.ts +8 -0
  65. package/eslint.config.js +79 -0
  66. package/index.html +41 -0
  67. package/jest.config.ts +194 -0
  68. package/package.json +137 -0
  69. package/public/favicon.ico +0 -0
  70. package/public/logo192.png +0 -0
  71. package/public/logo512.png +0 -0
  72. package/public/manifest.json +25 -0
  73. package/public/robots.txt +3 -0
  74. package/scripts/post-pack.js +34 -0
  75. package/src/App.tsx +25 -0
  76. package/src/Page1.tsx +67 -0
  77. package/src/events.tsx +227 -0
  78. package/src/index.tsx +21 -0
  79. package/src/lib/SchedulerComponent.tsx +78 -0
  80. package/src/lib/__tests__/index.test.tsx +24 -0
  81. package/src/lib/components/common/Cell.tsx +52 -0
  82. package/src/lib/components/common/LocaleArrow.tsx +38 -0
  83. package/src/lib/components/common/ResourceHeader.tsx +73 -0
  84. package/src/lib/components/common/Tabs.tsx +119 -0
  85. package/src/lib/components/common/TodayTypo.tsx +44 -0
  86. package/src/lib/components/common/WithResources.tsx +98 -0
  87. package/src/lib/components/events/Actions.tsx +65 -0
  88. package/src/lib/components/events/AgendaEventsList.tsx +115 -0
  89. package/src/lib/components/events/CurrentTimeBar.tsx +59 -0
  90. package/src/lib/components/events/EmptyAgenda.tsx +27 -0
  91. package/src/lib/components/events/EventItem.tsx +180 -0
  92. package/src/lib/components/events/EventItemPopover.tsx +179 -0
  93. package/src/lib/components/events/MonthEvents.tsx +141 -0
  94. package/src/lib/components/events/TodayEvents.tsx +99 -0
  95. package/src/lib/components/hoc/DateProvider.tsx +19 -0
  96. package/src/lib/components/inputs/DatePicker.tsx +95 -0
  97. package/src/lib/components/inputs/Input.tsx +113 -0
  98. package/src/lib/components/inputs/SelectInput.tsx +164 -0
  99. package/src/lib/components/month/MonthTable.tsx +207 -0
  100. package/src/lib/components/nav/DayDateBtn.tsx +77 -0
  101. package/src/lib/components/nav/MonthDateBtn.tsx +80 -0
  102. package/src/lib/components/nav/Navigation.tsx +201 -0
  103. package/src/lib/components/nav/WeekDateBtn.tsx +89 -0
  104. package/src/lib/components/week/WeekTable.tsx +229 -0
  105. package/src/lib/helpers/constants.ts +4 -0
  106. package/src/lib/helpers/generals.tsx +354 -0
  107. package/src/lib/hooks/useArrowDisable.ts +26 -0
  108. package/src/lib/hooks/useCellAttributes.ts +67 -0
  109. package/src/lib/hooks/useDragAttributes.ts +31 -0
  110. package/src/lib/hooks/useEventPermissions.ts +42 -0
  111. package/src/lib/hooks/useStore.ts +8 -0
  112. package/src/lib/hooks/useSyncScroll.ts +31 -0
  113. package/src/lib/hooks/useWindowResize.ts +37 -0
  114. package/src/lib/index.tsx +14 -0
  115. package/src/lib/positionManger/context.ts +14 -0
  116. package/src/lib/positionManger/provider.tsx +113 -0
  117. package/src/lib/positionManger/usePosition.ts +8 -0
  118. package/src/lib/store/context.ts +5 -0
  119. package/src/lib/store/default.ts +157 -0
  120. package/src/lib/store/provider.tsx +211 -0
  121. package/src/lib/store/types.ts +33 -0
  122. package/src/lib/styles/styles.ts +256 -0
  123. package/src/lib/types.ts +423 -0
  124. package/src/lib/views/Day.tsx +265 -0
  125. package/src/lib/views/DayAgenda.tsx +57 -0
  126. package/src/lib/views/Editor.tsx +258 -0
  127. package/src/lib/views/Month.tsx +82 -0
  128. package/src/lib/views/MonthAgenda.tsx +84 -0
  129. package/src/lib/views/Week.tsx +92 -0
  130. package/src/lib/views/WeekAgenda.tsx +81 -0
  131. package/src/vite-env.d.ts +3 -0
  132. package/tsconfig.build.json +5 -0
  133. package/tsconfig.json +27 -0
  134. 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;