---
title: "React Timeline Role-Based Access Example | Mobiscroll"
component: "Timeline"
framework: "React"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/react/timeline/role-based-views-with-different-permission"
---

# Role-Based Access - React Timeline demo

Implement role-based access in a React Timeline: managers edit all tasks, employees edit their own, and clients have read-only access. For React and Next.JS.

- [Live demo](https://demo.mobiscroll.com/react/timeline/role-based-views-with-different-permission)
- [View React JS code on GitHub](https://github.com/acidb/mobiscroll-demos-react/tree/main/src/demos/eventcalendar/timeline/role-based-views-with-different-permission)
- [View React TS code on GitHub](https://github.com/acidb/mobiscroll-demos-react-ts/tree/main/src/demos/eventcalendar/timeline/role-based-views-with-different-permission)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/timeline/role-based-views-with-different-permission.md), [jQuery](https://demo.mobiscroll.com/jquery/timeline/role-based-views-with-different-permission.md), [Angular](https://demo.mobiscroll.com/angular/timeline/role-based-views-with-different-permission.md), [Vue](https://demo.mobiscroll.com/vue/timeline/role-based-views-with-different-permission.md)

## Description

The following example demonstrates how to implement role-based permissions in a calendar by simulating a logged in user. The selected user determines which role and permissions are applied:

- **Project manager (Full access)** - can manage all tasks
- **Employees (Limited access)** - can only edit their own tasks
- **Clients (Read-only)** - can view the schedule without making changes

The demo uses an outside control to simulate logging in as a specific user. Based on the chosen role, the calendar applies the corresponding permissions conditionally.

This approach shows how you could pass the logged in user's role from your authentication system and dynamically configure what actions are allowed in the timeline.

## What this demo shows

- A desktop weekly timeline for role-based access control, with days arranged horizontally and resources listed as rows on the left.
- **Header navigation** The month and year label opens date navigation, while the previous and next arrows move between weeks and the Today button returns to the current date.
- **Week view** The strip below the header shows the selected week from Sunday to Saturday, with the current date highlighted.
- **Role selection** The demo simulates different logged-in users to show how permissions change by role.
- **Full access role** The Project manager can create, edit, drag, resize, and delete tasks across all resources.
- **Limited access role** Employees can view the full schedule but can only modify tasks assigned to themselves.
- **Employee preview** Selecting different employees shows the editing permissions for each employee.
- **Read-only role** Clients can browse the full schedule but cannot create, edit, move, resize, or delete tasks.
- **Permission handling** Editing capabilities are enabled or disabled automatically according to the active user role.
- **Shared timeline data** The same timeline data is shown with different interaction levels without changing the underlying schedule.
- **Time grid** The timeline displays hourly columns from 8 AM to 8 PM.
- **Resources** Multiple resources are shown as separate timeline rows.
- **Current time** A vertical blue line with a time label marks the current time.
- **Hover behavior** Hovering over the time grid shows a time indicator that follows the cursor in 15-minute increments.
- **Event cards** Events are shown as colored cards with a left color stripe, a bold title, a description, and a bold time range.
- **Date positioning** Events are positioned by their assigned resource and exact date and time range.
- **Event interaction** Editable events highlight on hover and show drag and resize handles for repositioning or changing duration.
- **Event selection** Clicking an event selects and highlights it.
- **Event creation** New events can be created by double-clicking the timeline or by clicking and dragging across a time range when the active role allows it.
- **Scrolling** The timeline supports horizontal and vertical scrolling for navigating through time and resources.

## Best for

- **Frontend permission handling** Demonstrating how to apply role-based editing rules while keeping a shared timeline interface visible to every user.
- **Project management tools** Showing different task management permissions for managers, team members, and external clients.
- **Workforce scheduling** Letting employees view the full team timeline while limiting edits to their own assigned work.
- **Team collaboration apps** Supporting shared planning views where each role has a different level of control over the same timeline.
- **Client-facing timeline** Providing read-only access to project timelines without exposing editing actions.
- **Permission testing** Previewing how the same timeline behaves for full access, limited access, and read-only users.
- **Resource-based timelines** Managing tasks across multiple people or resources in a weekly business-hours timeline.

## Tutorial

### Simulate logged in user

- ( ) Full access - Project manager
- (x) Limited access `Willis Cane`- Employee
- ( ) Read-only access - Client

```jsx
const initialEvents = [{
  start: '2026-10-22T11:00',
  end: '2026-10-22T15:00',
  title: 'Task 1',
  resource: 1
}, {
  start: '2026-10-22T14:00',
  end: '2026-10-22T17:00',
  title: 'Task 2',
  resource: 3
}, {
  start: '2026-10-22T12:00',
  end: '2026-10-22T14:00',
  title: 'Task 3',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T15:00',
  title: 'Task 4',
  resource: 1
}, {
  start: '2026-10-23T11:00',
  end: '2026-10-23T13:00',
  title: 'Task 5',
  resource: 2
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T17:00',
  title: 'Task 6',
  resource: 2
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T15:00',
  title: 'Task 7',
  resource: 3
}, {
  start: '2026-10-23T17:00',
  end: '2026-10-23T20:00',
  title: 'Task 8',
  resource: 3
}, {
  start: '2026-10-23T08:00',
  end: '2026-10-23T11:30',
  title: 'Task 9',
  resource: 4
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T14:00',
  title: 'Task 10',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T13:00',
  title: 'Task 11',
  resource: 5
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T16:00',
  title: 'Task 12',
  resource: 5
}, {      
  start: '2026-10-23T16:30',
  end: '2026-10-23T19:00',
  title: 'Task 13',
  resource: 5
}, {
  start: '2026-10-24T11:00',
  end: '2026-10-24T14:00',
  title: 'Task 14',
  resource: 2
}, {
  start: '2026-10-24T16:00',
  end: '2026-10-24T20:00',
  title: 'Task 15',
  resource: 5
}];

const initialResources = [{
  id: 1,
  name: 'Jude Chester',
  color: '#af2ec3'
}, {
  id: 2,
  name: 'Willis Cane',
  color: '#cccc39'
}, {
  id: 3,
  name: 'Derek Austyn',
  color: '#56ca2c'
}, {
  id: 4,
  name: 'Merv Kenny',
  color: '#af2424'
}, {
  id: 5,
  name: 'Fred Waldez',
  color: '#256ebc'
}]

function App() {
  const user = useMemo(() => ({ 
    id: 2,
    name: 'Willis Cane',
    role: 'limited'
  }), []);

  const [editEvents, setEditEvents] = useState(false);

  const myView = useMemo(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const [myEvents, setEvents] = useState(initialEvents);
  const [myResources, setResources] = useState(initialResources);

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  // Simulate login
  useEffect(() => {
    const newTasks = [...initialEvents];
    const newResources = [...initialResources];

    if (user.role === 'readonly') {
      for (const task of newTasks) {
        // Disable editing for all events
        task.editable = false;
        // Make event colors uniform
        task.color = '#af2ec3';
      }
    } else if (user.role === 'limited') {
      for (const task of newTasks) {
        if (task.resource !== user.id) {
          // Disable editing events of other users
          task.editable = false
          // Set disabled event colors to gray
          task.color = '#6a6a6a'
        } else {
          // Make event colors uniform
          task.color = '#af2424'
        }
      }

      for (const res of newResources) {
        if (res.id !== user.id) {
          res.eventCreation = false;
        }
      }
    }

    // Update events, resources and d&d options
    setEvents(newTasks);
    setResources(newResources);
    setEditEvents(user.role !== 'readonly');
  }, [user]);

  return (
    <Eventcalendar
      view={myView}
      data={myEvents}
      resources={myResources}
      clickToCreate={editEvents}
      dragToCreate={editEvents}
      dragToMove={editEvents}
      dragToResize={editEvents}
      eventDelete={editEvents}
      extendDefaultEvent={getDefaultEvent}
    />
);
```

With "Full access - Project manager":

```jsx
const initialEvents = [{
  start: '2026-10-22T11:00',
  end: '2026-10-22T15:00',
  title: 'Task 1',
  resource: 1
}, {
  start: '2026-10-22T14:00',
  end: '2026-10-22T17:00',
  title: 'Task 2',
  resource: 3
}, {
  start: '2026-10-22T12:00',
  end: '2026-10-22T14:00',
  title: 'Task 3',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T15:00',
  title: 'Task 4',
  resource: 1
}, {
  start: '2026-10-23T11:00',
  end: '2026-10-23T13:00',
  title: 'Task 5',
  resource: 2
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T17:00',
  title: 'Task 6',
  resource: 2
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T15:00',
  title: 'Task 7',
  resource: 3
}, {
  start: '2026-10-23T17:00',
  end: '2026-10-23T20:00',
  title: 'Task 8',
  resource: 3
}, {
  start: '2026-10-23T08:00',
  end: '2026-10-23T11:30',
  title: 'Task 9',
  resource: 4
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T14:00',
  title: 'Task 10',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T13:00',
  title: 'Task 11',
  resource: 5
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T16:00',
  title: 'Task 12',
  resource: 5
}, {      
  start: '2026-10-23T16:30',
  end: '2026-10-23T19:00',
  title: 'Task 13',
  resource: 5
}, {
  start: '2026-10-24T11:00',
  end: '2026-10-24T14:00',
  title: 'Task 14',
  resource: 2
}, {
  start: '2026-10-24T16:00',
  end: '2026-10-24T20:00',
  title: 'Task 15',
  resource: 5
}];

const initialResources = [{
  id: 1,
  name: 'Jude Chester',
  color: '#af2ec3'
}, {
  id: 2,
  name: 'Willis Cane',
  color: '#cccc39'
}, {
  id: 3,
  name: 'Derek Austyn',
  color: '#56ca2c'
}, {
  id: 4,
  name: 'Merv Kenny',
  color: '#af2424'
}, {
  id: 5,
  name: 'Fred Waldez',
  color: '#256ebc'
}]

function App() {
  const user = useMemo(() => ({ 
    id: 'client'
    name: 'Project manager',
    role: 'full'
  }), []);

  const [editEvents, setEditEvents] = useState(false);

  const myView = useMemo(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const [myEvents, setEvents] = useState(initialEvents);
  const [myResources, setResources] = useState(initialResources);

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  // Simulate login
  useEffect(() => {
    const newTasks = [...initialEvents];
    const newResources = [...initialResources];

    if (user.role === 'readonly') {
      for (const task of newTasks) {
        // Disable editing for all events
        task.editable = false;
        // Make event colors uniform
        task.color = '#af2ec3';
      }
    } else if (user.role === 'limited') {
      for (const task of newTasks) {
        if (task.resource !== user.id) {
          // Disable editing events of other users
          task.editable = false
          // Set disabled event colors to gray
          task.color = '#6a6a6a'
        } else {
          // Make event colors uniform
          task.color = '#af2424'
        }
      }

      for (const res of newResources) {
        if (res.id !== user.id) {
          res.eventCreation = false;
        }
      }
    }

    // Update events, resources and d&d options
    setEvents(newTasks);
    setResources(newResources);
    setEditEvents(user.role !== 'readonly');
  }, [user]);

  return (
    <Eventcalendar
      view={myView}
      data={myEvents}
      resources={myResources}
      clickToCreate={editEvents}
      dragToCreate={editEvents}
      dragToMove={editEvents}
      dragToResize={editEvents}
      eventDelete={editEvents}
      extendDefaultEvent={getDefaultEvent}
    />
);
```

With "Read-only access - Client":

```jsx
const initialEvents = [{
  start: '2026-10-22T11:00',
  end: '2026-10-22T15:00',
  title: 'Task 1',
  resource: 1
}, {
  start: '2026-10-22T14:00',
  end: '2026-10-22T17:00',
  title: 'Task 2',
  resource: 3
}, {
  start: '2026-10-22T12:00',
  end: '2026-10-22T14:00',
  title: 'Task 3',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T15:00',
  title: 'Task 4',
  resource: 1
}, {
  start: '2026-10-23T11:00',
  end: '2026-10-23T13:00',
  title: 'Task 5',
  resource: 2
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T17:00',
  title: 'Task 6',
  resource: 2
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T15:00',
  title: 'Task 7',
  resource: 3
}, {
  start: '2026-10-23T17:00',
  end: '2026-10-23T20:00',
  title: 'Task 8',
  resource: 3
}, {
  start: '2026-10-23T08:00',
  end: '2026-10-23T11:30',
  title: 'Task 9',
  resource: 4
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T14:00',
  title: 'Task 10',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T13:00',
  title: 'Task 11',
  resource: 5
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T16:00',
  title: 'Task 12',
  resource: 5
}, {      
  start: '2026-10-23T16:30',
  end: '2026-10-23T19:00',
  title: 'Task 13',
  resource: 5
}, {
  start: '2026-10-24T11:00',
  end: '2026-10-24T14:00',
  title: 'Task 14',
  resource: 2
}, {
  start: '2026-10-24T16:00',
  end: '2026-10-24T20:00',
  title: 'Task 15',
  resource: 5
}];

const initialResources = [{
  id: 1,
  name: 'Jude Chester',
  color: '#af2ec3'
}, {
  id: 2,
  name: 'Willis Cane',
  color: '#cccc39'
}, {
  id: 3,
  name: 'Derek Austyn',
  color: '#56ca2c'
}, {
  id: 4,
  name: 'Merv Kenny',
  color: '#af2424'
}, {
  id: 5,
  name: 'Fred Waldez',
  color: '#256ebc'
}]

function App() {
  const user = useMemo(() => ({ 
    id: 'client'
    name: 'Client',
    role: 'readonly'
  }), []);

  const [editEvents, setEditEvents] = useState(false);

  const myView = useMemo(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const [myEvents, setEvents] = useState(initialEvents);
  const [myResources, setResources] = useState(initialResources);

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  // Simulate login
  useEffect(() => {
    const newTasks = [...initialEvents];
    const newResources = [...initialResources];

    if (user.role === 'readonly') {
      for (const task of newTasks) {
        // Disable editing for all events
        task.editable = false;
        // Make event colors uniform
        task.color = '#af2ec3';
      }
    } else if (user.role === 'limited') {
      for (const task of newTasks) {
        if (task.resource !== user.id) {
          // Disable editing events of other users
          task.editable = false
          // Set disabled event colors to gray
          task.color = '#6a6a6a'
        } else {
          // Make event colors uniform
          task.color = '#af2424'
        }
      }

      for (const res of newResources) {
        if (res.id !== user.id) {
          res.eventCreation = false;
        }
      }
    }

    // Update events, resources and d&d options
    setEvents(newTasks);
    setResources(newResources);
    setEditEvents(user.role !== 'readonly');
  }, [user]);

  return (
    <Eventcalendar
      view={myView}
      data={myEvents}
      resources={myResources}
      clickToCreate={editEvents}
      dragToCreate={editEvents}
      dragToMove={editEvents}
      dragToResize={editEvents}
      eventDelete={editEvents}
      extendDefaultEvent={getDefaultEvent}
    />
);
```

With "Jude Chester":

```jsx
const initialEvents = [{
  start: '2026-10-22T11:00',
  end: '2026-10-22T15:00',
  title: 'Task 1',
  resource: 1
}, {
  start: '2026-10-22T14:00',
  end: '2026-10-22T17:00',
  title: 'Task 2',
  resource: 3
}, {
  start: '2026-10-22T12:00',
  end: '2026-10-22T14:00',
  title: 'Task 3',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T15:00',
  title: 'Task 4',
  resource: 1
}, {
  start: '2026-10-23T11:00',
  end: '2026-10-23T13:00',
  title: 'Task 5',
  resource: 2
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T17:00',
  title: 'Task 6',
  resource: 2
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T15:00',
  title: 'Task 7',
  resource: 3
}, {
  start: '2026-10-23T17:00',
  end: '2026-10-23T20:00',
  title: 'Task 8',
  resource: 3
}, {
  start: '2026-10-23T08:00',
  end: '2026-10-23T11:30',
  title: 'Task 9',
  resource: 4
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T14:00',
  title: 'Task 10',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T13:00',
  title: 'Task 11',
  resource: 5
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T16:00',
  title: 'Task 12',
  resource: 5
}, {      
  start: '2026-10-23T16:30',
  end: '2026-10-23T19:00',
  title: 'Task 13',
  resource: 5
}, {
  start: '2026-10-24T11:00',
  end: '2026-10-24T14:00',
  title: 'Task 14',
  resource: 2
}, {
  start: '2026-10-24T16:00',
  end: '2026-10-24T20:00',
  title: 'Task 15',
  resource: 5
}];

const initialResources = [{
  id: 1,
  name: 'Jude Chester',
  color: '#af2ec3'
}, {
  id: 2,
  name: 'Willis Cane',
  color: '#cccc39'
}, {
  id: 3,
  name: 'Derek Austyn',
  color: '#56ca2c'
}, {
  id: 4,
  name: 'Merv Kenny',
  color: '#af2424'
}, {
  id: 5,
  name: 'Fred Waldez',
  color: '#256ebc'
}]

function App() {
  const user = useMemo(() => ({ 
    id: 1,
    name: 'Jude Chester',
    role: 'limited'
  }), []);

  const [editEvents, setEditEvents] = useState(false);

  const myView = useMemo(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const [myEvents, setEvents] = useState(initialEvents);
  const [myResources, setResources] = useState(initialResources);

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  // Simulate login
  useEffect(() => {
    const newTasks = [...initialEvents];
    const newResources = [...initialResources];

    if (user.role === 'readonly') {
      for (const task of newTasks) {
        // Disable editing for all events
        task.editable = false;
        // Make event colors uniform
        task.color = '#af2ec3';
      }
    } else if (user.role === 'limited') {
      for (const task of newTasks) {
        if (task.resource !== user.id) {
          // Disable editing events of other users
          task.editable = false
          // Set disabled event colors to gray
          task.color = '#6a6a6a'
        } else {
          // Make event colors uniform
          task.color = '#af2424'
        }
      }

      for (const res of newResources) {
        if (res.id !== user.id) {
          res.eventCreation = false;
        }
      }
    }

    // Update events, resources and d&d options
    setEvents(newTasks);
    setResources(newResources);
    setEditEvents(user.role !== 'readonly');
  }, [user]);

  return (
    <Eventcalendar
      view={myView}
      data={myEvents}
      resources={myResources}
      clickToCreate={editEvents}
      dragToCreate={editEvents}
      dragToMove={editEvents}
      dragToResize={editEvents}
      eventDelete={editEvents}
      extendDefaultEvent={getDefaultEvent}
    />
);
```

With "Derek Austyn":

```jsx
const initialEvents = [{
  start: '2026-10-22T11:00',
  end: '2026-10-22T15:00',
  title: 'Task 1',
  resource: 1
}, {
  start: '2026-10-22T14:00',
  end: '2026-10-22T17:00',
  title: 'Task 2',
  resource: 3
}, {
  start: '2026-10-22T12:00',
  end: '2026-10-22T14:00',
  title: 'Task 3',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T15:00',
  title: 'Task 4',
  resource: 1
}, {
  start: '2026-10-23T11:00',
  end: '2026-10-23T13:00',
  title: 'Task 5',
  resource: 2
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T17:00',
  title: 'Task 6',
  resource: 2
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T15:00',
  title: 'Task 7',
  resource: 3
}, {
  start: '2026-10-23T17:00',
  end: '2026-10-23T20:00',
  title: 'Task 8',
  resource: 3
}, {
  start: '2026-10-23T08:00',
  end: '2026-10-23T11:30',
  title: 'Task 9',
  resource: 4
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T14:00',
  title: 'Task 10',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T13:00',
  title: 'Task 11',
  resource: 5
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T16:00',
  title: 'Task 12',
  resource: 5
}, {      
  start: '2026-10-23T16:30',
  end: '2026-10-23T19:00',
  title: 'Task 13',
  resource: 5
}, {
  start: '2026-10-24T11:00',
  end: '2026-10-24T14:00',
  title: 'Task 14',
  resource: 2
}, {
  start: '2026-10-24T16:00',
  end: '2026-10-24T20:00',
  title: 'Task 15',
  resource: 5
}];

const initialResources = [{
  id: 1,
  name: 'Jude Chester',
  color: '#af2ec3'
}, {
  id: 2,
  name: 'Willis Cane',
  color: '#cccc39'
}, {
  id: 3,
  name: 'Derek Austyn',
  color: '#56ca2c'
}, {
  id: 4,
  name: 'Merv Kenny',
  color: '#af2424'
}, {
  id: 5,
  name: 'Fred Waldez',
  color: '#256ebc'
}]

function App() {
  const user = useMemo(() => ({ 
    id: 3,
    name: 'Derek Austyn',
    role: 'limited'
  }), []);

  const [editEvents, setEditEvents] = useState(false);

  const myView = useMemo(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const [myEvents, setEvents] = useState(initialEvents);
  const [myResources, setResources] = useState(initialResources);

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  // Simulate login
  useEffect(() => {
    const newTasks = [...initialEvents];
    const newResources = [...initialResources];

    if (user.role === 'readonly') {
      for (const task of newTasks) {
        // Disable editing for all events
        task.editable = false;
        // Make event colors uniform
        task.color = '#af2ec3';
      }
    } else if (user.role === 'limited') {
      for (const task of newTasks) {
        if (task.resource !== user.id) {
          // Disable editing events of other users
          task.editable = false
          // Set disabled event colors to gray
          task.color = '#6a6a6a'
        } else {
          // Make event colors uniform
          task.color = '#af2424'
        }
      }

      for (const res of newResources) {
        if (res.id !== user.id) {
          res.eventCreation = false;
        }
      }
    }

    // Update events, resources and d&d options
    setEvents(newTasks);
    setResources(newResources);
    setEditEvents(user.role !== 'readonly');
  }, [user]);

  return (
    <Eventcalendar
      view={myView}
      data={myEvents}
      resources={myResources}
      clickToCreate={editEvents}
      dragToCreate={editEvents}
      dragToMove={editEvents}
      dragToResize={editEvents}
      eventDelete={editEvents}
      extendDefaultEvent={getDefaultEvent}
    />
);
```

With "Merv Kenny":

```jsx
const initialEvents = [{
  start: '2026-10-22T11:00',
  end: '2026-10-22T15:00',
  title: 'Task 1',
  resource: 1
}, {
  start: '2026-10-22T14:00',
  end: '2026-10-22T17:00',
  title: 'Task 2',
  resource: 3
}, {
  start: '2026-10-22T12:00',
  end: '2026-10-22T14:00',
  title: 'Task 3',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T15:00',
  title: 'Task 4',
  resource: 1
}, {
  start: '2026-10-23T11:00',
  end: '2026-10-23T13:00',
  title: 'Task 5',
  resource: 2
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T17:00',
  title: 'Task 6',
  resource: 2
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T15:00',
  title: 'Task 7',
  resource: 3
}, {
  start: '2026-10-23T17:00',
  end: '2026-10-23T20:00',
  title: 'Task 8',
  resource: 3
}, {
  start: '2026-10-23T08:00',
  end: '2026-10-23T11:30',
  title: 'Task 9',
  resource: 4
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T14:00',
  title: 'Task 10',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T13:00',
  title: 'Task 11',
  resource: 5
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T16:00',
  title: 'Task 12',
  resource: 5
}, {      
  start: '2026-10-23T16:30',
  end: '2026-10-23T19:00',
  title: 'Task 13',
  resource: 5
}, {
  start: '2026-10-24T11:00',
  end: '2026-10-24T14:00',
  title: 'Task 14',
  resource: 2
}, {
  start: '2026-10-24T16:00',
  end: '2026-10-24T20:00',
  title: 'Task 15',
  resource: 5
}];

const initialResources = [{
  id: 1,
  name: 'Jude Chester',
  color: '#af2ec3'
}, {
  id: 2,
  name: 'Willis Cane',
  color: '#cccc39'
}, {
  id: 3,
  name: 'Derek Austyn',
  color: '#56ca2c'
}, {
  id: 4,
  name: 'Merv Kenny',
  color: '#af2424'
}, {
  id: 5,
  name: 'Fred Waldez',
  color: '#256ebc'
}]

function App() {
  const user = useMemo(() => ({ 
    id: 4,
    name: 'Merv Kenny',
    role: 'limited'
  }), []);

  const [editEvents, setEditEvents] = useState(false);

  const myView = useMemo(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const [myEvents, setEvents] = useState(initialEvents);
  const [myResources, setResources] = useState(initialResources);

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  // Simulate login
  useEffect(() => {
    const newTasks = [...initialEvents];
    const newResources = [...initialResources];

    if (user.role === 'readonly') {
      for (const task of newTasks) {
        // Disable editing for all events
        task.editable = false;
        // Make event colors uniform
        task.color = '#af2ec3';
      }
    } else if (user.role === 'limited') {
      for (const task of newTasks) {
        if (task.resource !== user.id) {
          // Disable editing events of other users
          task.editable = false
          // Set disabled event colors to gray
          task.color = '#6a6a6a'
        } else {
          // Make event colors uniform
          task.color = '#af2424'
        }
      }

      for (const res of newResources) {
        if (res.id !== user.id) {
          res.eventCreation = false;
        }
      }
    }

    // Update events, resources and d&d options
    setEvents(newTasks);
    setResources(newResources);
    setEditEvents(user.role !== 'readonly');
  }, [user]);

  return (
    <Eventcalendar
      view={myView}
      data={myEvents}
      resources={myResources}
      clickToCreate={editEvents}
      dragToCreate={editEvents}
      dragToMove={editEvents}
      dragToResize={editEvents}
      eventDelete={editEvents}
      extendDefaultEvent={getDefaultEvent}
    />
);
```

With "Fred Waldez":

```jsx
const initialEvents = [{
  start: '2026-10-22T11:00',
  end: '2026-10-22T15:00',
  title: 'Task 1',
  resource: 1
}, {
  start: '2026-10-22T14:00',
  end: '2026-10-22T17:00',
  title: 'Task 2',
  resource: 3
}, {
  start: '2026-10-22T12:00',
  end: '2026-10-22T14:00',
  title: 'Task 3',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T15:00',
  title: 'Task 4',
  resource: 1
}, {
  start: '2026-10-23T11:00',
  end: '2026-10-23T13:00',
  title: 'Task 5',
  resource: 2
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T17:00',
  title: 'Task 6',
  resource: 2
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T15:00',
  title: 'Task 7',
  resource: 3
}, {
  start: '2026-10-23T17:00',
  end: '2026-10-23T20:00',
  title: 'Task 8',
  resource: 3
}, {
  start: '2026-10-23T08:00',
  end: '2026-10-23T11:30',
  title: 'Task 9',
  resource: 4
}, {
  start: '2026-10-23T12:00',
  end: '2026-10-23T14:00',
  title: 'Task 10',
  resource: 4
}, {
  start: '2026-10-23T10:00',
  end: '2026-10-23T13:00',
  title: 'Task 11',
  resource: 5
}, {
  start: '2026-10-23T14:00',
  end: '2026-10-23T16:00',
  title: 'Task 12',
  resource: 5
}, {      
  start: '2026-10-23T16:30',
  end: '2026-10-23T19:00',
  title: 'Task 13',
  resource: 5
}, {
  start: '2026-10-24T11:00',
  end: '2026-10-24T14:00',
  title: 'Task 14',
  resource: 2
}, {
  start: '2026-10-24T16:00',
  end: '2026-10-24T20:00',
  title: 'Task 15',
  resource: 5
}];

const initialResources = [{
  id: 1,
  name: 'Jude Chester',
  color: '#af2ec3'
}, {
  id: 2,
  name: 'Willis Cane',
  color: '#cccc39'
}, {
  id: 3,
  name: 'Derek Austyn',
  color: '#56ca2c'
}, {
  id: 4,
  name: 'Merv Kenny',
  color: '#af2424'
}, {
  id: 5,
  name: 'Fred Waldez',
  color: '#256ebc'
}]

function App() {
  const user = useMemo(() => ({ 
    id: 5,
    name: 'Fred Waldez',
    role: 'limited'
  }), []);

  const [editEvents, setEditEvents] = useState(false);

  const myView = useMemo(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const [myEvents, setEvents] = useState(initialEvents);
  const [myResources, setResources] = useState(initialResources);

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  // Simulate login
  useEffect(() => {
    const newTasks = [...initialEvents];
    const newResources = [...initialResources];

    if (user.role === 'readonly') {
      for (const task of newTasks) {
        // Disable editing for all events
        task.editable = false;
        // Make event colors uniform
        task.color = '#af2ec3';
      }
    } else if (user.role === 'limited') {
      for (const task of newTasks) {
        if (task.resource !== user.id) {
          // Disable editing events of other users
          task.editable = false
          // Set disabled event colors to gray
          task.color = '#6a6a6a'
        } else {
          // Make event colors uniform
          task.color = '#af2424'
        }
      }

      for (const res of newResources) {
        if (res.id !== user.id) {
          res.eventCreation = false;
        }
      }
    }

    // Update events, resources and d&d options
    setEvents(newTasks);
    setResources(newResources);
    setEditEvents(user.role !== 'readonly');
  }, [user]);

  return (
    <Eventcalendar
      view={myView}
      data={myEvents}
      resources={myResources}
      clickToCreate={editEvents}
      dragToCreate={editEvents}
      dragToMove={editEvents}
      dragToResize={editEvents}
      eventDelete={editEvents}
      extendDefaultEvent={getDefaultEvent}
    />
);
```

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-themeVariant) - option
- [`view`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-data) - option
- [`resources`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-resources) - option
- [`clickToCreate`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToResize) - option
- [`eventDelete`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-eventDelete) - option
- [`extendDefaultEvent`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-extendDefaultEvent) - option
- [`onClose`](https://mobiscroll.com/docs/react/notifications/toast#event-onClose) - event

## Code

### JSX

```jsx
import { Eventcalendar, setOptions, Toast } from '@mobiscroll/react';
import { useCallback, useEffect, useMemo, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

const initialEvents = [
  {
    start: '2026-10-22T11:00',
    end: '2026-10-22T15:00',
    title: 'Task 1',
    resource: 1,
  },
  {
    start: '2026-10-22T14:00',
    end: '2026-10-22T17:00',
    title: 'Task 2',
    resource: 3,
  },
  {
    start: '2026-10-22T12:00',
    end: '2026-10-22T14:00',
    title: 'Task 3',
    resource: 4,
  },
  {
    start: '2026-10-23T10:00',
    end: '2026-10-23T15:00',
    title: 'Task 4',
    resource: 1,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T13:00',
    title: 'Task 5',
    resource: 2,
  },
  {
    start: '2026-10-23T14:00',
    end: '2026-10-23T17:00',
    title: 'Task 6',
    resource: 2,
  },
  {
    start: '2026-10-23T12:00',
    end: '2026-10-23T15:00',
    title: 'Task 7',
    resource: 3,
  },
  {
    start: '2026-10-23T17:00',
    end: '2026-10-23T20:00',
    title: 'Task 8',
    resource: 3,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T11:30',
    title: 'Task 9',
    resource: 4,
  },
  {
    start: '2026-10-23T12:00',
    end: '2026-10-23T14:00',
    title: 'Task 10',
    resource: 4,
  },
  {
    start: '2026-10-23T10:00',
    end: '2026-10-23T13:00',
    title: 'Task 11',
    resource: 5,
  },
  {
    start: '2026-10-23T14:00',
    end: '2026-10-23T16:00',
    title: 'Task 12',
    resource: 5,
  },
  {
    start: '2026-10-23T16:30',
    end: '2026-10-23T19:00',
    title: 'Task 13',
    resource: 5,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T14:00',
    title: 'Task 14',
    resource: 2,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T20:00',
    title: 'Task 15',
    resource: 5,
  },
];

const initialResources = [
  { id: 1, name: 'Jude Chester', color: '#af2ec3' },
  { id: 2, name: 'Willis Cane', color: '#cccc39' },
  { id: 3, name: 'Derek Austyn', color: '#56ca2c' },
  { id: 4, name: 'Merv Kenny', color: '#af2424' },
  { id: 5, name: 'Fred Waldez', color: '#256ebc' },
];

function App() {
  const [isToastOpen, setToastOpen] = useState(false);

  const user = useMemo(() => ({ id: 2, name: 'Willis Cane', role: 'limited' }), []);
  /* Other user examples
  const user = useMemo(() => ({ name: 'Client', role: 'readonly' }), []);
  const user = useMemo(() => ({ name: 'Project Manager', role: 'full' }), []); */

  const editEvents = useMemo(() => user.role !== 'readonly', [user]);

  const toastMessage = useMemo(() => {
    if (user.role === 'readonly') {
      return 'Client with read-only access logged in';
    }
    if (user.role === 'limited') {
      return 'User ' + user.name + ' with limited access logged in';
    }
    return 'User with full access logged in';
  }, [user]);

  const myView = useMemo(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const myEvents = useMemo(
    () =>
      initialEvents.map((task) => {
        if (user.role === 'readonly') {
          return { ...task, editable: false, color: '#af2ec3' };
        }
        if (user.role === 'limited') {
          return { ...task, editable: task.resource === user.id, color: task.resource !== user.id ? '#6a6a6a' : '#af2424' };
        }
        return task;
      }),
    [user],
  );

  const myResources = useMemo(
    () => initialResources.map((res) => (user.role === 'limited' && res.id !== user.id ? { ...res, eventCreation: false } : res)),
    [user],
  );

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  useEffect(() => {
    const timer = setTimeout(() => {
      setToastOpen(true);
    });
    return () => clearTimeout(timer);
  }, []);

  return (
    <>
      <Eventcalendar
        className="mds-role-based-timeline"
        view={myView}
        data={myEvents}
        resources={myResources}
        clickToCreate={editEvents}
        dragToCreate={editEvents}
        dragToMove={editEvents}
        dragToResize={editEvents}
        eventDelete={editEvents}
        extendDefaultEvent={getDefaultEvent}
      />
      <Toast message={toastMessage} isOpen={isToastOpen} onClose={handleCloseToast} />
    </>
  );
}

export default App;
```

### TSX

```tsx
import {
  Eventcalendar,
  MbscCalendarEvent,
  MbscEventcalendarView,
  MbscResource,
  setOptions,
  Toast,
} from '@mobiscroll/react';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

const initialEvents: MbscCalendarEvent[] = [
  {
    start: '2026-10-22T11:00',
    end: '2026-10-22T15:00',
    title: 'Task 1',
    resource: 1,
  },
  {
    start: '2026-10-22T14:00',
    end: '2026-10-22T17:00',
    title: 'Task 2',
    resource: 3,
  },
  {
    start: '2026-10-22T12:00',
    end: '2026-10-22T14:00',
    title: 'Task 3',
    resource: 4,
  },
  {
    start: '2026-10-23T10:00',
    end: '2026-10-23T15:00',
    title: 'Task 4',
    resource: 1,
  },
  {
    start: '2026-10-23T11:00',
    end: '2026-10-23T13:00',
    title: 'Task 5',
    resource: 2,
  },
  {
    start: '2026-10-23T14:00',
    end: '2026-10-23T17:00',
    title: 'Task 6',
    resource: 2,
  },
  {
    start: '2026-10-23T12:00',
    end: '2026-10-23T15:00',
    title: 'Task 7',
    resource: 3,
  },
  {
    start: '2026-10-23T17:00',
    end: '2026-10-23T20:00',
    title: 'Task 8',
    resource: 3,
  },
  {
    start: '2026-10-23T08:00',
    end: '2026-10-23T11:30',
    title: 'Task 9',
    resource: 4,
  },
  {
    start: '2026-10-23T12:00',
    end: '2026-10-23T14:00',
    title: 'Task 10',
    resource: 4,
  },
  {
    start: '2026-10-23T10:00',
    end: '2026-10-23T13:00',
    title: 'Task 11',
    resource: 5,
  },
  {
    start: '2026-10-23T14:00',
    end: '2026-10-23T16:00',
    title: 'Task 12',
    resource: 5,
  },
  {
    start: '2026-10-23T16:30',
    end: '2026-10-23T19:00',
    title: 'Task 13',
    resource: 5,
  },
  {
    start: '2026-10-24T11:00',
    end: '2026-10-24T14:00',
    title: 'Task 14',
    resource: 2,
  },
  {
    start: '2026-10-24T16:00',
    end: '2026-10-24T20:00',
    title: 'Task 15',
    resource: 5,
  },
];

const initialResources: MbscResource[] = [
  { id: 1, name: 'Jude Chester', color: '#af2ec3' },
  { id: 2, name: 'Willis Cane', color: '#cccc39' },
  { id: 3, name: 'Derek Austyn', color: '#56ca2c' },
  { id: 4, name: 'Merv Kenny', color: '#af2424' },
  { id: 5, name: 'Fred Waldez', color: '#256ebc' },
];

const App: FC = () => {
  const [isToastOpen, setToastOpen] = useState<boolean>(false);

  const user = useMemo(() => ({ id: 2, name: 'Willis Cane', role: 'limited' }), []);
  /* Other user examples
  const user = useMemo(() => ({ name: 'Client', role: 'readonly' }), []);
  const user = useMemo(() => ({ name: 'Project Manager', role: 'full' }), []); */

  const editEvents = useMemo<boolean>(() => user.role !== 'readonly', [user]);

  const toastMessage = useMemo(() => {
    if (user.role === 'readonly') {
      return 'Client with read-only access logged in';
    }
    if (user.role === 'limited') {
      return 'User ' + user.name + ' with limited access logged in';
    }
    return 'User with full access logged in';
  }, [user]);

  const myView = useMemo<MbscEventcalendarView>(
    () => ({
      timeline: {
        type: 'week',
        startTime: '08:00',
        endTime: '20:00',
      },
    }),
    [],
  );

  const myEvents = useMemo<MbscCalendarEvent[]>(
    () =>
      initialEvents.map((task) => {
        if (user.role === 'readonly') {
          return { ...task, editable: false, color: '#af2ec3' };
        }
        if (user.role === 'limited') {
          return { ...task, editable: task.resource === user.id, color: task.resource !== user.id ? '#6a6a6a' : '#af2424' };
        }
        return task;
      }),
    [user],
  );

  const myResources = useMemo<MbscResource[]>(
    () => initialResources.map((res) => (user.role === 'limited' && res.id !== user.id ? { ...res, eventCreation: false } : res)),
    [user],
  );

  const getDefaultEvent = useCallback(
    () => ({
      color: user.role === 'limited' ? '#af2424' : '',
    }),
    [user.role],
  );

  const handleCloseToast = useCallback(() => {
    setToastOpen(false);
  }, []);

  useEffect(() => {
    const timer = setTimeout(() => {
      setToastOpen(true);
    });
    return () => clearTimeout(timer);
  }, []);

  return (
    <>
      <Eventcalendar
        className="mds-role-based-timeline"
        view={myView}
        data={myEvents}
        resources={myResources}
        clickToCreate={editEvents}
        dragToCreate={editEvents}
        dragToMove={editEvents}
        dragToResize={editEvents}
        eventDelete={editEvents}
        extendDefaultEvent={getDefaultEvent}
      />
      <Toast message={toastMessage} isOpen={isToastOpen} onClose={handleCloseToast} />
    </>
  );
};
export default App;
```

### CSS

```css
.mds-role-based-timeline .mbsc-schedule-event:not(.mbsc-readonly-event) .mbsc-schedule-event-background {
  border: 2px solid currentColor;
}

.mds-role-based-timeline .mbsc-schedule-event .mbsc-schedule-event-background::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.6);
}
```

## Implementation notes

- Use `timeline: { type: 'week', startTime: '08:00', endTime: '20:00' }` to show a week view restricted to business hours.
- Define 5 named employee resources (Jude Chester, Willis Cane, Derek Austyn, Merv Kenny, Fred Waldez), each with a distinct `color`.
- Prepare 15 tasks using `dyndatetime` offsets spread across today-1, today, and today+1. Each task has a single `resource` ID. Store them as the initial (unmodified) event array.
- Represent the current user as an object with `id`, `name`, and `role` (`'full'` / `'limited'` / `'readonly'`). The demo hard-codes Willis Cane with `role: 'limited'` as the default logged-in user.
- **Run the login simulation on mount.** It works on shallow copies of the event and resource arrays to avoid mutating the originals, then applies role logic:
  - **`'readonly'`**: Set `editable: false` and `color: '#af2ec3'` on every event.
  - **`'limited'`**: For each event, if `task.resource !== user.id` set `editable: false` and `color: '#6a6a6a'`; otherwise set `color: '#af2424'` (the user's own color). For each resource other than the user's own, set `eventCreation: false` to block creating events on foreign rows.
  - **`'full'`**: Leave all events and resources unmodified.
- Derive an `editEvents` boolean: `true` for `'full'` and `'limited'`, `false` for `'readonly'`. Pass this single value to `clickToCreate`, `dragToCreate`, `dragToMove`, `dragToResize`, and `eventDelete` simultaneously.
- Pass `extendDefaultEvent` returning `{ color: defaultColor }` where `defaultColor` is `'#af2424'` for `'limited'` and `''` for the other roles. This ensures newly drag-created events inherit the user's own color in limited mode.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-react.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config react
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config react --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/react/getting-started/installation

## Related demos

- Previous: [External Date Navigation](https://demo.mobiscroll.com/react/timeline/navigate-view-from-external-calendar.md) - Use a two-panel React Timeline navigated by an external date picker — selecting a date syncs the timeline and the picker instantly.
