diff --git a/package.json b/package.json index ff212d6..ad58e79 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "react-dnd": "^14.0.5", "react-dnd-html5-backend": "^14.1.0", "react-dom": "^18.3.1", + "react-router-dom": "^6.26.2", "rrule": "^2.8.1" }, "devDependencies": { diff --git a/public/banner.png b/public/banner.png new file mode 100644 index 0000000..0aeea40 Binary files /dev/null and b/public/banner.png differ diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000..485be64 Binary files /dev/null and b/public/logo.png differ diff --git a/public/npm.svg b/public/npm.svg new file mode 100644 index 0000000..6f57583 --- /dev/null +++ b/public/npm.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/examples/AddResource.jsx b/src/examples/AddResource.jsx deleted file mode 100644 index bd67a33..0000000 --- a/src/examples/AddResource.jsx +++ /dev/null @@ -1,162 +0,0 @@ -import React, { Component } from 'react'; -import dayjs from 'dayjs'; -import { SchedulerData, ViewType, DATE_FORMAT, DemoData, Scheduler, wrapperFun } from '../components/index'; -import AddResourceForm from './AddResourceForm'; - -class AddResource extends Component { - constructor(props) { - super(props); - const today = dayjs().format(DATE_FORMAT); - const schedulerData = new SchedulerData(today, ViewType.Week); - schedulerData.localeDayjs.locale('en'); - schedulerData.setResources(DemoData.resources); - schedulerData.setEvents(DemoData.events); - this.state = { - viewModel: schedulerData, - visible: false, - }; - } - - showModal = () => this.setState({ visible: true }); - - handleCancel = () => this.setState({ visible: false }); - - saveFormRef = form => (this.form = form); - - handleCreate = () => { - const { form } = this; - form.validateFields().then(values => { - this.addResource(values.name); - form.resetFields(); - this.setState({ visible: false }); - }); - }; - - render() { - const { viewModel } = this.state; - - const leftCustomHeader = ( -
- - Add a resource - - -
- ); - - return ( -
-
-

Add resource

- -
-
- ); - } - - prevClick = schedulerData => { - schedulerData.prev(); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - nextClick = schedulerData => { - schedulerData.next(); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - onViewChange = (schedulerData, view) => { - schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - onSelectDate = (schedulerData, date) => { - schedulerData.setDate(date); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - eventClicked = (schedulerData, event) => { - alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops1 = (schedulerData, event) => { - alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops2 = (schedulerData, event) => { - alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { - if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { - let newFreshId = 0; - schedulerData.events.forEach(item => { - if (item.id >= newFreshId) newFreshId = item.id + 1; - }); - - const newEvent = { id: newFreshId, title: 'New event you just created', start, end, resourceId: slotId, bgColor: 'purple' }; - schedulerData.addEvent(newEvent); - this.setState({ viewModel: schedulerData }); - } - }; - - updateEventStart = (schedulerData, event, newStart) => { - if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { - schedulerData.updateEventStart(event, newStart); - } - this.setState({ viewModel: schedulerData }); - }; - - updateEventEnd = (schedulerData, event, newEnd) => { - if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { - schedulerData.updateEventEnd(event, newEnd); - } - this.setState({ viewModel: schedulerData }); - }; - - moveEvent = (schedulerData, event, slotId, slotName, start, end) => { - if ( - confirm( - `Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}` - ) - ) { - schedulerData.moveEvent(event, slotId, slotName, start, end); - this.setState({ viewModel: schedulerData }); - } - }; - - addResource = resourceName => { - const schedulerData = this.state.viewModel; - const newFreshId = schedulerData.resources.length + 1; - const newFreshName = resourceName; - schedulerData.addResource({ id: newFreshId, name: newFreshName }); - this.setState({ viewModel: schedulerData }); - }; - - toggleExpandFunc = (schedulerData, slotId) => { - schedulerData.toggleExpandStatus(slotId); - this.setState({ viewModel: schedulerData }); - }; -} - -export default wrapperFun(AddResource); diff --git a/src/examples/AddResourceForm.jsx b/src/examples/AddResourceForm.jsx deleted file mode 100644 index 7353e7b..0000000 --- a/src/examples/AddResourceForm.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { forwardRef } from 'react'; -import { Modal, Form, Input } from 'antd'; - -const FormItem = Form.Item; - -const AddResourceForm = forwardRef((props, ref) => { - const { visible, onCancel, onCreate } = props; - return ( - -
- - - -
-
- ); -}); - -export default AddResourceForm; diff --git a/src/examples/CellUnits.jsx b/src/examples/CellUnits.jsx deleted file mode 100644 index 176e7d2..0000000 --- a/src/examples/CellUnits.jsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, { Component } from 'react'; -import { Scheduler, SchedulerData, ViewType, CellUnit, DemoData, wrapperFun, DATE_FORMAT } from '../components/index'; - -class CellUnitComponent extends Component { - constructor(props) { - super(props); - - const schedulerData = new SchedulerData( - '2022-12-10', - ViewType.Custom, - false, - false, - { - customCellWidth: 150, - nonAgendaDayCellHeaderFormat: 'M/D|HH:mm', - views: [ - { viewName: 'Week', viewType: ViewType.Custom, showAgenda: false, isEventPerspective: false }, - { viewName: 'Month', viewType: ViewType.Custom1, showAgenda: false, isEventPerspective: false }, - { viewName: 'Year', viewType: ViewType.Custom2, showAgenda: false, isEventPerspective: false }, - ], - }, - { getCustomDateFunc: this.getCustomDate } - ); - schedulerData.localeDayjs.locale('en'); - schedulerData.setResources(DemoData.resources); - schedulerData.setEvents(DemoData.events); - this.state = { viewModel: schedulerData }; - } - - render() { - const { viewModel } = this.state; - return ( -
-

Custom time window

- -
- ); - } - - prevClick = schedulerData => { - schedulerData.prev(); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - nextClick = schedulerData => { - schedulerData.next(); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - onViewChange = (schedulerData, view) => { - schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); - schedulerData.config.customCellWidth = view.viewType === ViewType.Custom ? 30 : 80; - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - onSelectDate = (schedulerData, date) => { - schedulerData.setDate(date); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - getCustomDate = (schedulerData, num, date = undefined) => { - const { viewType } = schedulerData; - let selectDate = schedulerData.startDate; - if (date !== undefined) selectDate = date; - - let startDate = num === 0 - ? selectDate - : schedulerData - .localeDayjs(selectDate) - .add(2 * num, 'days') - .format(DATE_FORMAT); - let endDate = schedulerData.localeDayjs(startDate).add(1, 'week').format(DATE_FORMAT); - let cellUnit = CellUnit.Day; - if (viewType === ViewType.Custom) { - const monday = schedulerData.localeDayjs(selectDate).startOf('week').format(DATE_FORMAT); - startDate = num === 0 - ? monday - : schedulerData - .localeDayjs(monday) - .add(2 * num, 'weeks') - .format(DATE_FORMAT); - endDate = schedulerData.localeDayjs(startDate).add(12, 'months').endOf('week').format(DATE_FORMAT); - cellUnit = CellUnit.Week; - } else if (viewType === ViewType.Custom1) { - const firstDayOfMonth = schedulerData.localeDayjs(selectDate).startOf('month').format(DATE_FORMAT); - startDate = num === 0 - ? firstDayOfMonth - : schedulerData - .local2eMoment(firstDayOfMonth) - .add(2 * num, 'months') - .format(DATE_FORMAT); - endDate = schedulerData.localeDayjs(startDate).add(16, 'months').endOf('month').format(DATE_FORMAT); - cellUnit = CellUnit.Month; - } else if (viewType === ViewType.Custom2) { - const firstDayOfMonth = schedulerData.localeDayjs(selectDate).startOf('month').format(DATE_FORMAT); - startDate = num === 0 - ? firstDayOfMonth - : schedulerData - .localeDayjs(firstDayOfMonth) - .add(2 * num, 'months') - .format(DATE_FORMAT); - endDate = schedulerData.localeDayjs(startDate).add(60, 'months').endOf('month').format(DATE_FORMAT); - cellUnit = CellUnit.Year; - } - return { startDate, endDate, cellUnit }; - }; -} - -export default wrapperFun(CellUnitComponent); diff --git a/src/examples/ComingSoon.jsx b/src/examples/ComingSoon.jsx deleted file mode 100644 index a41f5c7..0000000 --- a/src/examples/ComingSoon.jsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import { Result } from 'antd'; - -function ComingSoon() { - return ; -} - -export default ComingSoon; diff --git a/src/examples/CustomEventStyle.jsx b/src/examples/CustomEventStyle.jsx deleted file mode 100644 index 06a79bc..0000000 --- a/src/examples/CustomEventStyle.jsx +++ /dev/null @@ -1,177 +0,0 @@ -import React, { Component } from 'react'; -import { SchedulerData, ViewType, DemoData, Scheduler, wrapperFun } from '../components/index'; - -class CustomEventStyle extends Component { - constructor(props) { - super(props); - - const schedulerData = new SchedulerData('2022-12-18', ViewType.Week, false, false, { - views: [ - { viewName: 'Day(Agenda)', viewType: ViewType.Day, showAgenda: true, isEventPerspective: false }, - { viewName: 'Week', viewType: ViewType.Week, showAgenda: false, isEventPerspective: false }, - { viewName: 'Month(TaskView)', viewType: ViewType.Month, showAgenda: false, isEventPerspective: true }, - { viewName: 'Year', viewType: ViewType.Year, showAgenda: false, isEventPerspective: false }, - ], - }); - schedulerData.localeDayjs.locale('en'); - schedulerData.setResources(DemoData.resources); - schedulerData.setEvents(DemoData.eventsForCustomEventStyle); - this.state = { - viewModel: schedulerData, - }; - } - - render() { - const { viewModel } = this.state; - return ( -
-
-

Custom event style

- -
-
- ); - } - - prevClick = schedulerData => { - schedulerData.prev(); - schedulerData.setEvents(DemoData.eventsForCustomEventStyle); - this.setState({ - viewModel: schedulerData, - }); - }; - - nextClick = schedulerData => { - schedulerData.next(); - schedulerData.setEvents(DemoData.eventsForCustomEventStyle); - this.setState({ - viewModel: schedulerData, - }); - }; - - onViewChange = (schedulerData, view) => { - schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); - schedulerData.setEvents(DemoData.eventsForCustomEventStyle); - this.setState({ - viewModel: schedulerData, - }); - }; - - onSelectDate = (schedulerData, date) => { - schedulerData.setDate(date); - schedulerData.setEvents(DemoData.eventsForCustomEventStyle); - this.setState({ - viewModel: schedulerData, - }); - }; - - eventClicked = (schedulerData, event) => { - alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops1 = (schedulerData, event) => { - alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops2 = (schedulerData, event) => { - alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { - if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { - let newFreshId = 0; - schedulerData.eventsForCustomEventStyle?.forEach(item => { - if (item.id >= newFreshId) newFreshId = item.id + 1; - }); - - const newEvent = { - id: newFreshId, - title: 'New event you just created', - start, - end, - resourceId: slotId, - bgColor: 'purple', - }; - schedulerData.addEvent(newEvent); - this.setState({ - viewModel: schedulerData, - }); - } - }; - - updateEventStart = (schedulerData, event, newStart) => { - if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { - schedulerData.updateEventStart(event, newStart); - } - this.setState({ - viewModel: schedulerData, - }); - }; - - updateEventEnd = (schedulerData, event, newEnd) => { - if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { - schedulerData.updateEventEnd(event, newEnd); - } - this.setState({ - viewModel: schedulerData, - }); - }; - - moveEvent = (schedulerData, event, slotId, slotName, start, end) => { - if ( - confirm( - `Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}` - ) - ) { - schedulerData.moveEvent(event, slotId, slotName, start, end); - this.setState({ - viewModel: schedulerData, - }); - } - }; - - eventItemTemplateResolver = (schedulerData, event, bgColor, isStart, isEnd, mustAddCssClass, mustBeHeight, agendaMaxEventWidth) => { - const borderWidth = isStart ? '4' : '0'; - let borderColor = 'rgba(0,139,236,1)'; - let backgroundColor = '#80C5F6'; - const titleText = schedulerData.behaviors.getEventTextFunc(schedulerData, event); - if (event.type) { - borderColor = event.type === 1 ? 'rgba(0,139,236,1)' : event.type === 3 ? 'rgba(245,60,43,1)' : '#999'; - backgroundColor = event.type === 1 ? '#80C5F6' : event.type === 3 ? '#FA9E95' : '#D9D9D9'; - } - let divStyle = { borderLeft: `${borderWidth}px solid ${borderColor}`, backgroundColor, height: mustBeHeight }; - if (agendaMaxEventWidth) divStyle = { ...divStyle, maxWidth: agendaMaxEventWidth }; - - return ( -
- {titleText} -
- ); - }; - - toggleExpandFunc = (schedulerData, slotId) => { - schedulerData.toggleExpandStatus(slotId); - this.setState({ - viewModel: schedulerData, - }); - }; -} - -export default wrapperFun(CustomEventStyle); diff --git a/src/examples/CustomHeader.jsx b/src/examples/CustomHeader.jsx deleted file mode 100644 index be0eb5f..0000000 --- a/src/examples/CustomHeader.jsx +++ /dev/null @@ -1,164 +0,0 @@ -import React, { Component } from 'react'; -import { Scheduler, SchedulerData, ViewType, DemoData, wrapperFun } from '../components/index'; - -class CustomHeader extends Component { - constructor(props) { - super(props); - - const schedulerData = new SchedulerData('2022-12-18', ViewType.Week); - schedulerData.localeDayjs.locale('en'); - schedulerData.setResources(DemoData.resources); - schedulerData.setEvents(DemoData.events); - this.state = { - viewModel: schedulerData, - }; - } - - render() { - const { viewModel } = this.state; - - const leftCustomHeader = ( -
- Put your content here -
- ); - const rightCustomHeader = ( -
- or here -
- ); - - return ( -
-
-

Custom header

- -
-
- ); - } - - prevClick = schedulerData => { - schedulerData.prev(); - schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); - }; - - nextClick = schedulerData => { - schedulerData.next(); - schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); - }; - - onViewChange = (schedulerData, view) => { - schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); - schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); - }; - - onSelectDate = (schedulerData, date) => { - schedulerData.setDate(date); - schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); - }; - - eventClicked = (schedulerData, event) => { - alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops1 = (schedulerData, event) => { - alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops2 = (schedulerData, event) => { - alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { - if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { - let newFreshId = 0; - schedulerData.events.forEach(item => { - if (item.id >= newFreshId) newFreshId = item.id + 1; - }); - - const newEvent = { - id: newFreshId, - title: 'New event you just created', - start, - end, - resourceId: slotId, - bgColor: 'purple', - }; - schedulerData.addEvent(newEvent); - this.setState({ - viewModel: schedulerData, - }); - } - }; - - updateEventStart = (schedulerData, event, newStart) => { - if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { - schedulerData.updateEventStart(event, newStart); - } - this.setState({ - viewModel: schedulerData, - }); - }; - - updateEventEnd = (schedulerData, event, newEnd) => { - if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { - schedulerData.updateEventEnd(event, newEnd); - } - this.setState({ - viewModel: schedulerData, - }); - }; - - moveEvent = (schedulerData, event, slotId, slotName, start, end) => { - if ( - confirm( - `Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}` - ) - ) { - schedulerData.moveEvent(event, slotId, slotName, start, end); - this.setState({ - viewModel: schedulerData, - }); - } - }; - - toggleExpandFunc = (schedulerData, slotId) => { - schedulerData.toggleExpandStatus(slotId); - this.setState({ - viewModel: schedulerData, - }); - }; -} - -export default wrapperFun(CustomHeader); diff --git a/src/examples/CustomPopoverStyle.jsx b/src/examples/CustomPopoverStyle.jsx deleted file mode 100644 index 99d4fea..0000000 --- a/src/examples/CustomPopoverStyle.jsx +++ /dev/null @@ -1,198 +0,0 @@ -import React, { Component } from 'react'; -import { Col, Row, Button } from 'antd'; -import { Scheduler, SchedulerData, ViewType, DemoData, wrapperFun } from '../components/index'; - -class CustomPopoverStyle extends Component { - constructor(props) { - super(props); - - const schedulerData = new SchedulerData('2022-12-18', ViewType.Week); - schedulerData.localeDayjs.locale('en'); - schedulerData.setResources(DemoData.resources); - schedulerData.setEvents(DemoData.events); - this.state = { - viewModel: schedulerData, - }; - } - - render() { - const { viewModel } = this.state; - return ( -
-
-

Custom popover style example

- -
-
- ); - } - - prevClick = schedulerData => { - schedulerData.prev(); - schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); - }; - - nextClick = schedulerData => { - schedulerData.next(); - schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); - }; - - onViewChange = (schedulerData, view) => { - schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); - schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); - }; - - onSelectDate = (schedulerData, date) => { - schedulerData.setDate(date); - schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); - }; - - eventClicked = (schedulerData, event) => { - alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops1 = (schedulerData, event) => { - alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops2 = (schedulerData, event) => { - alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { - if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { - let newFreshId = 0; - schedulerData.events.forEach(item => { - if (item.id >= newFreshId) newFreshId = item.id + 1; - }); - - const newEvent = { - id: newFreshId, - title: 'New event you just created', - start, - end, - resourceId: slotId, - bgColor: 'purple', - }; - schedulerData.addEvent(newEvent); - this.setState({ - viewModel: schedulerData, - }); - } - }; - - updateEventStart = (schedulerData, event, newStart) => { - if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { - schedulerData.updateEventStart(event, newStart); - } - this.setState({ - viewModel: schedulerData, - }); - }; - - updateEventEnd = (schedulerData, event, newEnd) => { - if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { - schedulerData.updateEventEnd(event, newEnd); - } - this.setState({ - viewModel: schedulerData, - }); - }; - - moveEvent = (schedulerData, event, slotId, slotName, start, end) => { - if ( - confirm( - `Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}` - ) - ) { - schedulerData.moveEvent(event, slotId, slotName, start, end); - this.setState({ - viewModel: schedulerData, - }); - } - }; - - eventItemPopoverTemplateResolver = (schedulerData, eventItem, title, start, end, statusColor) => ( -
- - -
- - - - {title} - - - - - -
- - - - {start.format('HH:mm')} - {' '} - - - {end.format('HH:mm')} - - - - - -
- - - - - -
- ); - - demoButtonClicked = eventItem => { - alert(`You just clicked demo button. event title: ${eventItem.title}`); - }; - - toggleExpandFunc = (schedulerData, slotId) => { - schedulerData.toggleExpandStatus(slotId); - this.setState({ - viewModel: schedulerData, - }); - }; -} - -export default wrapperFun(CustomPopoverStyle); diff --git a/src/examples/OverlapCheck.jsx b/src/examples/OverlapCheck.jsx deleted file mode 100644 index 7d281ef..0000000 --- a/src/examples/OverlapCheck.jsx +++ /dev/null @@ -1,142 +0,0 @@ -import React, { Component } from 'react'; -import { Scheduler, SchedulerData, ViewType, DemoData, wrapperFun } from '../components/index'; -import '../css/style.css'; - -class OverlapCheck extends Component { - constructor(props) { - super(props); - - const schedulerData = new SchedulerData('2022-12-18', ViewType.Week, false, false, { checkConflict: true }); - schedulerData.localeDayjs.locale('en'); - schedulerData.setResources(DemoData.resources); - schedulerData.setEvents(DemoData.events); - this.state = { viewModel: schedulerData }; - } - - render() { - const { viewModel } = this.state; - return ( -
-

Overlap check

- -
- ); - } - - prevClick = schedulerData => { - schedulerData.prev(); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - nextClick = schedulerData => { - schedulerData.next(); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - onViewChange = (schedulerData, view) => { - schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - onSelectDate = (schedulerData, date) => { - schedulerData.setDate(date); - schedulerData.setEvents(DemoData.events); - this.setState({ viewModel: schedulerData }); - }; - - eventClicked = (schedulerData, event) => { - alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops1 = (schedulerData, event) => { - alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - ops2 = (schedulerData, event) => { - alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); - }; - - newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { - if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { - let newFreshId = 0; - schedulerData.events.forEach(item => { - if (item.id >= newFreshId) newFreshId = item.id + 1; - }); - - const newEvent = { - id: newFreshId, - title: 'New event you just created', - start, - end, - resourceId: slotId, - bgColor: 'purple', - }; - schedulerData.addEvent(newEvent); - this.setState({ - viewModel: schedulerData, - }); - } - }; - - updateEventStart = (schedulerData, event, newStart) => { - if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { - schedulerData.updateEventStart(event, newStart); - } - this.setState({ - viewModel: schedulerData, - }); - }; - - updateEventEnd = (schedulerData, event, newEnd) => { - if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { - schedulerData.updateEventEnd(event, newEnd); - } - this.setState({ - viewModel: schedulerData, - }); - }; - - moveEvent = (schedulerData, event, slotId, slotName, start, end) => { - if ( - confirm( - `Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}` - ) - ) { - schedulerData.moveEvent(event, slotId, slotName, start, end); - this.setState({ - viewModel: schedulerData, - }); - } - }; - - conflictOccurred = (schedulerData, action, event, type, slotId, slotName, start, end) => { - alert(`Conflict occurred. {action: ${action}, event: ${event}`); - }; - - toggleExpandFunc = (schedulerData, slotId) => { - schedulerData.toggleExpandStatus(slotId); - this.setState({ viewModel: schedulerData }); - }; -} - -export default wrapperFun(OverlapCheck); diff --git a/src/examples/app.jsx b/src/examples/app.jsx new file mode 100644 index 0000000..d64920c --- /dev/null +++ b/src/examples/app.jsx @@ -0,0 +1,81 @@ +import { Result } from 'antd'; +import React, { Suspense, lazy } from 'react'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; + +// Library Style +import '../css/style.css'; + +import './css/style.css'; + +import Fallback from './components/Fallback.jsx'; +import Landing from './components/Landing.jsx'; + +const Home = lazy(() => import('./pages/Home')); +const Basic = lazy(() => import('./pages/Basic')); +const ReadOnly = lazy(() => import('./pages/Read-Only')); +const AddMore = lazy(() => import('./pages/Add-More')); +const DragAndDrop = lazy(() => import('./pages/Drag-And-Drop')); + + +function App() { + const router = createBrowserRouter([ + { + path: '/', + element: , + children: [ + { + path: '/', + element: ( + }> + + + ), + }, + { + path: '/basic', + element: ( + }> + + + ), + }, + { + path: '/read-only', + element: ( + }> + + + ), + }, + { + path: '/add-more', + element: ( + }> + + + ), + }, + { + path: '/drag-and-drop', + element: ( + }> + + + ), + }, + { + path: '*', + element: , + }, + ], + }, + { + path: '*', + element: , + }, + ]); + + return ; +} + +export default App; diff --git a/src/examples/components/Fallback.jsx b/src/examples/components/Fallback.jsx new file mode 100644 index 0000000..bee5bf4 --- /dev/null +++ b/src/examples/components/Fallback.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Spin } from 'antd'; +import { LoadingOutlined } from '@ant-design/icons'; + +const antIcon = ; + +const Fallback = () => { + return ( +
+ +

Please wait while the component is being loaded.

+
+ ); +}; + +export default Fallback; diff --git a/src/examples/components/Header.jsx b/src/examples/components/Header.jsx new file mode 100644 index 0000000..9d3636f --- /dev/null +++ b/src/examples/components/Header.jsx @@ -0,0 +1,26 @@ +import { GithubOutlined } from '@ant-design/icons'; +import { Col, Row } from 'antd'; +import React from 'react'; +import { Link } from 'react-router-dom'; + +function Header() { + return ( + + + + Logo + + + + + npm-logo + + + + + + + ); +} + +export default Header; diff --git a/src/examples/components/Landing.jsx b/src/examples/components/Landing.jsx new file mode 100644 index 0000000..5a58bf2 --- /dev/null +++ b/src/examples/components/Landing.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Layout } from 'antd'; +import { Outlet } from 'react-router-dom'; +import Header from './Header'; +import Slider from './Slider'; + +function Landing() { + return ( + + +
+ + + + + + + + + + + ); +} + +export default Landing; diff --git a/src/examples/components/ResourceItem.jsx b/src/examples/components/ResourceItem.jsx new file mode 100644 index 0000000..e25cc42 --- /dev/null +++ b/src/examples/components/ResourceItem.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +function ResourceItem({ resource, isDragging, connectDragSource, connectDragPreview }) { + const dragContent =
  • {resource.name}
  • ; + + return isDragging ? null :
    {connectDragPreview(connectDragSource(dragContent))}
    ; +} + +export default ResourceItem; diff --git a/src/examples/components/ResourceList.jsx b/src/examples/components/ResourceList.jsx new file mode 100644 index 0000000..d939ae2 --- /dev/null +++ b/src/examples/components/ResourceList.jsx @@ -0,0 +1,16 @@ +import React from 'react'; + +function ResourceList({ schedulerData, newEvent, resourceDndSource }) { + const DnDResourceItem = resourceDndSource.getDragSource(); + const resources = schedulerData.resources; + + return ( +
      + {resources.map(resource => ( + + ))} +
    + ); +} + +export default ResourceList; diff --git a/src/examples/components/Slider.jsx b/src/examples/components/Slider.jsx new file mode 100644 index 0000000..0908aed --- /dev/null +++ b/src/examples/components/Slider.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { Menu } from 'antd'; +import { useLocation, useNavigate } from 'react-router-dom'; + +const items = [ + { label: 'Home', key: 'home', path: '/' }, + { label: 'Basic', key: 'basic', path: '/basic' }, + { label: 'Read Only', key: 'read-only', path: '/read-only' }, + { label: 'Add More', key: 'add-more', path: '/add-more' }, + { label: 'Drag and Drop', key: 'drag-and-drop', path: '/drag-and-drop' }, +]; + +function Slider() { + const navigate = useNavigate(); + const { pathname } = useLocation(); + const activePath = pathname?.split('/')[1] || 'home'; + return ({ ...i, onClick: () => navigate(i.path) }))} />; +} + +export default Slider; diff --git a/src/examples/components/SourceCode.jsx b/src/examples/components/SourceCode.jsx new file mode 100644 index 0000000..ddd1552 --- /dev/null +++ b/src/examples/components/SourceCode.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Row } from 'antd'; + +function SourceCode({ value }) { + return ( + + + </> Source Code + + + ); +} + +export default SourceCode; diff --git a/src/examples/components/TaskItem.jsx b/src/examples/components/TaskItem.jsx new file mode 100644 index 0000000..9a1d6a8 --- /dev/null +++ b/src/examples/components/TaskItem.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +function TaskItem({ task, isDragging, connectDragSource, connectDragPreview }) { + const dragContent =
  • {task.name}
  • ; + + return isDragging ? null : <>{connectDragPreview(connectDragSource(dragContent))}; +} + +export default TaskItem; diff --git a/src/examples/components/TaskList.jsx b/src/examples/components/TaskList.jsx new file mode 100644 index 0000000..1a3c0a2 --- /dev/null +++ b/src/examples/components/TaskList.jsx @@ -0,0 +1,16 @@ +import React from 'react'; + +function TaskList({ schedulerData, newEvent, taskDndSource }) { + const DnDTaskItem = taskDndSource.getDragSource(); + const tasks = schedulerData.eventGroups; + + return ( +
      + {tasks?.map(task => ( + + ))} +
    + ); +} + +export default TaskList; diff --git a/src/examples/css/style.css b/src/examples/css/style.css new file mode 100644 index 0000000..41e59f5 --- /dev/null +++ b/src/examples/css/style.css @@ -0,0 +1,164 @@ +/* Default Styling */ + +@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); + +body { + margin: 0px; + font-family: 'Roboto', sans-serif; + font-size: 16px; + max-height: 100vh; + overflow: hidden; +} + +.m-0{ + margin: 0px; +} + +header { + text-align: center; + margin-bottom: 40px; +} + +.ant-layout-has-sider { + margin-bottom: 2px; +} + +.main-layout { + max-height: 100vh; + height: 100vh; + width: 100vw; + max-width: 100vw; + overflow: hidden; +} +.main-header { + background-color: #fff; + position: static !important; + margin-bottom: 0px; +} + +.main-content { + margin: 10px; + max-width: 100%; +} + +.ant-layout-header { + padding-inline: 30px; +} + +h1.header-title { + margin: 0px; + font-size: 2rem; + margin-bottom: 20px; +} + +.main_description { + .ant-typography { + font-weight: 500; + font-size: 1.25rem; + } +} + +.ant-layout-content { + height: 100%; + overflow: auto; +} + +::-webkit-scrollbar { + width: 0; + height: 0; +} + +::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +::-webkit-scrollbar-track:hover { + background-color: #eaeaea; +} + +::-webkit-scrollbar-thumb { + background-color: #888; +} + +::-webkit-scrollbar-thumb:hover { + background-color: #555; +} + +/* Header */ +.header-wrapper { + max-height: 64px; + overflow: hidden; +} + +.logo_img { + height: 60px; + cursor: pointer; +} + +.github-icon, +.npm-icon { + color: black; +} +.github-icon:hover { + color: gray; +} + +.github-icon svg, +.npm-icon img { + width: 35px; + height: 35px; +} + +.npm-icon { + margin-right: 20px; +} + +.title { + margin-bottom: 16px; + font-size: 32px; + font-weight: 700; +} + +.paragraph { + margin-bottom: 24px; + font-size: 18px; +} + +.button { + margin-top: 24px; +} + +section { + margin-bottom: 40px; +} + +ul { + list-style-type: disc; + margin-left: 24px; + margin-bottom: 16px; + font-size: 16px; +} + +ol { + list-style-type: decimal; + margin-left: 24px; + margin-bottom: 16px; + font-size: 16px; +} + +.pre { + background-color: #f7f7f7; + padding: 16px; + border-radius: 4px; +} + +.code { + font-family: monospace; +} + +/* Home page */ +.home-page { + max-width: 50vw; + margin: 0 auto; + padding: 10px 40px; +} diff --git a/src/examples/helpers/DnDTypes.js b/src/examples/helpers/DnDTypes.js new file mode 100644 index 0000000..beb4a01 --- /dev/null +++ b/src/examples/helpers/DnDTypes.js @@ -0,0 +1 @@ +export const DnDTypes = { TASK: 'task', RESOURCE: 'resource' }; \ No newline at end of file diff --git a/src/examples/index.jsx b/src/examples/index.jsx index bab5cc8..5a99f91 100644 --- a/src/examples/index.jsx +++ b/src/examples/index.jsx @@ -1,6 +1,5 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; -import Example from './Basic'; -import '../css/style.css'; +import App from './app'; -createRoot(document.getElementById('app')).render(); +createRoot(document.getElementById('app')).render(); diff --git a/src/examples/AddMore.jsx b/src/examples/pages/Add-More/class-based.jsx similarity index 94% rename from src/examples/AddMore.jsx rename to src/examples/pages/Add-More/class-based.jsx index a1bdfb7..7a854b7 100644 --- a/src/examples/AddMore.jsx +++ b/src/examples/pages/Add-More/class-based.jsx @@ -1,11 +1,13 @@ import React, { Component } from 'react'; -import { Scheduler, SchedulerData, ViewType, AddMorePopover, DemoData, wrapperFun } from '../components/index'; + +import { Scheduler, SchedulerData, ViewType, AddMorePopover, DemoData, wrapperFun } from '../../../index'; class AddMore extends Component { constructor(props) { super(props); - const schedulerData = new SchedulerData('2022-12-18', ViewType.Week, false, false, { + let schedulerData = new SchedulerData('2022-12-18', ViewType.Week, false, false, { + besidesWidth: 350, dayMaxEvents: 2, weekMaxEvents: 4, monthMaxEvents: 4, @@ -116,18 +118,18 @@ class AddMore extends Component { alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); }; - newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { + newEvent = (schedulerData, slotId = '', slotName = '', start = '', end = '', type = '', item = '') => { if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { let newFreshId = 0; schedulerData.events.forEach(item => { if (item.id >= newFreshId) newFreshId = item.id + 1; }); - const newEvent = { + let newEvent = { id: newFreshId, title: 'New event you just created', - start, - end, + start: start, + end: end, resourceId: slotId, bgColor: 'purple', }; diff --git a/src/examples/pages/Add-More/functional-based.jsx b/src/examples/pages/Add-More/functional-based.jsx new file mode 100644 index 0000000..6b86056 --- /dev/null +++ b/src/examples/pages/Add-More/functional-based.jsx @@ -0,0 +1,198 @@ +import React, { useEffect, useReducer, useState } from 'react'; +import { Scheduler, SchedulerData, ViewType, AddMorePopover, DemoData, wrapperFun } from '../../../index'; + +let schedulerData; + +const initialState = { + showScheduler: false, + viewModel: {}, +}; + +function reducer(state, action) { + switch (action.type) { + case 'INITIALIZE': + return { showScheduler: true, viewModel: action.payload }; + case 'UPDATE_SCHEDULER': + return { ...state, viewModel: action.payload }; + default: + return state; + } +} + +function AddMore() { + const [state, dispatch] = useReducer(reducer, initialState); + + const [popoverState, setPopoverState] = useState({ + headerItem: undefined, + left: 0, + top: 0, + height: 0, + }); + + useEffect(() => { + schedulerData = new SchedulerData('2022-12-18', ViewType.Week, false, false, { + besidesWidth: 350, + dayMaxEvents: 2, + weekMaxEvents: 4, + monthMaxEvents: 4, + quarterMaxEvents: 4, + yearMaxEvents: 4, + }); + schedulerData.localeDayjs.locale('en'); + schedulerData.setResources(DemoData.resources); + schedulerData.setEvents(DemoData.events); + + dispatch({ type: 'INITIALIZE', payload: schedulerData }); + }, []); + + const prevClick = schedulerData => { + schedulerData.prev(); + schedulerData.setEvents(DemoData.events); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const nextClick = schedulerData => { + schedulerData.next(); + schedulerData.setEvents(DemoData.events); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const onViewChange = (schedulerData, view) => { + schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); + schedulerData.setEvents(DemoData.events); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const onSelectDate = (schedulerData, date) => { + schedulerData.setDate(date); + schedulerData.setEvents(DemoData.events); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const eventClicked = (schedulerData, event) => { + alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); + }; + + const ops1 = (schedulerData, event) => { + alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); + }; + + const ops2 = (schedulerData, event) => { + alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); + }; + + const newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { + if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { + let newFreshId = 0; + schedulerData.events.forEach(item => { + if (item.id >= newFreshId) newFreshId = item.id + 1; + }); + + let newEvent = { + id: newFreshId, + title: 'New event you just created', + start: start, + end: end, + resourceId: slotId, + bgColor: 'purple', + }; + schedulerData.addEvent(newEvent); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + } + }; + + const updateEventStart = (schedulerData, event, newStart) => { + if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { + schedulerData.updateEventStart(event, newStart); + } + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const updateEventEnd = (schedulerData, event, newEnd) => { + if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { + schedulerData.updateEventEnd(event, newEnd); + } + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const moveEvent = (schedulerData, event, slotId, slotName, start, end) => { + if ( + confirm( + `Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}` + ) + ) { + schedulerData.moveEvent(event, slotId, slotName, start, end); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + } + }; + + const onSetAddMoreState = newState => { + if (newState === undefined) { + setPopoverState({ + headerItem: undefined, + left: 0, + top: 0, + height: 0, + }); + } else { + setPopoverState({ + ...newState, + }); + } + }; + + const toggleExpandFunc = (schedulerData, slotId) => { + schedulerData.toggleExpandStatus(slotId); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + let popover =
    ; + if (popoverState.headerItem !== undefined) { + popover = ( + + ); + } + + return ( + <> + {state.showScheduler && ( +
    + + {popover} +
    + )} + + ); +} + +export default wrapperFun(AddMore); diff --git a/src/examples/pages/Add-More/index.jsx b/src/examples/pages/Add-More/index.jsx new file mode 100644 index 0000000..eb67bd8 --- /dev/null +++ b/src/examples/pages/Add-More/index.jsx @@ -0,0 +1,20 @@ +import { Row, Typography } from 'antd'; +import React from 'react'; +import ClassBased from './class-based'; +import SourceCode from '../../components/SourceCode'; + +function Basic() { + return ( + <> + + + Add More Example + + + + + + ); +} + +export default Basic; diff --git a/src/examples/Basic.jsx b/src/examples/pages/Basic/class-based.jsx similarity index 80% rename from src/examples/Basic.jsx rename to src/examples/pages/Basic/class-based.jsx index 7e906f8..4e27091 100644 --- a/src/examples/Basic.jsx +++ b/src/examples/pages/Basic/class-based.jsx @@ -1,14 +1,15 @@ -import React, { Component } from 'react'; -import * as dayjsLocale from 'dayjs/locale/pt-br'; import * as antdLocale from 'antd/locale/pt_BR'; +import * as dayjsLocale from 'dayjs/locale/pt-br'; +import React, { Component } from 'react'; -import { Scheduler, SchedulerData, ViewType, DemoData, wrapperFun } from '../index'; +import { DemoData, Scheduler, SchedulerData, ViewType, wrapperFun } from '../../../index'; class Basic extends Component { constructor(props) { super(props); - const schedulerData = new SchedulerData('2022-12-02', ViewType.Month, false, false, { + let schedulerData = new SchedulerData('2022-12-22', ViewType.Week, false, false, { + besidesWidth: 300, dayMaxEvents: 99, weekMaxEvents: 9669, monthMaxEvents: 9669, @@ -16,7 +17,7 @@ class Basic extends Component { yearMaxEvents: 9956, customMaxEvents: 9965, eventItemPopoverTrigger: 'click', - schedulerContentHeight: '350px', + schedulerContentHeight: '100%', }); schedulerData.setSchedulerLocale(dayjsLocale); @@ -37,7 +38,6 @@ class Basic extends Component { nextClick={this.nextClick} onSelectDate={this.onSelectDate} onViewChange={this.onViewChange} - // eventItemClick={this.eventClicked} viewEventClick={this.ops1} viewEventText="Ops 1" viewEvent2Text="Ops 2" @@ -73,11 +73,11 @@ class Basic extends Component { schedulerData.setEvents(DemoData.events); this.setState({ viewModel: schedulerData }); function secondsBetween(date1, date2) { - const diff = Math.abs(date1.getTime() - date2.getTime()); + var diff = Math.abs(date1.getTime() - date2.getTime()); return diff / 1000; } - console.log(`Elapsed seconds: ${secondsBetween(start, new Date())}`); + console.log('Elapsed seconds: ' + secondsBetween(start, new Date())); }; onSelectDate = (schedulerData, date) => { @@ -107,18 +107,16 @@ class Basic extends Component { if (item.id >= newFreshId) newFreshId = item.id + 1; }); - const newEvent = { + let newEvent = { id: newFreshId, title: 'New event you just created', - start, - end, + start: start, + end: end, resourceId: slotId, bgColor: 'purple', }; schedulerData.addEvent(newEvent); - this.setState({ - viewModel: schedulerData, - }); + this.setState({ viewModel: schedulerData }); } }; @@ -126,18 +124,14 @@ class Basic extends Component { if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { schedulerData.updateEventStart(event, newStart); } - this.setState({ - viewModel: schedulerData, - }); + this.setState({ viewModel: schedulerData }); }; updateEventEnd = (schedulerData, event, newEnd) => { if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { schedulerData.updateEventEnd(event, newEnd); } - this.setState({ - viewModel: schedulerData, - }); + this.setState({ viewModel: schedulerData }); }; moveEvent = (schedulerData, event, slotId, slotName, start, end) => { @@ -147,9 +141,7 @@ class Basic extends Component { ) ) { schedulerData.moveEvent(event, slotId, slotName, start, end); - this.setState({ - viewModel: schedulerData, - }); + this.setState({ viewModel: schedulerData }); } }; @@ -157,39 +149,29 @@ class Basic extends Component { if (schedulerData.ViewTypes === ViewType.Day) { schedulerData.next(); schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); + this.setState({ viewModel: schedulerData }); schedulerContent.scrollLeft = maxScrollLeft - 10; } }; - onScrollLeft = (schedulerData, schedulerContent, maxScrollLeft) => { + onScrollLeft = (schedulerData, schedulerContent) => { if (schedulerData.ViewTypes === ViewType.Day) { schedulerData.prev(); schedulerData.setEvents(DemoData.events); - this.setState({ - viewModel: schedulerData, - }); + this.setState({ viewModel: schedulerData }); schedulerContent.scrollLeft = 10; } }; - onScrollTop = (schedulerData, schedulerContent, maxScrollTop) => { - console.log('onScrollTop'); - }; + onScrollTop = () => console.log('onScrollTop'); - onScrollBottom = (schedulerData, schedulerContent, maxScrollTop) => { - console.log('onScrollBottom'); - }; + onScrollBottom = () => console.log('onScrollBottom'); toggleExpandFunc = (schedulerData, slotId) => { schedulerData.toggleExpandStatus(slotId); - this.setState({ - viewModel: schedulerData, - }); + this.setState({ viewModel: schedulerData }); }; } diff --git a/src/examples/pages/Basic/index.jsx b/src/examples/pages/Basic/index.jsx new file mode 100644 index 0000000..c7fb357 --- /dev/null +++ b/src/examples/pages/Basic/index.jsx @@ -0,0 +1,18 @@ +import { Row, Typography } from 'antd'; +import React from 'react'; +import ClassBased from './class-based'; +import SourceCode from '../../components/SourceCode'; + +function Basic() { + return ( + <> + + Basic Example + + + + + ); +} + +export default Basic; diff --git a/src/examples/pages/Drag-And-Drop/class-based.jsx b/src/examples/pages/Drag-And-Drop/class-based.jsx new file mode 100644 index 0000000..812a741 --- /dev/null +++ b/src/examples/pages/Drag-And-Drop/class-based.jsx @@ -0,0 +1,209 @@ +import { Col, Row, Typography } from 'antd'; +import React, { Component } from 'react'; +import { DemoData, DnDSource, Scheduler, SchedulerData, ViewType, wrapperFun } from '../../../index'; +import ResourceItem from '../../components/ResourceItem.jsx'; +import ResourceList from '../../components/ResourceList.jsx'; +import TaskItem from '../../components/TaskItem.jsx'; +import TaskList from '../../components/TaskList.jsx'; +import { DnDTypes } from '../../helpers/DnDTypes.js'; + +class DragAndDrop extends Component { + constructor(props) { + super(props); + + let schedulerData = new SchedulerData('2022-12-18', ViewType.Month, false, false, { + schedulerMaxHeight: 500, + besidesWidth: window.innerWidth <= 1600 ? 400 : 500, + views: [ + { viewName: 'Agenda View', viewType: ViewType.Month, showAgenda: true, isEventPerspective: false }, + { viewName: 'Resource View', viewType: ViewType.Month, showAgenda: false, isEventPerspective: false }, + { viewName: 'Task View', viewType: ViewType.Month, showAgenda: false, isEventPerspective: true }, + ], + }); + schedulerData.localeDayjs.locale('en'); + schedulerData.setResources(DemoData.resources); + schedulerData.setEvents(DemoData.eventsForTaskView); + this.state = { + viewModel: schedulerData, + taskDndSource: new DnDSource(props => props.task, TaskItem, true, DnDTypes.TASK), + resourceDndSource: new DnDSource(props => props.resource, ResourceItem, true, DnDTypes.RESOURCE), + }; + } + + render() { + const { viewModel, taskDndSource, resourceDndSource } = this.state; + + return ( +
    + + + {viewModel.isEventPerspective + ? 'Drag a resource from outside and drop to the resource view.' + : 'Drag a task from outside and drop to the resource view'} + + + + + + + + {viewModel.isEventPerspective ? ( + + ) : ( + + )} + + +
    + ); + } + prevClick = schedulerData => { + schedulerData.prev(); + schedulerData.setEvents(DemoData.eventsForTaskView); + this.setState({ + viewModel: schedulerData, + }); + }; + + nextClick = schedulerData => { + schedulerData.next(); + schedulerData.setEvents(DemoData.eventsForTaskView); + this.setState({ + viewModel: schedulerData, + }); + }; + + onViewChange = (schedulerData, view) => { + schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); + schedulerData.config.creatable = !view.isEventPerspective; + schedulerData.setEvents(DemoData.eventsForTaskView); + this.setState({ + viewModel: schedulerData, + }); + }; + + onSelectDate = (schedulerData, date) => { + schedulerData.setDate(date); + schedulerData.setEvents(DemoData.eventsForTaskView); + this.setState({ + viewModel: schedulerData, + }); + }; + + eventClicked = (schedulerData, event) => { + alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); + }; + + ops1 = (schedulerData, event) => { + alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); + }; + + ops2 = (schedulerData, event) => { + alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); + }; + + newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { + if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { + let newFreshId = 0; + schedulerData.events.forEach(item => { + if (item.id >= newFreshId) newFreshId = item.id + 1; + }); + + let newEvent = { + id: newFreshId, + title: 'New event you just created', + start: start, + end: end, + resourceId: slotId, + bgColor: 'purple', + }; + + if (type === DnDTypes.RESOURCE) { + newEvent = { + ...newEvent, + groupId: slotId, + groupName: slotName, + resourceId: item.id, + }; + } else if (type === DnDTypes.TASK) { + newEvent = { + ...newEvent, + groupId: item.id, + groupName: item.name, + }; + } + + schedulerData.addEvent(newEvent); + this.setState({ + viewModel: schedulerData, + }); + } + }; + + updateEventStart = (schedulerData, event, newStart) => { + if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { + schedulerData.updateEventStart(event, newStart); + } + this.setState({ + viewModel: schedulerData, + }); + }; + + updateEventEnd = (schedulerData, event, newEnd) => { + if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { + schedulerData.updateEventEnd(event, newEnd); + } + this.setState({ + viewModel: schedulerData, + }); + }; + + moveEvent = (schedulerData, event, slotId, slotName, start, end) => { + if ( + confirm( + `Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}` + ) + ) { + schedulerData.moveEvent(event, slotId, slotName, start, end); + this.setState({ + viewModel: schedulerData, + }); + } + }; + + movingEvent = (schedulerData, slotId, slotName, newStart, newEnd, action, type, item) => { + console.log('moving event', schedulerData, slotId, slotName, newStart, newEnd, action, type, item); + }; + + subtitleGetter = (schedulerData, event) => { + return schedulerData.isEventPerspective ? schedulerData.getResourceById(event.resourceId).name : event.groupName; + }; + + toggleExpandFunc = (schedulerData, slotId) => { + schedulerData.toggleExpandStatus(slotId); + this.setState({ + viewModel: schedulerData, + }); + }; +} + +export default wrapperFun(DragAndDrop); diff --git a/src/examples/pages/Drag-And-Drop/functional-based.jsx b/src/examples/pages/Drag-And-Drop/functional-based.jsx new file mode 100644 index 0000000..06b564f --- /dev/null +++ b/src/examples/pages/Drag-And-Drop/functional-based.jsx @@ -0,0 +1,213 @@ +import { Col, Row, Typography } from 'antd'; +import React, { useEffect, useReducer, useState } from 'react'; +import { DemoData, DnDSource, Scheduler, SchedulerData, ViewType, wrapperFun } from '../../../index'; +import ResourceItem from '../../components/ResourceItem'; +import ResourceList from '../../components/ResourceList'; +import TaskItem from '../../components/TaskItem'; +import TaskList from '../../components/TaskList'; +import { DnDTypes } from '../../helpers/DnDTypes'; + +const initialState = { + showScheduler: false, + viewModel: {}, +}; + +function reducer(state, action) { + switch (action.type) { + case 'INITIALIZE': + return { showScheduler: true, viewModel: action.payload }; + case 'UPDATE_SCHEDULER': + return { ...state, viewModel: action.payload }; + default: + return state; + } +} + +let schedulerData; +function DragAndDrop() { + const [state, dispatch] = useReducer(reducer, initialState); + const [taskDndSource, setTaskDndSource] = useState(new DnDSource(props => props.task, TaskItem, true, DnDTypes.TASK)); + const [resourceDndSource, setResourceDndSource] = useState(new DnDSource(props => props.resource, ResourceItem, true, DnDTypes.RESOURCE)); + + useEffect(() => { + schedulerData = new SchedulerData('2022-12-18', ViewType.Month, false, false, { + schedulerMaxHeight: 500, + besidesWidth: window.innerWidth <= 1600 ? 400 : 500, + views: [ + { viewName: 'Agenda View', viewType: ViewType.Month, showAgenda: true, isEventPerspective: false }, + { viewName: 'Resource View', viewType: ViewType.Month, showAgenda: false, isEventPerspective: false }, + { viewName: 'Task View', viewType: ViewType.Month, showAgenda: false, isEventPerspective: true }, + ], + }); + schedulerData.localeDayjs.locale('en'); + schedulerData.setResources(DemoData.resources); + schedulerData.setEvents(DemoData.eventsForTaskView); + + dispatch({ type: 'INITIALIZE', payload: schedulerData }); + setTaskDndSource(new DnDSource(props => props.task, TaskItem, true, DnDTypes.TASK)); + setResourceDndSource(new DnDSource(props => props.resource, ResourceItem, true, DnDTypes.RESOURCE)); + + return () => { + schedulerData = null; + }; + }, []); + + const prevClick = schedulerData => { + schedulerData.prev(); + schedulerData.setEvents(DemoData.eventsForTaskView); + this.setState({ viewModel: schedulerData }); + }; + + const nextClick = schedulerData => { + schedulerData.next(); + schedulerData.setEvents(DemoData.eventsForTaskView); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const onViewChange = (schedulerData, view) => { + schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); + schedulerData.config.creatable = !view.isEventPerspective; + schedulerData.setEvents(DemoData.eventsForTaskView); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const onSelectDate = (schedulerData, date) => { + schedulerData.setDate(date); + schedulerData.setEvents(DemoData.eventsForTaskView); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const eventClicked = (schedulerData, event) => { + alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); + }; + + const ops1 = (schedulerData, event) => { + alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); + }; + + const ops2 = (schedulerData, event) => { + alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); + }; + + const newEvent = (schedulerData, slotId, slotName, start, end, type, item) => { + if (confirm(`Do you want to create a new event? {slotId: ${slotId}, slotName: ${slotName}, start: ${start}, end: ${end}, type: ${type}, item: ${item}}`)) { + let newFreshId = 0; + schedulerData.events.forEach(item => { + if (item.id >= newFreshId) newFreshId = item.id + 1; + }); + + let newEvent = { + id: newFreshId, + title: 'New event you just created', + start: start, + end: end, + resourceId: slotId, + bgColor: 'purple', + }; + + if (type === DnDTypes.RESOURCE) { + newEvent = { + ...newEvent, + groupId: slotId, + groupName: slotName, + resourceId: item.id, + }; + } else if (type === DnDTypes.TASK) { + newEvent = { + ...newEvent, + groupId: item.id, + groupName: item.name, + }; + } + + schedulerData.addEvent(newEvent); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + } + }; + + const updateEventStart = (schedulerData, event, newStart) => { + if (confirm(`Do you want to adjust the start of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newStart: ${newStart}}`)) { + schedulerData.updateEventStart(event, newStart); + } + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const updateEventEnd = (schedulerData, event, newEnd) => { + if (confirm(`Do you want to adjust the end of the event? {eventId: ${event.id}, eventTitle: ${event.title}, newEnd: ${newEnd}}`)) { + schedulerData.updateEventEnd(event, newEnd); + } + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + const moveEvent = (schedulerData, event, slotId, slotName, start, end) => { + if ( + confirm( + `Do you want to move the event? {eventId: ${event.id}, eventTitle: ${event.title}, newSlotId: ${slotId}, newSlotName: ${slotName}, newStart: ${start}, newEnd: ${end}` + ) + ) { + schedulerData.moveEvent(event, slotId, slotName, start, end); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + } + }; + + const movingEvent = (schedulerData, slotId, slotName, newStart, newEnd, action, type, item) => { + console.log('moving event', schedulerData, slotId, slotName, newStart, newEnd, action, type, item); + }; + + const subtitleGetter = (schedulerData, event) => { + return schedulerData.isEventPerspective ? schedulerData.getResourceById(event.resourceId).name : event.groupName; + }; + + const toggleExpandFunc = (schedulerData, slotId) => { + schedulerData.toggleExpandStatus(slotId); + dispatch({ type: 'UPDATE_SCHEDULER', payload: schedulerData }); + }; + + return ( + <> + {state.showScheduler && ( + <> + + + {state.showScheduler && + (state.viewModel?.isEventPerspective ? 'Drag a resource from outside and drop to the resource view.' : 'Drag a task from outside and drop to the resource view')} + + + + + + + + {state.viewModel.isEventPerspective ? ( + + ) : ( + + )} + + + + )} + + ); +} + +export default wrapperFun(DragAndDrop); diff --git a/src/examples/pages/Drag-And-Drop/index.jsx b/src/examples/pages/Drag-And-Drop/index.jsx new file mode 100644 index 0000000..4cc03b5 --- /dev/null +++ b/src/examples/pages/Drag-And-Drop/index.jsx @@ -0,0 +1,20 @@ +import { Row, Typography } from 'antd'; +import React from 'react'; +import ClassBased from './class-based'; +import SourceCode from '../../components/SourceCode'; + +function Basic() { + return ( + <> + + + Drag and Drop Example + + + + + + ); +} + +export default Basic; diff --git a/src/examples/pages/Home.jsx b/src/examples/pages/Home.jsx new file mode 100644 index 0000000..aa9e820 --- /dev/null +++ b/src/examples/pages/Home.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Typography, Button } from 'antd'; +import { useNavigate } from 'react-router-dom'; + +const { Title, Paragraph } = Typography; + +const Home = () => { + const navigate = useNavigate(); + return ( +
    +
    + React Big Schedule + + React Big Schedule is a powerful and intuitive scheduler and resource planning solution built with React. Seamlessly integrate this modern, browser-compatible component + into your applications to effectively manage time, appointments, and resources. With drag-and-drop functionality, interactive UI, and granular views, React Big Schedule + empowers you to effortlessly schedule and allocate resources with precision. + + +
    +
    + ); +}; + +export default Home; diff --git a/src/examples/pages/Read-Only/class-based.jsx b/src/examples/pages/Read-Only/class-based.jsx new file mode 100644 index 0000000..216aebf --- /dev/null +++ b/src/examples/pages/Read-Only/class-based.jsx @@ -0,0 +1,93 @@ +import React, { Component } from 'react'; + +import { Scheduler, SchedulerData, ViewType, DemoData, wrapperFun } from '../../../index'; + +class Readonly extends Component { + constructor(props) { + super(props); + + let schedulerData = new SchedulerData('2022-12-22', ViewType.Week, false, false, { + besidesWidth: 350, + startResizable: false, + endResizable: false, + movable: false, + creatable: false, + }); + schedulerData.localeDayjs.locale('en'); + schedulerData.setResources(DemoData.resources); + schedulerData.setEvents(DemoData.events); + this.state = { + viewModel: schedulerData, + }; + } + + render() { + const { viewModel } = this.state; + return ( + + ); + } + + prevClick = schedulerData => { + schedulerData.prev(); + schedulerData.setEvents(DemoData.events); + this.setState({ + viewModel: schedulerData, + }); + }; + + nextClick = schedulerData => { + schedulerData.next(); + schedulerData.setEvents(DemoData.events); + this.setState({ + viewModel: schedulerData, + }); + }; + + onViewChange = (schedulerData, view) => { + schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective); + schedulerData.setEvents(DemoData.events); + this.setState({ + viewModel: schedulerData, + }); + }; + + onSelectDate = (schedulerData, date) => { + schedulerData.setDate(date); + schedulerData.setEvents(DemoData.events); + this.setState({ + viewModel: schedulerData, + }); + }; + + eventClicked = (schedulerData, event) => { + alert(`You just clicked an event: {id: ${event.id}, title: ${event.title}}`); + }; + + ops1 = (schedulerData, event) => { + alert(`You just executed ops1 to event: {id: ${event.id}, title: ${event.title}}`); + }; + + ops2 = (schedulerData, event) => { + alert(`You just executed ops2 to event: {id: ${event.id}, title: ${event.title}}`); + }; + + toggleExpandFunc = (schedulerData, slotId) => { + schedulerData.toggleExpandStatus(slotId); + this.setState({ viewModel: schedulerData }); + }; +} + +export default wrapperFun(Readonly); diff --git a/src/examples/pages/Read-Only/index.jsx b/src/examples/pages/Read-Only/index.jsx new file mode 100644 index 0000000..8d6cb04 --- /dev/null +++ b/src/examples/pages/Read-Only/index.jsx @@ -0,0 +1,18 @@ +import { Row, Typography } from 'antd'; +import React from 'react'; +import ClassBased from './class-based'; +import SourceCode from '../../components/SourceCode'; + +function Basic() { + return ( + <> + + Read Only Example + + + + + ); +} + +export default Basic; diff --git a/src/index.html b/src/index.html index 5bde2c5..35c76ae 100644 --- a/src/index.html +++ b/src/index.html @@ -6,6 +6,7 @@ + React Big Schedule diff --git a/src/index.js b/src/index.js index 5bd85d3..bb451cd 100644 --- a/src/index.js +++ b/src/index.js @@ -1,14 +1,14 @@ export { + AddMorePopover, + CellUnit, DATE_FORMAT, DATETIME_FORMAT, + DnDContext, + DnDSource, + DemoData, Scheduler, SchedulerData, - ViewType, - CellUnit, SummaryPos, - DnDSource, - DnDContext, - AddMorePopover, - DemoData, + ViewType, wrapperFun, } from './components/index'; diff --git a/src/main.jsx b/src/main.jsx deleted file mode 100644 index f996a7e..0000000 --- a/src/main.jsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './examples/AddMore'; -import './css/style.css'; - -ReactDOM.createRoot(document.getElementById('root')).render(); diff --git a/webpack/webpack.dev.config.js b/webpack/webpack.dev.config.js index f590e1e..ba1b28a 100644 --- a/webpack/webpack.dev.config.js +++ b/webpack/webpack.dev.config.js @@ -8,6 +8,7 @@ module.exports = { output: { path: path.resolve(__dirname, '..', 'dist'), filename: 'bundle.js', + publicPath: '/', }, module: { rules: [ @@ -20,6 +21,10 @@ module.exports = { test: /\.css$/, use: ['style-loader', 'css-loader'], }, + { + test: /\.(png|jpg|jpeg|gif|svg)$/i, + type: 'asset/resource', + }, ], }, resolve: { @@ -27,11 +32,12 @@ module.exports = { }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', title: 'React Big Schedule' }), - new ESLintWebpackPlugin({ emitError: true, emitWarning: false, failOnError: true, extensions: ['js', 'jsx'] }), + new ESLintWebpackPlugin({ emitError: false, emitWarning: false, failOnError: false, extensions: ['js', 'jsx'] }), ], devServer: { - static: { directory: path.join(__dirname, '..', 'dist') }, + static: { directory: path.join(__dirname, '..', 'public') }, compress: true, port: 8080, + historyApiFallback: true, }, };