@bitnoi.se/react-scheduler
Version:
<div align="center"> <img src="logo.svg" style="height: 60px" alt="@bitnoise/react-scheduler"> <hr /> <p align="center"> ✨ <a href="https://scheduler.bitnoise.pl/">https://scheduler.bitnoise.pl/</a> ✨ <br/> Open sourced, typescript orien
383 lines (322 loc) • 16.5 kB
Markdown
<div align="center">
<img src="logo.svg" style="height: 60px" alt="@bitnoise/react-scheduler">
<hr />
<p align="center">
✨ <a href="https://scheduler.bitnoise.pl/">https://scheduler.bitnoise.pl/</a> ✨
<br/>
Open sourced, typescript oriented, light-weight, and ultra fast React Component for creating gantt charts.
</p>
<div align="center">
<a href="https://bit.ly/react_scheduler">Youtube Tutorial</a>
<span> • </span>
<a href="https://www.npmjs.com/package/@bitnoi.se/react-scheduler">npm</a>
<span> • </span>
<a href="https://github.com/Bitnoise/react-scheduler/issues/new">Report an issue</a>
</div>
</div>
<hr />
<div>
<p>
We're thrilled that our component has caught your interest! We'd love to see how you're using it in your projects.
</p>
<p>
Share descriptions and screenshots of your projects that feature Scheduler, and you could be one of 5 lucky winners to receive our limited edition hoodies!
</p>
<p>
This promotion runs until September 25, 2024. For more information, click here: <a href="https://github.com/Bitnoise/react-scheduler/discussions/149">Scheduler in your projects 🎉</a>
</p>
<p>
<b>Can't wait to see your amazing work! Good luck!</b>
</p>
<h3 align="center">NEWSLETTER</h3>
<p>
If you want to stay updated with Scheduler updates and news from the technical world, sign up for our newsletter. We don't spam (we send emails once a month), we don't run a sales newsletter, and we respect your time.
</p>
<p>
See for yourself: <a href="https://www.bitnoise.pl/newsletter">NEWSLETTER</a>
</p>
</div>
<hr/>
### Installation
```bash
# yarn
yarn add '.se/react-scheduler'
# npm
npm install '.se/react-scheduler'
```
### Example usage
1. import required styles for scheduler
```ts
import "@bitnoi.se/react-scheduler/dist/style.css";
```
2. Import Scheduler component into your project
```ts
import { Scheduler, SchedulerData } from "@bitnoi.se/react-scheduler";
import dayjs from "dayjs";
default export function Component() {
const [filterButtonState, setFilterButtonState] = useState(0);
const [range, setRange] = useState({
startDate: new Date(),
endDate: new Date()
});
const handleRangeChange = useCallback((range) => {
setRange(range);
}, []);
// Filtering events that are included in current date range
// Example can be also found on video https://youtu.be/9oy4rTVEfBQ?t=118&si=52BGKSIYz6bTZ7fx
// and in the react-scheduler repo App.tsx file https://github.com/Bitnoise/react-scheduler/blob/master/src/App.tsx
const filteredMockedSchedulerData = mockedSchedulerData.map((person) => ({
...person,
data: person.data.filter(
(project) =>
// we use "dayjs" for date calculations, but feel free to use library of your choice
dayjs(project.startDate).isBetween(range.startDate, range.endDate) ||
dayjs(project.endDate).isBetween(range.startDate, range.endDate) ||
(dayjs(project.startDate).isBefore(range.startDate, "day") &&
dayjs(project.endDate).isAfter(range.endDate, "day"))
)
}))
return (
<section>
<Scheduler
data={filteredMockedSchedulerData}
isLoading={isLoading}
onRangeChange={handleRangeChange}
onTileClick={(clickedResource) => console.log(clickedResource)}
onItemClick={(item) => console.log(item)}
onFilterData={() => {
// Some filtering logic...
setFilterButtonState(1);
}}
onClearFilterData={() => {
// Some clearing filters logic...
setFilterButtonState(0)
}}
config={{
zoom: 0,
filterButtonState,
}}
/>
</section>
);
}
const mockedSchedulerData: SchedulerData = [
{
id: "070ac5b5-8369-4cd2-8ba2-0a209130cc60",
label: {
icon: "https://picsum.photos/24",
title: "Joe Doe",
subtitle: "Frontend Developer"
},
data: [
{
id: "8b71a8a5-33dd-4fc8-9caa-b4a584ba3762",
startDate: new Date("2023-04-13T15:31:24.272Z"),
endDate: new Date("2023-08-28T10:28:22.649Z"),
occupancy: 3600,
title: "Project A",
subtitle: "Subtitle A",
description: "array indexing Salad West Account",
bgColor: "rgb(254,165,177)"
},
{
id: "22fbe237-6344-4c8e-affb-64a1750f33bd",
startDate: new Date("2023-10-07T08:16:31.123Z"),
endDate: new Date("2023-11-15T21:55:23.582Z"),
occupancy: 2852,
title: "Project B",
subtitle: "Subtitle B",
description: "Tuna Home pascal IP drive",
bgColor: "rgb(254,165,177)"
},
{
id: "3601c1cd-f4b5-46bc-8564-8c983919e3f5",
startDate: new Date("2023-03-30T22:25:14.377Z"),
endDate: new Date("2023-09-01T07:20:50.526Z"),
occupancy: 1800,
title: "Project C",
subtitle: "Subtitle C",
bgColor: "rgb(254,165,177)"
},
{
id: "b088e4ac-9911-426f-aef3-843d75e714c2",
startDate: new Date("2023-10-28T10:08:22.986Z"),
endDate: new Date("2023-10-30T12:30:30.150Z"),
occupancy: 11111,
title: "Project D",
subtitle: "Subtitle D",
description: "Garden heavy an software Metal",
bgColor: "rgb(254,165,177)"
}
]
}
];
```
3. If some problems occur, please see our troubleshooting section below.
### Scheduler API
##### Scheduler Component Props
| Property Name | Type | Arguments | Description |
| ----------------- | ---------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| isLoading | `boolean` | - | shows loading indicators on scheduler |
| onRangeChange | `function` | updated `startDate` and `endDate` | runs whenever user reaches end of currently rendered canvas |
| onTileClick | `function` | clicked resource data | detects resource click |
| onItemClick | `function` | clicked left column item data | detects item click on left column |
| onFilterData | `function` | - | callback firing when filter button was clicked |
| onClearFilterData | `function` | - | callback firing when clear filters button was clicked (clearing button is visible **only** when filterButtonState is set to `>0`) |
| config | `Config` | - | object with scheduler config properties |
##### Scheduler Config Object
---
| Property Name | Type | Default | Description |
| ------------------------------------ | ------------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| zoom | `0` or `1` or `2` | 0 | `0` - display grid divided into weeks `1` - display grid divided into days `2` - display grid divided into hours |
| filterButtonState | `number` | 0 | `< 0` - hides filter button, `0` - state for when filters were not set, `> 0` - state for when some filters were set (allows to also handle `onClearFilterData` event) |
| maxRecordsPerPage | `number` | 50 | number of items from `SchedulerData` visible per page |
| lang | `en`, `lt` or `pl` | en | scheduler's language |
| includeTakenHoursOnWeekendsInDayView | `boolean` | `false` | show weekends as taken when given resource is longer than a week |
| showTooltip | `boolean` | `true` | show tooltip when hovering over tiles |
| translations | `LocaleType[]` | `undefined` | option to add specific langs translations |
| showThemeToggle | `boolean` | `false` | show toggle button to switch between light/dark mode |
| defaultTheme | `light` or `dark` | `light` | scheduler's default theme |
#### Translation object example
```ts
import enDayjsTranslations from "dayjs/locale/en";
const langs: LocaleType[] = [
{
id: "en",
lang: {
feelingEmpty: "I feel so empty...",
free: "Free",
loadNext: "Next",
loadPrevious: "Previous",
over: "over",
taken: "Taken",
topbar: {
filters: "Filters",
next: "next",
prev: "prev",
today: "Today",
view: "View"
},
search: "search",
week: "week"
},
translateCode: "en-EN",
dayjsTranslations: enDayjsTranslations
}
];
<Scheduler
// ... //
config={{
lang: "en",
translations: langs
}}
/>;
```
#### Scheduler LocaleType Object
| Property Name | Type | Description |
| ----------------- | -------------------------- | ---------------------------------- |
| id | `string` | key is needed for selecting lang |
| lang | `Translation` | object with translations |
| translateCode | `string` | code that is saved in localStorage |
| dayjsTranslations | `string ILocale undefined` | object with translation from dayjs |
#### Scheduler Translation Object
| Property Name | Type |
| ------------- | -------- |
| feelingEmpty | `string` |
| free | `string` |
| loadNext | `string` |
| loadPrevious | `string` |
| over | `string` |
| taken | `string` |
| search | `string` |
| week | `string` |
| topbar | `Topbar` |
##### Scheduler Topbar Object
| Property Name | Type |
| ------------- | -------- |
| filters | `string` |
| next | `string` |
| prev | `string` |
| today | `string` |
| view | `string` |
##### Scheduler Data
array of chart rows with shape of
| Property Name | Type | Description |
| -------- | --------------------- | -------------------------------- |
| id | `string` | unique row id |
| label | `SchedulerRowLabel` | row's label, `e.g person's name, surname, icon` |
| data | `Array<ResourceItem>` | array of `resources` |
##### Left Colum Item Data
data that is accessible as argument of `onItemClick` callback
| Property Name | Type | Description |
| -------- | --------------------- | -------------------------------- |
| id | `string` | unique row id |
| label | `SchedulerRowLabel` | row's label, `e.g person's name, surname, icon` |
##### Resource Item
item that will be visible on the grid as tile and that will be accessible as argument of `onTileClick` event
| Property Name | Type | Description |
| ----------- | ----------------- | ------------------------------------------------------------------------------------------------------- |
| id | `string` | unique resource id |
| title | `string` | resource title that will be displayed on resource tile |
| subtitle | `string (optional)` | resource subtitle that will be displayed on resource tile |
| description | `string (optional)` | resource description that will be displayed on resource tile |
| startDate | `Date` | date for calculating start position for resource |
| endDate | `Date` | date for calculating end position for resource |
| occupancy | `number` | number of seconds resource takes up for given row that will be visible on resource tooltip when hovered |
| bgColor | `string (optional)` | tile color |
### Troubleshooting
- For using Scheduler with RemixJS make sure to add `.se/react-scheduler` to `serverDependenciesToBundle` in `remix.config.js` like so:
```js
// remix.config.js
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
// ...
serverDependenciesToBundle: [..., "@bitnoi.se/react-scheduler"],
};
```
- When using with NextJS (app router) Scheduler needs to be wrapped with component with `use client`
```ts
"use client"
import { Scheduler, SchedulerProps } from "@bitnoi.se/react-scheduler";
default export function SchedulerClient(props: SchedulerProps) {
return <Scheduler {...props} />;
}
```
- When using with NextJS (pages router) it needs to be imported using `dynamic`:
```ts
import dynamic from "next/dynamic";
const Scheduler = dynamic(() => import("@bitnoi.se/react-scheduler").then((mod) => mod.Scheduler), {
ssr: false
});
```
- How to customize Scheduler dimensions
Scheduler is position absolutely to take all available space. If you want to have fixed dimensions wrap Scheduler inside a div with position set to relative.
Example using styled components:
```ts
export const StyledSchedulerFrame = styled.div`
position: relative;
height: 40vh;
width: 40vw;
`;
<StyledSchedulerFrame>
<Scheduler {...}/>
</StyledSchedulerFrame>
```
### Known Issues
1. No responsiveness
2. Slower performance on Firefox when working with big set of data due to Firefox being slower working with canvas
### How to contribute
- **Reporting Issues**: If you come across any bugs, glitches, or have any suggestions for improvements, please [open an issue](https://github.com/Bitnoise/react-scheduler/issues) on our GitHub repository. Provide as much detail as possible, including steps to reproduce the issue.
- **Suggesting Enhancements**: If you have ideas for new features or enhancements, we would love to hear them! You can [open an issue](https://github.com/Bitnoise/react-scheduler/issues) on our GitHub repository and clearly describe your suggestion.
- **Submitting Pull Requests**: If you have developed a fix or a new feature that you would like to contribute, you can submit a pull request. Here's a quick overview of the process:
- Clone the repository and create your own branch: `git checkout -b feat/your-branch-name`.
- Implement your changes, following the **code style and guidelines** from [development.md](development.md).
- Test your changes to ensure they work as expected.
- Commit your changes and push to your forked repository.
- Open a pull request against our main repository's `master` branch.
- add at least 1 reviewer
- link correct issue
### Contact
If you have any questions or need further assistance, feel free to reach out to us at [scheduler.se](mailto:scheduler.se). We appreciate your contributions and thank you for helping us improve this project!
### License
MIT Licensed. Copyright (c) Bitnoise 2023.