@isma91/react-scheduler 4.0.4 → 4.0.5
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/README.md +1 -0
- package/dist/LICENSE +24 -0
- package/dist/README.md +174 -0
- package/{index.js → dist/index.js} +571 -557
- package/dist/package.json +65 -0
- package/{store → dist/store}/default.d.ts +2 -0
- package/{types.d.ts → dist/types.d.ts} +6 -0
- package/eslint.config.js +79 -0
- package/index.html +41 -0
- package/isma91-react-scheduler-4.0.5.tgz +0 -0
- package/jest.config.ts +194 -0
- package/package.json +76 -4
- 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 +126 -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 +159 -0
- package/src/lib/store/provider.tsx +226 -0
- package/src/lib/store/types.ts +40 -0
- package/src/lib/styles/styles.ts +256 -0
- package/src/lib/types.ts +429 -0
- package/src/lib/views/Day.tsx +272 -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/{SchedulerComponent.d.ts → dist/SchedulerComponent.d.ts} +0 -0
- /package/{components → dist/components}/common/Cell.d.ts +0 -0
- /package/{components → dist/components}/common/LocaleArrow.d.ts +0 -0
- /package/{components → dist/components}/common/ResourceHeader.d.ts +0 -0
- /package/{components → dist/components}/common/Tabs.d.ts +0 -0
- /package/{components → dist/components}/common/TodayTypo.d.ts +0 -0
- /package/{components → dist/components}/common/WithResources.d.ts +0 -0
- /package/{components → dist/components}/events/Actions.d.ts +0 -0
- /package/{components → dist/components}/events/AgendaEventsList.d.ts +0 -0
- /package/{components → dist/components}/events/CurrentTimeBar.d.ts +0 -0
- /package/{components → dist/components}/events/EmptyAgenda.d.ts +0 -0
- /package/{components → dist/components}/events/EventItem.d.ts +0 -0
- /package/{components → dist/components}/events/EventItemPopover.d.ts +0 -0
- /package/{components → dist/components}/events/MonthEvents.d.ts +0 -0
- /package/{components → dist/components}/events/TodayEvents.d.ts +0 -0
- /package/{components → dist/components}/hoc/DateProvider.d.ts +0 -0
- /package/{components → dist/components}/inputs/DatePicker.d.ts +0 -0
- /package/{components → dist/components}/inputs/Input.d.ts +0 -0
- /package/{components → dist/components}/inputs/SelectInput.d.ts +0 -0
- /package/{components → dist/components}/month/MonthTable.d.ts +0 -0
- /package/{components → dist/components}/nav/DayDateBtn.d.ts +0 -0
- /package/{components → dist/components}/nav/MonthDateBtn.d.ts +0 -0
- /package/{components → dist/components}/nav/Navigation.d.ts +0 -0
- /package/{components → dist/components}/nav/WeekDateBtn.d.ts +0 -0
- /package/{components → dist/components}/week/WeekTable.d.ts +0 -0
- /package/{helpers → dist/helpers}/constants.d.ts +0 -0
- /package/{helpers → dist/helpers}/generals.d.ts +0 -0
- /package/{hooks → dist/hooks}/useArrowDisable.d.ts +0 -0
- /package/{hooks → dist/hooks}/useCellAttributes.d.ts +0 -0
- /package/{hooks → dist/hooks}/useDragAttributes.d.ts +0 -0
- /package/{hooks → dist/hooks}/useEventPermissions.d.ts +0 -0
- /package/{hooks → dist/hooks}/useStore.d.ts +0 -0
- /package/{hooks → dist/hooks}/useSyncScroll.d.ts +0 -0
- /package/{hooks → dist/hooks}/useWindowResize.d.ts +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{positionManger → dist/positionManger}/context.d.ts +0 -0
- /package/{positionManger → dist/positionManger}/provider.d.ts +0 -0
- /package/{positionManger → dist/positionManger}/usePosition.d.ts +0 -0
- /package/{store → dist/store}/context.d.ts +0 -0
- /package/{store → dist/store}/provider.d.ts +0 -0
- /package/{store → dist/store}/types.d.ts +0 -0
- /package/{styles → dist/styles}/styles.d.ts +0 -0
- /package/{views → dist/views}/Day.d.ts +0 -0
- /package/{views → dist/views}/DayAgenda.d.ts +0 -0
- /package/{views → dist/views}/Editor.d.ts +0 -0
- /package/{views → dist/views}/Month.d.ts +0 -0
- /package/{views → dist/views}/MonthAgenda.d.ts +0 -0
- /package/{views → dist/views}/Week.d.ts +0 -0
- /package/{views → dist/views}/WeekAgenda.d.ts +0 -0
|
@@ -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;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { DefaultResource } from "../../types";
|
|
3
|
+
import { ResourceHeader } from "./ResourceHeader";
|
|
4
|
+
import { ButtonTabProps, ButtonTabs } from "./Tabs";
|
|
5
|
+
import useStore from "../../hooks/useStore";
|
|
6
|
+
import { Box, useTheme } from "@mui/material";
|
|
7
|
+
|
|
8
|
+
interface WithResourcesProps {
|
|
9
|
+
renderChildren(resource: DefaultResource): React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
const WithResources = ({ renderChildren }: WithResourcesProps) => {
|
|
12
|
+
const { resources, resourceFields, resourceViewMode } = useStore();
|
|
13
|
+
const theme = useTheme();
|
|
14
|
+
|
|
15
|
+
if (resourceViewMode === "tabs") {
|
|
16
|
+
return <ResourcesTabTables renderChildren={renderChildren} />;
|
|
17
|
+
} else if (resourceViewMode === "vertical") {
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
{resources.map((res: DefaultResource, i: number) => (
|
|
21
|
+
<Box key={`${res[resourceFields.idField]}_${i}`} sx={{ display: "flex" }}>
|
|
22
|
+
<Box
|
|
23
|
+
sx={{
|
|
24
|
+
borderColor: theme.palette.grey[300],
|
|
25
|
+
borderStyle: "solid",
|
|
26
|
+
borderWidth: "1px 1px 0 1px",
|
|
27
|
+
paddingTop: 1,
|
|
28
|
+
flexBasis: 140,
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
<ResourceHeader resource={res} />
|
|
32
|
+
</Box>
|
|
33
|
+
<Box
|
|
34
|
+
//
|
|
35
|
+
sx={{ width: "100%", overflowX: "auto" }}
|
|
36
|
+
>
|
|
37
|
+
{renderChildren(res)}
|
|
38
|
+
</Box>
|
|
39
|
+
</Box>
|
|
40
|
+
))}
|
|
41
|
+
</>
|
|
42
|
+
);
|
|
43
|
+
} else {
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
{resources.map((res: DefaultResource, i: number) => (
|
|
47
|
+
<div key={`${res[resourceFields.idField]}_${i}`}>
|
|
48
|
+
<ResourceHeader resource={res} />
|
|
49
|
+
{renderChildren(res)}
|
|
50
|
+
</div>
|
|
51
|
+
))}
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const ResourcesTabTables = ({ renderChildren }: WithResourcesProps) => {
|
|
58
|
+
const { resources, resourceFields, selectedTab, handleState, onResourceChange } = useStore();
|
|
59
|
+
|
|
60
|
+
const tabs: ButtonTabProps[] = resources.map((res) => {
|
|
61
|
+
return {
|
|
62
|
+
id: res[resourceFields.idField],
|
|
63
|
+
label: <ResourceHeader resource={res} />,
|
|
64
|
+
component: <>{renderChildren(res)}</>,
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const setTab = (tab: DefaultResource["assignee"]) => {
|
|
69
|
+
handleState(tab, "selectedTab");
|
|
70
|
+
if (typeof onResourceChange === "function") {
|
|
71
|
+
const selected = resources.find((re) => re[resourceFields.idField] === tab);
|
|
72
|
+
if (selected) {
|
|
73
|
+
onResourceChange(selected);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const currentTabSafeId = useMemo(() => {
|
|
79
|
+
const firstId = resources[0][resourceFields.idField];
|
|
80
|
+
if (!selectedTab) {
|
|
81
|
+
return firstId;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Make sure current selected id is within the resources array
|
|
85
|
+
const idx = resources.findIndex((re) => re[resourceFields.idField] === selectedTab);
|
|
86
|
+
if (idx < 0) {
|
|
87
|
+
return firstId;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return selectedTab;
|
|
91
|
+
}, [resources, resourceFields.idField, selectedTab]);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<ButtonTabs tabs={tabs} tab={currentTabSafeId} setTab={setTab} style={{ display: "grid" }} />
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export { WithResources };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import DeleteRounded from "@mui/icons-material/DeleteRounded";
|
|
2
|
+
import EditRounded from "@mui/icons-material/EditRounded";
|
|
3
|
+
import { Button, Grow, IconButton, Slide } from "@mui/material";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { EventActions as Actions } from "../../styles/styles";
|
|
6
|
+
import { ProcessedEvent } from "../../types";
|
|
7
|
+
import useStore from "../../hooks/useStore";
|
|
8
|
+
import useEventPermissions from "../../hooks/useEventPermissions";
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
event: ProcessedEvent;
|
|
12
|
+
onDelete(): void;
|
|
13
|
+
onEdit(): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const EventActions = ({ event, onDelete, onEdit }: Props) => {
|
|
17
|
+
const { translations, direction } = useStore();
|
|
18
|
+
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
|
19
|
+
|
|
20
|
+
const handleDelete = () => {
|
|
21
|
+
if (!deleteConfirm) {
|
|
22
|
+
return setDeleteConfirm(true);
|
|
23
|
+
}
|
|
24
|
+
onDelete();
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const { canEdit, canDelete } = useEventPermissions(event);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Actions>
|
|
31
|
+
<Grow in={!deleteConfirm} exit={false} timeout={400} unmountOnExit>
|
|
32
|
+
<div>
|
|
33
|
+
{canEdit && (
|
|
34
|
+
<IconButton size="small" onClick={onEdit}>
|
|
35
|
+
<EditRounded />
|
|
36
|
+
</IconButton>
|
|
37
|
+
)}
|
|
38
|
+
{canDelete && (
|
|
39
|
+
<IconButton size="small" onClick={handleDelete}>
|
|
40
|
+
<DeleteRounded />
|
|
41
|
+
</IconButton>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
</Grow>
|
|
45
|
+
<Slide
|
|
46
|
+
in={deleteConfirm}
|
|
47
|
+
direction={direction === "rtl" ? "right" : "left"}
|
|
48
|
+
unmountOnExit
|
|
49
|
+
timeout={400}
|
|
50
|
+
exit={false}
|
|
51
|
+
>
|
|
52
|
+
<div>
|
|
53
|
+
<Button className="delete" size="small" onClick={handleDelete}>
|
|
54
|
+
{translations.form.delete.toUpperCase()}
|
|
55
|
+
</Button>
|
|
56
|
+
<Button className="cancel" size="small" onClick={() => setDeleteConfirm(false)}>
|
|
57
|
+
{translations.form.cancel.toUpperCase()}
|
|
58
|
+
</Button>
|
|
59
|
+
</div>
|
|
60
|
+
</Slide>
|
|
61
|
+
</Actions>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default EventActions;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Fragment, MouseEvent, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
useTheme,
|
|
4
|
+
List,
|
|
5
|
+
ListItemButton,
|
|
6
|
+
ListItemAvatar,
|
|
7
|
+
Avatar,
|
|
8
|
+
ListItemText,
|
|
9
|
+
} from "@mui/material";
|
|
10
|
+
import { format } from "date-fns";
|
|
11
|
+
import { ProcessedEvent } from "../../types";
|
|
12
|
+
import { getHourFormat, isTimeZonedToday } from "../../helpers/generals";
|
|
13
|
+
import useStore from "../../hooks/useStore";
|
|
14
|
+
import EventItemPopover from "./EventItemPopover";
|
|
15
|
+
|
|
16
|
+
interface AgendaEventsListProps {
|
|
17
|
+
day: Date;
|
|
18
|
+
events: ProcessedEvent[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const AgendaEventsList = ({ day, events }: AgendaEventsListProps) => {
|
|
22
|
+
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
|
|
23
|
+
const [selectedEvent, setSelectedEvent] = useState<ProcessedEvent>();
|
|
24
|
+
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
|
25
|
+
const {
|
|
26
|
+
locale,
|
|
27
|
+
hourFormat,
|
|
28
|
+
eventRenderer,
|
|
29
|
+
onEventClick,
|
|
30
|
+
timeZone,
|
|
31
|
+
disableViewer,
|
|
32
|
+
displayHourRange = true,
|
|
33
|
+
} = useStore();
|
|
34
|
+
const theme = useTheme();
|
|
35
|
+
const hFormat = getHourFormat(hourFormat);
|
|
36
|
+
|
|
37
|
+
const triggerViewer = (el?: MouseEvent<Element>) => {
|
|
38
|
+
if (!el?.currentTarget && deleteConfirm) {
|
|
39
|
+
setDeleteConfirm(false);
|
|
40
|
+
}
|
|
41
|
+
setAnchorEl(el?.currentTarget || null);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Fragment>
|
|
46
|
+
<List>
|
|
47
|
+
{events.map((event) => {
|
|
48
|
+
const startIsToday = isTimeZonedToday({
|
|
49
|
+
dateLeft: event.start,
|
|
50
|
+
dateRight: day,
|
|
51
|
+
timeZone,
|
|
52
|
+
});
|
|
53
|
+
const startFormat = startIsToday ? hFormat : `MMM d, ${hFormat}`;
|
|
54
|
+
const startDate = format(event.start, startFormat, {
|
|
55
|
+
locale,
|
|
56
|
+
});
|
|
57
|
+
const endIsToday = isTimeZonedToday({ dateLeft: event.end, dateRight: day, timeZone });
|
|
58
|
+
|
|
59
|
+
const endFormat = endIsToday ? hFormat : `MMM d, ${hFormat}`;
|
|
60
|
+
const endDate = format(event.end, endFormat, {
|
|
61
|
+
locale,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (typeof eventRenderer === "function") {
|
|
65
|
+
return eventRenderer({
|
|
66
|
+
event,
|
|
67
|
+
onClick: (e) => {
|
|
68
|
+
setSelectedEvent(event);
|
|
69
|
+
triggerViewer(e);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<ListItemButton
|
|
76
|
+
key={`${event.start.getTime()}_${event.end.getTime()}_${event.event_id}`}
|
|
77
|
+
focusRipple
|
|
78
|
+
disableRipple={disableViewer}
|
|
79
|
+
tabIndex={disableViewer ? -1 : 0}
|
|
80
|
+
disabled={event.disabled}
|
|
81
|
+
onClick={(e) => {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
e.stopPropagation();
|
|
84
|
+
if (!disableViewer) {
|
|
85
|
+
triggerViewer(e);
|
|
86
|
+
}
|
|
87
|
+
setSelectedEvent(event);
|
|
88
|
+
if (typeof onEventClick === "function") {
|
|
89
|
+
onEventClick(event);
|
|
90
|
+
}
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<ListItemAvatar>
|
|
94
|
+
<Avatar
|
|
95
|
+
sx={{
|
|
96
|
+
bgcolor: event.disabled ? "#d0d0d0" : event.color || theme.palette.primary.main,
|
|
97
|
+
color: event.disabled
|
|
98
|
+
? "#808080"
|
|
99
|
+
: event.textColor || theme.palette.primary.contrastText,
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{event.agendaAvatar || " "}
|
|
103
|
+
</Avatar>
|
|
104
|
+
</ListItemAvatar>
|
|
105
|
+
<ListItemText
|
|
106
|
+
primary={event.title}
|
|
107
|
+
secondary={displayHourRange ? `${startDate} - ${endDate}` : undefined}
|
|
108
|
+
/>
|
|
109
|
+
</ListItemButton>
|
|
110
|
+
);
|
|
111
|
+
})}
|
|
112
|
+
</List>
|
|
113
|
+
|
|
114
|
+
{/* Viewer */}
|
|
115
|
+
{selectedEvent && (
|
|
116
|
+
<EventItemPopover
|
|
117
|
+
anchorEl={anchorEl}
|
|
118
|
+
event={selectedEvent}
|
|
119
|
+
onTriggerViewer={triggerViewer}
|
|
120
|
+
/>
|
|
121
|
+
)}
|
|
122
|
+
</Fragment>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export default AgendaEventsList;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { differenceInMinutes, set } from "date-fns";
|
|
3
|
+
import { BORDER_HEIGHT } from "../../helpers/constants";
|
|
4
|
+
import { getTimeZonedDate } from "../../helpers/generals";
|
|
5
|
+
import { TimeIndicatorBar } from "../../styles/styles";
|
|
6
|
+
|
|
7
|
+
interface CurrentTimeBarProps {
|
|
8
|
+
startHour: number;
|
|
9
|
+
step: number;
|
|
10
|
+
minuteHeight: number;
|
|
11
|
+
timeZone?: string;
|
|
12
|
+
zIndex?: number;
|
|
13
|
+
currentTime?: Date;
|
|
14
|
+
color?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function calculateTop({
|
|
18
|
+
startHour,
|
|
19
|
+
step,
|
|
20
|
+
minuteHeight,
|
|
21
|
+
timeZone,
|
|
22
|
+
currentTime,
|
|
23
|
+
}: CurrentTimeBarProps): number {
|
|
24
|
+
const now = currentTime
|
|
25
|
+
? getTimeZonedDate(currentTime, timeZone)
|
|
26
|
+
: getTimeZonedDate(new Date(), timeZone);
|
|
27
|
+
|
|
28
|
+
const minutesFromTop = differenceInMinutes(now, set(now, { hours: startHour, minutes: 0 }));
|
|
29
|
+
const topSpace = minutesFromTop * minuteHeight;
|
|
30
|
+
const slotsFromTop = minutesFromTop / step;
|
|
31
|
+
const borderFactor = slotsFromTop + BORDER_HEIGHT;
|
|
32
|
+
const top = topSpace + borderFactor;
|
|
33
|
+
|
|
34
|
+
return top;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const CurrentTimeBar = (props: CurrentTimeBarProps) => {
|
|
38
|
+
const [top, setTop] = useState(calculateTop(props));
|
|
39
|
+
const { startHour, step, minuteHeight, timeZone, currentTime, color } = props;
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const calcProps = { startHour, step, minuteHeight, timeZone, currentTime };
|
|
43
|
+
setTop(calculateTop(calcProps));
|
|
44
|
+
const interval = setInterval(() => setTop(calculateTop(calcProps)), 60 * 1000);
|
|
45
|
+
return () => clearInterval(interval);
|
|
46
|
+
}, [startHour, step, minuteHeight, timeZone, currentTime]);
|
|
47
|
+
|
|
48
|
+
// Prevent showing bar on top of days/header
|
|
49
|
+
if (top < 0) return null;
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<TimeIndicatorBar style={{ top, zIndex: props.zIndex }} color={color}>
|
|
53
|
+
<div />
|
|
54
|
+
<div />
|
|
55
|
+
</TimeIndicatorBar>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default CurrentTimeBar;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AgendaDiv } from "../../styles/styles";
|
|
2
|
+
import { Typography } from "@mui/material";
|
|
3
|
+
import useStore from "../../hooks/useStore";
|
|
4
|
+
|
|
5
|
+
const EmptyAgenda = () => {
|
|
6
|
+
const { height, translations, stickyNavigationOffset, stickyNavigationHeight } = useStore();
|
|
7
|
+
return (
|
|
8
|
+
<AgendaDiv
|
|
9
|
+
stickyOffset={stickyNavigationOffset}
|
|
10
|
+
stickyHeight={stickyNavigationHeight}
|
|
11
|
+
sx={{
|
|
12
|
+
borderWidth: 1,
|
|
13
|
+
padding: 1,
|
|
14
|
+
height: height / 2,
|
|
15
|
+
display: "flex",
|
|
16
|
+
alignItems: "center",
|
|
17
|
+
justifyContent: "center",
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
<div className="rs__cell rs__agenda_items">
|
|
21
|
+
<Typography>{translations.noDataToDisplay}</Typography>
|
|
22
|
+
</div>
|
|
23
|
+
</AgendaDiv>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default EmptyAgenda;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Fragment, MouseEvent, useCallback, useMemo, useState } from "react";
|
|
2
|
+
import { Typography, ButtonBase, useTheme } from "@mui/material";
|
|
3
|
+
import { format } from "date-fns";
|
|
4
|
+
import { ProcessedEvent } from "../../types";
|
|
5
|
+
import ArrowRightRoundedIcon from "@mui/icons-material/ArrowRightRounded";
|
|
6
|
+
import ArrowLeftRoundedIcon from "@mui/icons-material/ArrowLeftRounded";
|
|
7
|
+
import { EventItemPaper } from "../../styles/styles";
|
|
8
|
+
import { differenceInDaysOmitTime, getHourFormat } from "../../helpers/generals";
|
|
9
|
+
import useStore from "../../hooks/useStore";
|
|
10
|
+
import useDragAttributes from "../../hooks/useDragAttributes";
|
|
11
|
+
import EventItemPopover from "./EventItemPopover";
|
|
12
|
+
import useEventPermissions from "../../hooks/useEventPermissions";
|
|
13
|
+
|
|
14
|
+
interface EventItemProps {
|
|
15
|
+
event: ProcessedEvent;
|
|
16
|
+
multiday?: boolean;
|
|
17
|
+
hasPrev?: boolean;
|
|
18
|
+
hasNext?: boolean;
|
|
19
|
+
showdate?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const EventItem = ({ event, multiday, hasPrev, hasNext, showdate = true }: EventItemProps) => {
|
|
23
|
+
const { direction, locale, hourFormat, eventRenderer, onEventClick, view, disableViewer } =
|
|
24
|
+
useStore();
|
|
25
|
+
const dragProps = useDragAttributes(event);
|
|
26
|
+
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
|
|
27
|
+
const [deleteConfirm, setDeleteConfirm] = useState(false);
|
|
28
|
+
const theme = useTheme();
|
|
29
|
+
const hFormat = getHourFormat(hourFormat);
|
|
30
|
+
|
|
31
|
+
const NextArrow = direction === "rtl" ? ArrowLeftRoundedIcon : ArrowRightRoundedIcon;
|
|
32
|
+
const PrevArrow = direction === "rtl" ? ArrowRightRoundedIcon : ArrowLeftRoundedIcon;
|
|
33
|
+
const hideDates = differenceInDaysOmitTime(event.start, event.end) <= 0 && event.allDay;
|
|
34
|
+
|
|
35
|
+
const { canDrag } = useEventPermissions(event);
|
|
36
|
+
|
|
37
|
+
const triggerViewer = useCallback(
|
|
38
|
+
(el?: MouseEvent<Element>) => {
|
|
39
|
+
if (!el?.currentTarget && deleteConfirm) {
|
|
40
|
+
setDeleteConfirm(false);
|
|
41
|
+
}
|
|
42
|
+
setAnchorEl(el?.currentTarget || null);
|
|
43
|
+
},
|
|
44
|
+
[deleteConfirm]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const renderEvent = useMemo(() => {
|
|
48
|
+
// Check if has custom render event method
|
|
49
|
+
// only applicable to non-multiday events and not in month-view
|
|
50
|
+
if (typeof eventRenderer === "function" && !multiday && view !== "month") {
|
|
51
|
+
const custom = eventRenderer({ event, onClick: triggerViewer, ...dragProps });
|
|
52
|
+
if (custom) {
|
|
53
|
+
return (
|
|
54
|
+
<EventItemPaper key={`${event.start.getTime()}_${event.end.getTime()}_${event.event_id}`}>
|
|
55
|
+
{custom}
|
|
56
|
+
</EventItemPaper>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let item = (
|
|
62
|
+
<div style={{ padding: "2px 6px" }}>
|
|
63
|
+
<Typography variant="subtitle2" style={{ fontSize: 12 }} noWrap>
|
|
64
|
+
{event.title}
|
|
65
|
+
</Typography>
|
|
66
|
+
{event.subtitle && (
|
|
67
|
+
<Typography variant="body2" style={{ fontSize: 11 }} noWrap>
|
|
68
|
+
{event.subtitle}
|
|
69
|
+
</Typography>
|
|
70
|
+
)}
|
|
71
|
+
{showdate && (
|
|
72
|
+
<Typography style={{ fontSize: 11 }} noWrap>
|
|
73
|
+
{`${format(event.start, hFormat, {
|
|
74
|
+
locale,
|
|
75
|
+
})} - ${format(event.end, hFormat, { locale })}`}
|
|
76
|
+
</Typography>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
if (multiday) {
|
|
81
|
+
item = (
|
|
82
|
+
<div
|
|
83
|
+
style={{
|
|
84
|
+
padding: 2,
|
|
85
|
+
display: "flex",
|
|
86
|
+
alignItems: "center",
|
|
87
|
+
justifyContent: "space-between",
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<Typography sx={{ fontSize: 11 }} noWrap>
|
|
91
|
+
{hasPrev ? (
|
|
92
|
+
<PrevArrow fontSize="small" sx={{ display: "flex" }} />
|
|
93
|
+
) : (
|
|
94
|
+
showdate && !hideDates && format(event.start, hFormat, { locale })
|
|
95
|
+
)}
|
|
96
|
+
</Typography>
|
|
97
|
+
<Typography variant="subtitle2" align="center" sx={{ fontSize: 12 }} noWrap>
|
|
98
|
+
{event.title}
|
|
99
|
+
</Typography>
|
|
100
|
+
<Typography sx={{ fontSize: 11 }} noWrap>
|
|
101
|
+
{hasNext ? (
|
|
102
|
+
<NextArrow fontSize="small" sx={{ display: "flex" }} />
|
|
103
|
+
) : (
|
|
104
|
+
showdate && !hideDates && format(event.end, hFormat, { locale })
|
|
105
|
+
)}
|
|
106
|
+
</Typography>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return (
|
|
111
|
+
<EventItemPaper
|
|
112
|
+
key={`${event.start.getTime()}_${event.end.getTime()}_${event.event_id}`}
|
|
113
|
+
disabled={event.disabled}
|
|
114
|
+
sx={{
|
|
115
|
+
bgcolor: event.disabled ? "#d0d0d0" : event.color || theme.palette.primary.main,
|
|
116
|
+
color: event.disabled ? "#808080" : event.textColor || theme.palette.primary.contrastText,
|
|
117
|
+
borderTop: event._hasPrev
|
|
118
|
+
? `3px dashed ${event.textColor || theme.palette.primary.contrastText}`
|
|
119
|
+
: undefined,
|
|
120
|
+
borderBottom: event._hasNext
|
|
121
|
+
? `3px dashed ${event.textColor || theme.palette.primary.contrastText}`
|
|
122
|
+
: undefined,
|
|
123
|
+
...(event.sx || {}),
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
<ButtonBase
|
|
127
|
+
onClick={(e) => {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
e.stopPropagation();
|
|
130
|
+
if (!disableViewer) {
|
|
131
|
+
triggerViewer(e);
|
|
132
|
+
}
|
|
133
|
+
if (typeof onEventClick === "function") {
|
|
134
|
+
onEventClick(event);
|
|
135
|
+
}
|
|
136
|
+
}}
|
|
137
|
+
focusRipple
|
|
138
|
+
tabIndex={disableViewer ? -1 : 0}
|
|
139
|
+
disableRipple={disableViewer}
|
|
140
|
+
disabled={event.disabled}
|
|
141
|
+
>
|
|
142
|
+
<div {...dragProps} draggable={canDrag}>
|
|
143
|
+
{item}
|
|
144
|
+
</div>
|
|
145
|
+
</ButtonBase>
|
|
146
|
+
</EventItemPaper>
|
|
147
|
+
);
|
|
148
|
+
}, [
|
|
149
|
+
eventRenderer,
|
|
150
|
+
multiday,
|
|
151
|
+
view,
|
|
152
|
+
event,
|
|
153
|
+
showdate,
|
|
154
|
+
hFormat,
|
|
155
|
+
locale,
|
|
156
|
+
theme.palette.primary.main,
|
|
157
|
+
theme.palette.primary.contrastText,
|
|
158
|
+
disableViewer,
|
|
159
|
+
dragProps,
|
|
160
|
+
canDrag,
|
|
161
|
+
triggerViewer,
|
|
162
|
+
hasPrev,
|
|
163
|
+
PrevArrow,
|
|
164
|
+
hideDates,
|
|
165
|
+
hasNext,
|
|
166
|
+
NextArrow,
|
|
167
|
+
onEventClick,
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<Fragment>
|
|
172
|
+
{renderEvent}
|
|
173
|
+
|
|
174
|
+
{/* Viewer */}
|
|
175
|
+
<EventItemPopover anchorEl={anchorEl} event={event} onTriggerViewer={triggerViewer} />
|
|
176
|
+
</Fragment>
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export default EventItem;
|