From ab0c48b24bf30c010d68b15e5793c03013768ef5 Mon Sep 17 00:00:00 2001 From: Maksim Lakatkou Date: Thu, 4 Jun 2026 14:58:11 +0200 Subject: [PATCH 1/2] feat: add dhtmlx-js-scheduler skill --- README.md | 6 + dhtmlx-js-scheduler/SKILL.md | 110 +++++ .../references/advanced-patterns.md | 216 ++++++++++ .../references/data-and-crud.md | 261 ++++++++++++ dhtmlx-js-scheduler/references/editions.md | 51 +++ .../references/known-failures.md | 102 +++++ .../references/live-updates.md | 166 ++++++++ dhtmlx-js-scheduler/references/setup.md | 382 ++++++++++++++++++ .../references/styling-and-theming.md | 223 ++++++++++ 9 files changed, 1517 insertions(+) create mode 100644 dhtmlx-js-scheduler/SKILL.md create mode 100644 dhtmlx-js-scheduler/references/advanced-patterns.md create mode 100644 dhtmlx-js-scheduler/references/data-and-crud.md create mode 100644 dhtmlx-js-scheduler/references/editions.md create mode 100644 dhtmlx-js-scheduler/references/known-failures.md create mode 100644 dhtmlx-js-scheduler/references/live-updates.md create mode 100644 dhtmlx-js-scheduler/references/setup.md create mode 100644 dhtmlx-js-scheduler/references/styling-and-theming.md diff --git a/README.md b/README.md index 860cfae..2f0d40c 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ Building and integrating DHTMLX React Gantt (`@dhtmlx/trial-react-gantt` and `@d Building and integrating DHTMLX Angular Gantt (`@dhtmlx/trial-angular-gantt` and `@dhx/angular-gantt`) - wrapper-specific setup, data ownership and persistence patterns (`data.save` / `data.batchSave`), and theming approach, with known failure modes and concrete fixes. +### `dhtmlx-js-scheduler` + +Building and integrating the core DHTMLX JavaScript Scheduler in JavaScript and TypeScript applications. Recognises all delivery channels (`dhtmlx-scheduler` Standard/GPL, `@dhx/trial-scheduler`, `@dhx/scheduler`, ` +
+ +``` + +Globals exposed in this mode: +- `window.scheduler` — the singleton, present in every edition. +- `window.Scheduler` — the factory class, present only in PRO editions that support multiple instances. When `window.Scheduler` is `undefined`, the page is running Standard or a single-instance PRO build and `Scheduler.getSchedulerInstance()` is not available. + +The same singleton-vs-factory rules apply: do not call `scheduler.destructor()` on the global singleton in Standard/single-instance builds unless the page will be reloaded (see Cleanup), and use `Scheduler.getSchedulerInstance()` only when the global `Scheduler` is present and the edition supports it. + +See [editions.md](editions.md) for how to detect the edition when there is no `package.json` to inspect. + +## Vite + +Include the installed Scheduler package in `optimizeDeps` when Vite has trouble pre-bundling or the project already follows this pattern. Use the package name that matches the installed edition: + +```ts +// vite.config.ts +export default { + optimizeDeps: { + include: ["dhtmlx-scheduler"], // Standard + // include: ["@dhx/trial-scheduler"], // Trial + // include: ["@dhx/scheduler"], // Commercial + }, +}; +``` +## Height Rule + +The Scheduler container must have a defined height: + +```html +
+``` + +If using `height: 100%`, all parent elements must also have a defined height. Otherwise, the container will collapse. + +## Initialization + +Initialize Scheduler after the container exists: + +```ts +scheduler.init("scheduler_here", new Date(), "week"); +``` + +or: + +```ts +scheduler.init(container, new Date(), "week"); +``` + +Load client-side data with: + +```ts +scheduler.parse(events); +``` + +Keep all Scheduler-related logic (initialization, configuration, data loading, events, DataProcessor, cleanup) in a single module. Avoid scattering direct Scheduler calls across the app. + +## Plugins + +Enable plugins before calling APIs that depend on them: + +```ts +scheduler.plugins({ + recurring: true, + tooltip: true, + readonly: true, +}); +``` + +In v6.0+, extensions are bundled in `dhtmlxscheduler.js` and activated through `scheduler.plugins({...})`; do not import old `ext/dhtmlxscheduler_*.js` files unless the project is on an older version. + +## Markup vs Header Config + +You can initialize with Scheduler markup: + +```html +
+
+
 
+
 
+
+
+
+
+
+
+
+
+
+``` + +For app layouts, prefer `scheduler.config.header` plus an empty container because it is easier to make responsive: + +```ts +scheduler.config.header = ["day", "week", "month", "date", "prev", "today", "next"]; +``` + +## Cleanup + +Cleanup depends on whether the instance was created via the singleton or the factory. The two paths are not interchangeable. + +**Factory instance (`Scheduler.getSchedulerInstance()`):** call `destructor()` when the container is removed. This clears data, removes event handlers, detaches the instance from the DOM, and disposes the attached DataProcessor. Safe because the factory created a dedicated instance. + +```ts +const schedulerA = Scheduler.getSchedulerInstance(); +// ... +schedulerA.destructor(); +``` + +**Singleton (`import { scheduler }`):** **do not** call `scheduler.destructor()` for cleanup-and-reuse. The singleton is the only Scheduler instance available to the page, and destroying it leaves the module in a non-functional state until a full page reload. For reuse, clear data and detach the handlers that the init code attached: + +```ts +// during init — capture handler ids +const handlerIds = [ + scheduler.attachEvent("onEventChanged", onEventChanged), + scheduler.attachEvent("onEventDeleted", onEventDeleted), +]; + +// on cleanup +scheduler.clearAll(); +for (const id of handlerIds) scheduler.detachEvent(id); +``` + +The DataProcessor attached to the singleton, however, is safe to destroy and recreate. Keep a reference to the DataProcessor returned by `scheduler.createDataProcessor(...)` and call `dataProcessor.destructor()` on cleanup; create a fresh DataProcessor when Scheduler is activated again: + +```ts +// during init +const dataProcessor = scheduler.createDataProcessor(routerFn); + +// on cleanup +dataProcessor.destructor(); + +// on reactivation +const dataProcessor = scheduler.createDataProcessor(routerFn); +``` + +Clearing `container.innerHTML` afterwards is rarely needed and only useful when the host element survives across re-mounts. + +## Config And Templates + +Common configuration areas: + +- `scheduler.config.header` — navigation controls and visible views +- `scheduler.config.date_format` — event date parsing format +- `scheduler.config.first_hour`, `last_hour`, `time_step` — time scale and time resolution +- `scheduler.config.readonly`, `readonly_form` — interaction settings +- `scheduler.config.lightbox.sections` — event editing UI +- `scheduler.config.show_loading` — loading indicator behavior +- `scheduler.xy.*` — sizing values +- view-specific configuration for Timeline, Units, Grid, Agenda, Year, and other Scheduler views + +Templates: + +- `scheduler.templates.*` — formatting of event text, CSS classes, scale labels, tooltips, month cells, Timeline cells, Timeline rows, Units output, and view-specific rendering + +Example: + +```ts +// Event text +scheduler.templates.event_text = (start, end, event) => { + return event.text; +}; + +// Event CSS class +scheduler.templates.event_class = (start, end, event) => { + return event.status ? `status-${event.status}` : ""; +}; + +// Tooltip text +scheduler.templates.tooltip_text = (start, end, event) => { + return event.text; +}; + +// Highlight month cells, e.g. weekends +scheduler.templates.month_date_class = (date) => { + const day = date.getDay(); + return day === 0 || day === 6 ? "weekend" : ""; +}; +``` + +Do not guess template signatures. Verify unfamiliar templates with MCP. + +**Security — templates return raw HTML.** Whatever a template returns is inserted into the DOM as HTML, not as text. Any user-supplied field (`event.text`, descriptions, resource names, notes, free-form custom fields) that reaches a template unsanitized is an XSS vector — e.g. an event named `` will execute on render. + +Mitigate at both ends: + +- Sanitize / escape user-supplied text on save in the backend (defense in depth). +- Escape — or sanitize via a vetted library like DOMPurify — before the data reaches Scheduler (`parse`, `addEvent`, `updateEvent`, `remoteUpdates`, DataProcessor responses), or escape inside the template before returning: + +```ts +const escapeHtml = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({ + "&": "&", + "<": "<", + ">": ">", + "\"": """, + "'": "'", +})[character]); + +scheduler.templates.event_text = (_start, _end, event) => escapeHtml(event.text); +scheduler.templates.tooltip_text = (_start, _end, event) => `${escapeHtml(event.text)}`; +``` + +If the template intentionally returns markup, escape only the user-supplied substrings, not the static markup. + +`scheduler.templates.parse_date` and `scheduler.templates.format_date` can be overridden to accept or emit non-default date formats (useful when the backend returns timestamps or compact strings): + +```ts +scheduler.config.date_format = "%Y-%m-%d %H:%i"; + +const parseDate = scheduler.date.str_to_date(scheduler.config.date_format); +const formatDate = scheduler.date.date_to_str(scheduler.config.date_format); + +scheduler.templates.parse_date = (value) => parseDate(value); +scheduler.templates.format_date = (date) => formatDate(date); +``` + +## Events + +Wire app logic through documented events rather than direct mutation. Attach with `scheduler.attachEvent` and detach (or destroy via `scheduler.destructor`) on unmount: + +```ts +const handlerId = scheduler.attachEvent("onEventChanged", (id, event) => { + // app-side reactions, persistence triggers, store updates +}); + +// later +scheduler.detachEvent(handlerId); +``` + +For one-shot subscriptions (e.g. running first-init startup code through `onSchedulerReady`), pass `{ once: true }` so Scheduler auto-detaches the handler after the first invocation: + +```ts +scheduler.attachEvent("onSchedulerReady", () => { + // runs once, even if scheduler.init is called again later +}, { once: true }); +``` + +Event names and argument lists are stable but numerous. Verify unfamiliar events with MCP before wiring them. + +## Localization + +Switch the active locale at runtime with `scheduler.i18n.setLocale("xx")`. Per-label overrides go through `scheduler.locale.labels.*` or a partial locale object passed to `scheduler.i18n.setLocale(...)`: + +```ts +scheduler.i18n.setLocale("ru"); +scheduler.locale.labels.day_tab = "Day"; +scheduler.locale.labels.week_tab = "Week"; +scheduler.locale.labels.section_description = "Description"; +``` + +Or: + +```ts +scheduler.i18n.setLocale({ + labels: { + day_tab: "Day", + week_tab: "Week", + section_description: "Description", + }, +}); +``` + +Apply locale changes before `scheduler.init` when possible. If changed after init, call `scheduler.render()`. + +## Read-Only Mode + +To make the Scheduler non-editable: + +```ts +scheduler.config.readonly = true; +``` + +This makes the Scheduler non-editable and prevents users from opening the lightbox. + +To allow opening the lightbox while preventing edits inside it: + +```ts +scheduler.config.readonly_form = true; +``` + +This requires the `readonly` extension. + +Read-only behavior can also be applied to individual events: + +```ts +const event = scheduler.getEvent(id); +event.readonly = true; +``` + +Note that read-only mode is a UI restriction, not a security boundary. Also remove or hide app-level controls that mutate data (e.g. add buttons, undo/redo) and enforce permissions in DataProcessor routes and on the backend. diff --git a/dhtmlx-js-scheduler/references/styling-and-theming.md b/dhtmlx-js-scheduler/references/styling-and-theming.md new file mode 100644 index 0000000..c67afd1 --- /dev/null +++ b/dhtmlx-js-scheduler/references/styling-and-theming.md @@ -0,0 +1,223 @@ +# Styling And Theming + +Use this file when theming Scheduler, matching an app design system, or styling events, scales, lightboxes, blocked time, and advanced views. + +## Contents + +- Styling Priority +- Skins +- Theme Variables First +- Use Config For Size And Geometry +- Templates +- Data-Driven Styling +- Common Selectors And Escape Hatches +- Practical Guardrails + +## Styling Priority + +Use this order by default: +1. built-in skins via `scheduler.skin` or `scheduler.setSkin` +2. CSS variables for global theme alignment +3. config for size and geometry +4. templates for semantic or data-driven styling +5. direct CSS selectors only for structural exceptions + +## Skins + +Built-in skins: +- `"terrace"` (default) +- `"dark"` +- `"material"` +- `"flat"` +- `"contrast-black"` +- `"contrast-white"` + +Starting from Scheduler v7.0, all skins are bundled in `dhtmlxscheduler.css`. + +Set the skin before calling `scheduler.init()`: + +```ts +scheduler.skin = appTheme === "dark" ? "dark" : "terrace"; + +scheduler.init("scheduler_here", new Date(), "week"); +``` + +Or switch the skin at runtime: + +```ts +scheduler.setSkin("dark"); +``` + +Use the application theme as the single source of truth. Avoid introducing a Scheduler-specific theme switch when a global theme already exists. + +## Theme Variables First + +CSS variables are the preferred customization path. + +Define shared variables at `:root` so inheritance works correctly across the whole component: + +```css +:root { + --dhx-scheduler-base-colors-primary: #2563eb; + --dhx-scheduler-container-background: #ffffff; + --dhx-scheduler-container-color: #0f172a; + --dhx-scheduler-event-background: #2563eb; + --dhx-scheduler-event-color: #ffffff; +} +``` + +### High-Impact Variables + +Typography and surfaces: +- `--dhx-scheduler-font-family` +- `--dhx-scheduler-font-size` +- `--dhx-scheduler-container-background` +- `--dhx-scheduler-container-color` +- `--dhx-scheduler-popup-background` +- `--dhx-scheduler-popup-color` + +Primary and semantic colors: +- `--dhx-scheduler-base-colors-primary` +- `--dhx-scheduler-base-colors-background` +- `--dhx-scheduler-base-colors-text-base` +- `--dhx-scheduler-base-colors-border` + +Events: +- `--dhx-scheduler-event-background` +- `--dhx-scheduler-event-color` +- `--dhx-scheduler-event-border` + +Use CSS variables for application-wide theming and design system alignment. + +Per-event `color` and `textColor` override event CSS variables through inline CSS variables. Use them only when colors are true event data; use classes and templates for semantic styling. + +## Templates + +Templates should be the primary customization mechanism for data-driven content. + +```ts +scheduler.templates.event_text = (start, end, event) => { + return escapeHtml(event.text); +}; +``` + +Common template areas: +- event text and content +- event CSS classes +- hour scale labels +- quick info and tooltip content +- agenda view rendering +- units view rendering +- year view rendering + +## Use Config For Size And Geometry + +Some visible styling is controlled by Scheduler configuration, not CSS: + +```ts +scheduler.xy.scale_width = 60; +scheduler.xy.scale_height = 25; +scheduler.xy.bar_height = 24; +scheduler.xy.scroll_width = 18; + +scheduler.config.hour_size_px = 90; +``` + +Common geometry settings: +- `scheduler.xy.scale_width` +- `scheduler.xy.scale_height` +- `scheduler.xy.bar_height` +- `scheduler.xy.scroll_width` +- `scheduler.xy.month_scale_height` +- `scheduler.xy.min_event_height` +- `scheduler.config.hour_size_px` + +Apply geometry-related settings before calling `scheduler.init()`. + +## Data-Driven Styling + +When styling depends on event data or time cell data, return CSS classes from templates and style those classes with CSS variables. + +```ts +scheduler.templates.event_class = (_start, _end, event) => { + return event.priority === "high" ? "event-priority-high" : ""; +}; +``` + +```css +.event-priority-high { + --dhx-scheduler-event-background: #dc2626; + --dhx-scheduler-event-color: #fff; +} +``` + +For background time cells in Day and Week views, use documented templates such as `scheduler.templates.time_slot_class` rather than broad selectors. + +### Events + +Prefer `event_class` to style groups of events by priority, status, owner, or any other semantic field: + +```js +scheduler.templates.event_class = (_start, _end, event) => { + return event.priority ? `priority_${event.priority}` : ""; +}; +``` + +```css +.priority_high { + --dhx-scheduler-event-background: #dc2626; + --dhx-scheduler-event-color: #ffffff; +} +``` + +Supported event color shortcut fields: +- `color` +- `textColor` + +Important caveat: +- these fields are applied by Scheduler directly to the event container and text +- they can override class-based event styling depending on CSS precedence + +Use shortcut color fields only when the app truly stores per-event visual values as data. For reusable semantic styling, `event_class` is usually safer. + +Advanced pattern: +- if colors come from backend-driven entities such as owners, calendars, or stages, generate CSS classes from the loaded dataset and return those classes from `event_class` + +## Common Selectors And Escape Hatches + +Use these when skins, variables, and templates are not enough. + +Scheduler structure: +- `.dhx_cal_container` +- `.dhx_cal_navline` +- `.dhx_cal_header` +- `.dhx_cal_data` +- `.dhx_cal_tab` + +Events: +- `.dhx_cal_event` +- `.dhx_cal_event_line` +- `.dhx_cal_event_clear` + +Mini calendar: +- `.dhx_month_head` +- `.dhx_calendar_click` +- `.dhx_month_head.dhx_year_event` +- `.dhx_now` + +Lightbox: +- `.dhx_cal_light` + +Prefer CSS variables and templates over direct selectors whenever possible. + +Scope overrides to a wrapper class when multiple Scheduler instances or other DHTMLX widgets exist on the page. + +## Practical Guardrails + +- Prefer Scheduler CSS variables for theme-wide restyling before touching selectors. +- Prefer templates when styling depends on event or time cell data. +- Prefer scoped selectors over global overrides when changing only one Scheduler area. +- Apply geometry changes through documented `scheduler.xy` and `scheduler.config` settings instead of CSS. +- Keep skin changes wired to the application theme. +- Remember that the Material skin requires Roboto to be imported manually. +- Avoid selector-heavy overrides that depend on internal DOM structure. From 939faaeec58cd4ff17e864102ff364fc135ee2de Mon Sep 17 00:00:00 2001 From: Maksim Lakatkou Date: Thu, 4 Jun 2026 22:00:06 +0200 Subject: [PATCH 2/2] fix: add DHTMLX npm registry configuration for trial scheduler install --- dhtmlx-js-scheduler/references/setup.md | 1 + 1 file changed, 1 insertion(+) diff --git a/dhtmlx-js-scheduler/references/setup.md b/dhtmlx-js-scheduler/references/setup.md index 559297d..b9d704d 100644 --- a/dhtmlx-js-scheduler/references/setup.md +++ b/dhtmlx-js-scheduler/references/setup.md @@ -13,6 +13,7 @@ npm install dhtmlx-scheduler Professional Evaluation (Trial, full PRO surface, 30 days): ```bash +npm config set @dhx:registry=https://npm.dhtmlx.com npm install @dhx/trial-scheduler ```