---
title: "Javascript Timeline Resource Search and Filtering Example | Mobiscroll"
component: "Timeline"
framework: "JavaScript"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/javascript/timeline/resource-filtering-search"
---

# Resource Search and Filtering - JavaScript Timeline demo

Add live search and filter to a Javascript Timeline resource header — multi-select by status or group, with a custom empty state and reset. Plain JavaScript API.

- [Live demo](https://demo.mobiscroll.com/javascript/timeline/resource-filtering-search)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-javascript/tree/main/src/demos/eventcalendar/timeline/resource-filtering-search)
- This demo in other frameworks: [jQuery](https://demo.mobiscroll.com/jquery/timeline/resource-filtering-search.md), [Angular](https://demo.mobiscroll.com/angular/timeline/resource-filtering-search.md), [React](https://demo.mobiscroll.com/react/timeline/resource-filtering-search.md), [Vue](https://demo.mobiscroll.com/vue/timeline/resource-filtering-search.md)

## Description

When dealing with numerous resources, filtering and search are essential tools. Using the [`renderResourceHeader`](https://mobiscroll.com/docs/javascript/eventcalendar/timeline#renderer-renderResourceHeader) function you can customize the resource header and fit a search input and filter button. Having full control over what you render in the resource header, you can implement live search and filering with a flyout, allowing users to quickly locate specific resources based on selected filters.

To handle cases where no search results are found, use the `renderResourceEmpty` function to craft a custom placeholder with specific call-to-action buttons eg. clearing all search terms and resetting filters. The placeholder appears when an empty resource array is passed to the calendar.

## What this demo shows

- A desktop resource timeline for scheduling construction equipment, with Monday to Friday working days arranged horizontally and a hierarchical resource tree arranged vertically on the left.
- **Header navigation** The month and year label in the top left opens date navigation, while the blue previous and next arrows and the Today button on the right make it easy to move between weeks and jump back to the current day.
- **Week numbers** A fixed strip below the header shows the week number for the visible work week.
- **Week view** The timeline displays the selected work week from Monday to Friday using the `DD DDD MMM YYYY` date format, with the current date highlighted.
- **Time grid** Each day shows working hours from 5 AM to 9 PM with hourly columns.
- **Resources** The left resource column shows expandable and collapsible job site groups, with equipment resources nested under each site.
- **Resource rendering** Custom resource content displays the equipment name, identifier, operational status, and a color-coded status indicator.
- **Resource search** A search field in the resource header lets users locate resources by name or keyword.
- **Filter panel** A filter popup provides selectable categories for operational status and job sites.
- **Multi-select filtering** Users can enable multiple statuses and job sites at the same time.
- **Filter actions** Apply updates the visible resources, while Cancel closes the popup without applying the temporary filter selections.
- **Filtered results** The timeline updates to show only resources that match the active search query and selected filters.
- **Empty state** When no resources match, the resource area shows an illustration, a guidance message, and a Reset Filters action.
- **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 appear as colored cards with a left color stripe, job name, and exact start and end time.
- **Event positioning** Events are placed by assigned resource, date, start time, and end time.
- **Event interaction** Events highlight on hover and expose drag and resize handles for moving or changing duration.
- **Event selection** Clicking an event selects and highlights it.
- **Vertical scrolling** The resource area scrolls vertically so many resources can be shown in the same timeline.
- **Horizontal scrolling** The timeline supports horizontal scrolling for navigating the visible date and time range.

## Best for

- **Construction equipment scheduling** Plan equipment assignments across job sites in a work-week timeline.
- **Fleet management** Search and filter vehicle or equipment resources before assigning scheduled work.
- **Asset planning** Keep many grouped resources visible while scheduling jobs by date, time, and duration.
- **Maintenance coordination** Separate resources by operational status, such as on-site equipment and equipment in maintenance.
- **Field operations** Manage schedules where dispatchers need resource search, job site filtering, and a dense desktop timeline.
- **Large resource lists** Support planning workflows where users need search, multi-select filters, vertical scrolling, and an empty state for no matching resources.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-themeVariant) - option
- [`setOptions`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-setOptions) - method
- [`resources`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-resources) - option
- [`buttons`](https://mobiscroll.com/docs/javascript/popup/api#opt-buttons) - option
- [`close`](https://mobiscroll.com/docs/javascript/popup/api#method-close) - method
- [`cssClass`](https://mobiscroll.com/docs/javascript/popup/api#opt-cssClass) - option
- [`display`](https://mobiscroll.com/docs/javascript/popup/api#opt-display) - option
- [`focusOnClose`](https://mobiscroll.com/docs/javascript/popup/api#opt-focusOnClose) - option
- [`showOverlay`](https://mobiscroll.com/docs/javascript/popup/api#opt-showOverlay) - option
- [`width`](https://mobiscroll.com/docs/javascript/popup/api#opt-width) - option
- [`cssClass`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-cssClass) - option
- [`clickToCreate`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-dragToCreate) - option
- [`dragToResize`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-dragToResize) - option
- [`dragToMove`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-dragToMove) - option
- [`view`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-data) - option
- [`renderResource`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderResource) - renderer
- [`renderResourceEmpty`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderResourceEmpty) - renderer
- [`renderResourceHeader`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderResourceHeader) - renderer
- [`setOptions`](https://mobiscroll.com/docs/javascript/popup/api#method-setOptions) - method
- [`anchor`](https://mobiscroll.com/docs/javascript/popup/api#opt-anchor) - option
- [`open`](https://mobiscroll.com/docs/javascript/popup/api#method-open) - method

## Code

### JS

```js
mobiscroll.setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

var myEvents = [
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T19:00',
    title: 'Excavate Foundation',
    resource: 'bulldozer - TX1234',
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T18:00',
    title: 'Install Framing',
    resource: 'crane - NY9101',
  },
  {
    start: '2026-10-25T07:00',
    end: '2026-10-25T20:00',
    title: 'Electrical Wiring',
    resource: 'excavator - FL1213',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T22:00',
    title: 'Roofing',
    resource: 'crane - IL1617',
  },
  {
    start: '2026-10-25T07:00',
    end: '2026-10-25T19:00',
    title: 'Site Cleanup',
    resource: 'bulldozer - PA1819',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T18:00',
    title: 'Foundation Work',
    resource: 'concrete mixer - CA2223',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T22:00',
    title: 'Steel Framing',
    resource: 'crane - NY2425',
  },
  {
    start: '2026-10-25T07:00',
    end: '2026-10-25T16:00',
    title: 'Interior Plumbing',
    resource: 'excavator - FL2627',
  },
  {
    start: '2026-10-25T07:00',
    end: '2026-10-25T16:00',
    title: 'Masonry Work',
    resource: 'crane - IL3031',
  },
  {
    start: '2026-10-25T06:00',
    end: '2026-10-25T15:00',
    title: 'Exterior Work',
    resource: 'bulldozer - PA3233',
  },
  {
    start: '2026-10-25T06:00',
    end: '2026-10-25T15:00',
    title: 'Ground Breaking',
    resource: 'bulldozer - TX3435',
  },
  {
    start: '2026-10-25T09:00',
    end: '2026-10-25T21:00',
    title: 'Wall Construction',
    resource: 'crane - NY3839',
  },
  {
    start: '2026-10-25T07:00',
    end: '2026-10-25T16:00',
    title: 'Roof Installation',
    resource: 'excavator - FL4041',
  },
  {
    start: '2026-10-25T06:00',
    end: '2026-10-25T19:00',
    title: 'Painting',
    resource: 'crane - IL4445',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T20:00',
    title: 'Driveway Paving',
    resource: 'bulldozer - PA4647',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T18:00',
    title: 'Foundation',
    resource: 'concrete mixer - CA5051',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T19:00',
    title: 'Demolition',
    resource: 'crane - NY5253',
  },
  {
    start: '2026-10-25T06:00',
    end: '2026-10-25T16:00',
    title: 'Electrical Wiring',
    resource: 'excavator - FL5455',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T17:00',
    title: 'Roofing',
    resource: 'crane - IL5859',
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T18:00',
    title: 'Finishing Touches',
    resource: 'bulldozer - PA6061',
  },
  {
    start: '2026-10-25T12:00',
    end: '2026-10-25T22:00',
    title: 'Site Preparation',
    resource: 'bulldozer - TX6263',
  },
  {
    start: '2026-10-25T08:00',
    end: '2026-10-25T19:00',
    title: 'Piling Work',
    resource: 'crane - NY6667',
  },
  {
    start: '2026-10-25T07:00',
    end: '2026-10-25T18:00',
    title: 'Concrete Pouring',
    resource: 'excavator - FL6869',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T21:00',
    title: 'Deck Construction',
    resource: 'drill - OH7071',
  },
  {
    start: '2026-10-25T05:00',
    end: '2026-10-25T20:00',
    title: 'Final Touches',
    resource: 'bulldozer - PA7475',
  }, // Showing partial data. Download full source.
];

var myResources = [
  {
    id: 'site1',
    name: '123 Main St, Downtown City',
    eventCreation: false,
    children: [
      {
        id: 'bulldozer - TX1234',
        name: 'Bulldozer - TX1234',
        color: '#1dab2f',
        status: 'on site',
      },
      {
        id: 'concrete mixer - CA5678',
        name: 'Concrete Mixer - CA5678',
        color: '#1dab2f',
        status: 'in maintenance',
      },
      {
        id: 'crane - NY9101',
        name: 'Crane - NY9101',
        color: '#1dab2f',
        status: 'on site',
      },
      {
        id: 'excavator - FL1213',
        name: 'Excavator - FL1213',
        color: '#1dab2f',
        status: 'on site',
      },
      {
        id: 'drill - OH1415',
        name: 'Drill - OH1415',
        color: '#1dab2f',
        status: 'in maintenance',
      },
      {
        id: 'crane - IL1617',
        name: 'Crane - IL1617',
        color: '#1dab2f',
        status: 'on site',
      },
      {
        id: 'bulldozer - PA1819',
        name: 'Bulldozer - PA1819',
        color: '#1dab2f',
        status: 'on site',
      },
    ],
  },
  {
    id: 'site2',
    name: '456 Elm St, Uptown City',
    eventCreation: false,
    children: [
      {
        id: 'bulldozer - TX2021',
        name: 'Bulldozer - TX2021',
        color: '#4981d6',
        status: 'in maintenance',
      },
      {
        id: 'concrete mixer - CA2223',
        name: 'Concrete Mixer - CA2223',
        color: '#4981d6',
        status: 'on site',
      },
      {
        id: 'crane - NY2425',
        name: 'Crane - NY2425',
        color: '#4981d6',
        status: 'on site',
      },
      {
        id: 'excavator - FL2627',
        name: 'Excavator - FL2627',
        color: '#4981d6',
        status: 'on site',
      },
      {
        id: 'drill - OH2829',
        name: 'Drill - OH2829',
        color: '#4981d6',
        status: 'in maintenance',
      },
      {
        id: 'crane - IL3031',
        name: 'Crane - IL3031',
        color: '#4981d6',
        status: 'on site',
      },
      {
        id: 'bulldozer - PA3233',
        name: 'Bulldozer - PA3233',
        color: '#4981d6',
        status: 'on site',
      },
    ],
  },
  {
    id: 'site3',
    name: '789 Maple Ave, Suburban Area',
    eventCreation: false,
    children: [
      {
        id: 'bulldozer - TX3435',
        name: 'Bulldozer - TX3435',
        color: '#f7961e',
        status: 'on site',
      },
      {
        id: 'concrete mixer - CA3637',
        name: 'Concrete Mixer - CA3637',
        color: '#f7961e',
        status: 'in maintenance',
      },
      {
        id: 'crane - NY3839',
        name: 'Crane - NY3839',
        color: '#f7961e',
        status: 'on site',
      },
      {
        id: 'excavator - FL4041',
        name: 'Excavator - FL4041',
        color: '#f7961e',
        status: 'on site',
      },
      {
        id: 'crane - IL4445',
        name: 'Crane - IL4445',
        color: '#f7961e',
        status: 'on site',
      },
      {
        id: 'bulldozer - PA4647',
        name: 'Bulldozer - PA4647',
        color: '#f7961e',
        status: 'on site',
      },
    ],
  },
  {
    id: 'site4',
    name: '101 Industrial Blvd, Industrial City',
    eventCreation: false,
    children: [
      {
        id: 'bulldozer - TX4849',
        name: 'Bulldozer - TX4849',
        color: '#e25dd2',
        status: 'in maintenance',
      },
      {
        id: 'concrete mixer - CA5051',
        name: 'Concrete Mixer - CA5051',
        color: '#e25dd2',
        status: 'on site',
      },
      {
        id: 'crane - NY5253',
        name: 'Crane - NY5253',
        color: '#e25dd2',
        status: 'on site',
      },
      {
        id: 'excavator - FL5455',
        name: 'Excavator - FL5455',
        color: '#e25dd2',
        status: 'on site',
      },
      {
        id: 'drill - OH5657',
        name: 'Drill - OH5657',
        color: '#e25dd2',
        status: 'in maintenance',
      },
      {
        id: 'crane - IL5859',
        name: 'Crane - IL5859',
        color: '#e25dd2',
        status: 'on site',
      },
      {
        id: 'bulldozer - PA6061',
        name: 'Bulldozer - PA6061',
        color: '#e25dd2',
        status: 'on site',
      },
    ],
  },
  {
    id: 'site5',
    name: '202 River Rd, Riverside City',
    eventCreation: false,
    children: [
      {
        id: 'bulldozer - TX6263',
        name: 'Bulldozer - TX6263',
        color: '#34c8e0',
        status: 'on site',
      },
      {
        id: 'concrete mixer - CA6465',
        name: 'Concrete Mixer - CA6465',
        color: '#34c8e0',
        status: 'in maintenance',
      },
      {
        id: 'crane - NY6667',
        name: 'Crane - NY6667',
        color: '#34c8e0',
        status: 'on site',
      },
      {
        id: 'excavator - FL6869',
        name: 'Excavator - FL6869',
        color: '#34c8e0',
        status: 'on site',
      },
      {
        id: 'drill - OH7071',
        name: 'Drill - OH7071',
        color: '#34c8e0',
        status: 'on site',
      },
      {
        id: 'crane - IL7273',
        name: 'Crane - IL7273',
        color: '#34c8e0',
        status: 'in maintenance',
      },
      {
        id: 'bulldozer - PA7475',
        name: 'Bulldozer - PA7475',
        color: '#34c8e0',
        status: 'on site',
      },
    ],
  },
];

var calendarElm = document.getElementById('demo-filtering-calendar');
var popupElm = document.getElementById('demo-filtering-popup');
var resourceList = document.getElementById('demo-resource-list');

var filters = {};
var filteredResources = myResources;
var searchTimeout;
var searchQuery;

function filterResources() {
  filteredResources = myResources
    .map(function (site) {
      return {
        id: site.id,
        name: site.name,
        color: site.color,
        eventCreation: site.eventCreation,
        children: site.children.filter(function (resource) {
          return filters[resource.status] && (!searchQuery || resource.name.toLowerCase().includes(searchQuery.toLowerCase()));
        }),
      };
    })
    .filter(function (site) {
      return site.children.length > 0 && filters[site.id];
    });

  calendar.setOptions({ resources: filteredResources });
}

var popup = mobiscroll.popup(popupElm, {
  buttons: [
    'cancel',
    {
      text: 'Apply',
      keyCode: 'enter',
      handler: function () {
        document.querySelectorAll('.mds-resource-filtering-checkbox').forEach(function (checkbox) {
          filters[checkbox.value] = checkbox.checked;
        });
        filterResources();
        popup.close();
        mobiscroll.toast({
          message: 'Filters applied',
        });
      },
      cssClass: 'mbsc-popup-button-primary',
    },
  ],
  contentPadding: false,
  display: 'anchored',
  focusOnClose: false,
  focusOnOpen: false,
  showOverlay: false,
  width: 400,
});

var calendar = mobiscroll.eventcalendar(calendarElm, {
  cssClass: 'mds-resource-filtering-calendar',
  clickToCreate: true,
  dragToCreate: true,
  dragToResize: true,
  dragToMove: true,
  view: {
    timeline: {
      type: 'week',
      startTime: '05:00',
      endTime: '22:00',
      startDay: 1,
      endDay: 5,
      timeCellStep: 60,
      timeLabelStep: 60,
      weekNumbers: true,
    },
  },
  data: myEvents,
  resources: myResources,
  renderResource: function (resource) {
    return (
      '<div>' +
      '<div class="mds-resource-filtering-name">' +
      resource.name +
      '</div>' +
      (resource.status
        ? '<div class="mds-resource-filtering-status">' +
          '<span class="mds-resource-filtering-status-dot" style="background-color:' +
          (resource.status === 'on site' ? 'green' : 'orange') +
          ';"></span>' +
          resource.status +
          '</div>'
        : '') +
      '</div>'
    );
  },
  renderResourceEmpty: function () {
    return (
      '<div class="mds-resource-filtering-empty mbsc-flex mbsc-align-items-center">' +
      '<div  class="mbsc-flex-1-1">' +
      '<img src="https://img.mobiscroll.com/demos/filter-no-result.png" alt="Empty list" style="width:100px;" />' +
      '<p class="mbsc-font mbsc-margin mbsc-medium mbsc-italic mbsc-txt-muted">No resources match your search.</p>' +
      '<p class="mbsc-margin mbsc-medium mbsc-italic mbsc-txt-muted">Adjust your filters or try a different keyword.</p>' +
      '<button mbsc-button id="demo-reset-filters" data-variant="outline">Reset Filters</button>' +
      '</div>' +
      '</div>'
    );
  },
  renderResourceHeader: function () {
    return (
      '<div class="mbsc-flex mbsc-align-items-center mbsc-font mds-resource-filtering-search">' +
      '<label class="mbsc-flex-1-1">' +
      '<input type="text" mbsc-input id="demo-search-input" autocomplete="off" data-input-style="outline" data-start-icon="material-search" placeholder="Search..." />' +
      '</label>' +
      '<button mbsc-button id="demo-filter-button" data-start-icon="material-filter-list" data-variant="outline" class="mbsc-flex-none">Filter</button>' +
      '</div>'
    );
  },
});

calendarElm.addEventListener('input', function (event) {
  if (event.target.matches('#demo-search-input')) {
    clearTimeout(searchTimeout);
    searchQuery = event.target.value.toLowerCase();
    searchTimeout = setTimeout(filterResources, 300);
  }
});

calendarElm.addEventListener('click', function (event) {
  if (event.target.matches('#demo-filter-button')) {
    // Create resource checkbox list
    var checkboxes = '';
    myResources.forEach(function (site) {
      checkboxes +=
        '<label>' +
        '<input type="checkbox" mbsc-checkbox class="mds-resource-filtering-checkbox" value="' +
        site.id +
        '" checked /> ' +
        site.name +
        '</label>';
    });

    resourceList.innerHTML = checkboxes;
    mobiscroll.enhance(resourceList);

    // Set checkbox checked states
    document.querySelectorAll('.mds-resource-filtering-checkbox').forEach(function (checkboxElm) {
      var checkbox = mobiscroll.getInst(checkboxElm);
      checkbox.checked = filters[checkboxElm.value];
    });

    popup.setOptions({ anchor: event.target });
    popup.open();
  }
});

calendarElm.addEventListener('click', function (event) {
  if (event.target.matches('#demo-reset-filters')) {
    searchQuery = '';

    document.getElementById('demo-search-input').value = '';
    document.querySelectorAll('.mds-resource-filtering-checkbox').forEach(function (checkboxElm) {
      var checkbox = mobiscroll.getInst(checkboxElm);
      checkbox.checked = true;
      filters[checkbox.value] = true;
    });

    filterResources();
    mobiscroll.toast({
      message: 'Filters cleared',
    });
  }
});

// Set initial filters
filters['on site'] = true;
filters['in maintenance'] = true;
myResources.forEach(function (site) {
  filters[site.id] = true;
  site.children.forEach(function (resource) {
    filters[resource.id] = true;
  });
});
```

### HTML

```html
<div id="demo-filtering-calendar"></div>
<div style="display:none">
  <div id="demo-filtering-popup">
    <div class="mbsc-form-group">
      <div class="mbsc-form-group-title">Operational Status</div>
      <label>
        <input
          type="checkbox"
          mbsc-checkbox
          data-label="In maintenance"
          class="mds-resource-filtering-checkbox"
          value="in maintenance"
          checked
        />
      </label>
      <label>
        <input
          type="checkbox"
          mbsc-checkbox
          data-label="On site"
          class="mds-resource-filtering-checkbox"
          value="on site"
          checked
        />
      </label>
    </div>
    <div class="mbsc-form-group">
      <div class="mbsc-form-group-title">Job sites</div>
      <div id="demo-resource-list"></div>
    </div>
  </div>
</div>
```

### CSS

```css
.mds-resource-filtering-calendar .mbsc-timeline-resource-header {
  height: 100%;
  padding: 8px;
  box-sizing: border-box;
}

.mds-resource-filtering-calendar .mbsc-timeline-resource-col {
  width: 350px;
}

.mds-resource-filtering-calendar .mbsc-timeline-resource-title {
  height: 100%;
  box-sizing: border-box;
}

.mds-resource-filtering-calendar .mbsc-timeline-parent {
  height: 34px;
}

.mds-resource-filtering-calendar .mbsc-timeline-row-gutter {
  height: 6px;
}

.mds-resource-filtering-search {
  height: 100%;
}

.mds-resource-filtering-search .mbsc-textfield-wrapper {
  margin: 0;
}

.mds-resource-filtering-search .mbsc-textfield-wrapper.mbsc-ltr {
  margin-right: 8px;
}

.mds-resource-filtering-search .mbsc-textfield-wrapper.mbsc-rtl {
  margin-left: 8px;
}

.mds-resource-filtering-search .mbsc-textfield-wrapper.mbsc-material {
  margin-top: 2px;
}

.mds-resource-filtering-search .mbsc-textfield {
  height: 36px;
  padding: 0 8px 0 38px;
}

.mds-resource-filtering-search .mbsc-textfield-icon {
  top: 50%;
  left: 8px;
  font-size: 20px;
  height: 24px;
  line-height: 24px;
  margin-top: -12px;
}

.mds-resource-filtering-search .mbsc-button {
  margin: 0;
  height: 36px;
  font-size: 14px;
}

.mds-resource-filtering-name {
  font-size: 14px;
  font-weight: bold;
  margin-bottom: 7px;
}

.mds-resource-filtering-status-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: 5px;
}

.mds-resource-filtering-status {
  font-size: 13px;
  font-weight: normal;
}

.mds-resource-filtering-empty {
  height: 100%;
  text-align: center;
}
```

## Implementation notes

- Use `type: 'week'` with `startDay: 1`/`endDay: 5`, `timeCellStep: 60`, `timeLabelStep: 60`, and `weekNumbers: true` for a Mon-Fri hourly view with week numbers in the header.
- Define resources as a two-level tree of job site groups (`eventCreation: false`) and equipment leaf nodes, each with a custom `status` property (`'on site'` or `'in maintenance'`) and a `color`.
- Keep a `filteredResources` state and pass it to the `resources` prop. Write a `filterResources(currentFilters, currentQuery)` function that maps each site, filters its children by status and name match, and removes sites whose children are all filtered out or whose site checkbox is unchecked.
- Use `renderResourceHeader` (Angular: `resourceHeaderTemplate`, Vue: `#resourceHeader` slot) to render a search `Input` (with a search start icon) and a Filter `Button` side by side in the resource column header.
- Debounce the search input by 300ms using a `searchTimeout` ref: on each keystroke, clear the previous timeout and schedule a new call to `filterResources` with the current filters and query.
- Use `renderResource` (Angular: `resourceTemplate`, Vue: `#resource` slot) to show the equipment name and, for leaf nodes, a status line with a small colored dot (green for `'on site'`, orange for `'in maintenance'`).
- Use `renderResourceEmpty` (Angular: `resourceEmptyTemplate`, Vue: `#resourceEmpty` slot) to render a no-results placeholder with an image, a descriptive message, and a "Reset Filters" button that calls the reset handler. This appears automatically when the `resources` prop receives an empty array.
- Open the filter `Popup` anchored to the Filter button's DOM element (obtained via a `ref` on the `Button`). On open, copy the current filters into a `tempFilters` state so changes can be discarded on cancel.
- Inside the popup, render two `Checkbox` groups: one for operational status (on site / in maintenance) and one with a checkbox per job site. Wire each checkbox to toggle its key in `tempFilters`.
- On Apply, commit `tempFilters` to the active `filters` state, call `filterResources` with the new values, and show a `Toast`. On reset, restore all filter keys to `true`, clear the search query, re-run `filterResources`, and show a `Toast`.
- Enable `clickToCreate`, `dragToCreate`, `dragToMove`, and `dragToResize` for full event management on the filtered timeline.

## 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-javascript.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 javascript
   ```

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 javascript --trial`.

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

## Related demos

- Previous: [Shifts and Tasks](https://demo.mobiscroll.com/javascript/timeline/tasks-subtasks-under-shifts.md) - Display shifts and linked tasks on a Javascript Timeline — group tasks under parent shifts and restrict movement to each shift's time range.
- Next: [External Resource Drag & Drop](https://demo.mobiscroll.com/javascript/timeline/resource-management-with-dynamic-team-creation.md) - Drag external resources onto a Javascript Timeline to add team members, or drag them off to remove them — build crew scheduling and team management.
