@object-ui/plugin-gantt 17.3.0 → 17.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -66,8 +66,11 @@ to opt in:
66
66
  <GanttView
67
67
  tasks={tasks}
68
68
  onTaskUpdate={(task, { start, end }) => {
69
- // start/end are JS Date objects already snapped to whole days,
70
- // and the resize-left/right cases clamp so end - start >= 1 day.
69
+ // `changes` is a Partial<Pick<GanttTask, 'title' | 'start' | 'end' | 'progress'>>:
70
+ // only the keys the edit actually touched are present. A bar drag/resize
71
+ // sends start + end, but the progress grip sends just { progress } — so
72
+ // guard, or a progress drag writes both dates back as undefined.
73
+ if (!start || !end) return;
71
74
  save(task.id, { start, end });
72
75
  }}
73
76
  />
@@ -87,30 +90,95 @@ pnpm add @object-ui/plugin-gantt
87
90
  // In your app entry point (e.g., App.tsx or main.tsx)
88
91
  import '@object-ui/plugin-gantt';
89
92
 
90
- // Now you can use gantt types in your schemas
93
+ // Now you can use gantt types in your schemas.
94
+ // The gantt is RECORD-DRIVEN: it names a data source and the fields to read.
95
+ // It does not take a task array — see "Schema API" below.
91
96
  const schema = {
92
97
  type: 'gantt',
93
- tasks: [
94
- {
95
- id: '1',
96
- name: 'Project Setup',
97
- start: '2024-01-01',
98
- end: '2024-01-05',
99
- progress: 100
100
- }
101
- ]
98
+ objectName: 'project_tasks',
99
+ titleField: 'name',
100
+ startDateField: 'start_date',
101
+ endDateField: 'end_date',
102
+ progressField: 'completion_percentage'
102
103
  };
103
104
  ```
104
105
 
105
- ### Manual Registration
106
+ ### What the side-effect import registers
107
+
108
+ Registration is *only* a side effect of importing the package — the single
109
+ `import '@object-ui/plugin-gantt'` above is the whole of it. There is no
110
+ components map to iterate over: importing the entry point runs the
111
+ `ComponentRegistry.register(...)` calls at the bottom of `src/index.tsx`, which
112
+ claim these schema types:
113
+
114
+ | Schema `type` | Namespaced key | Renderer |
115
+ | --- | --- | --- |
116
+ | `object-gantt` | `plugin-gantt:object-gantt` | `ObjectGanttRenderer` |
117
+ | `gantt` | `view:gantt` | `ObjectGanttRenderer` |
118
+
119
+ Both spellings resolve — `register` stores the namespaced key *and* a bare-`type`
120
+ fallback. Both keys declare the same two inputs: `objectName` (required) and the
121
+ `gantt` configuration object.
122
+
123
+ `ObjectGanttRenderer` is a thin wrapper: it pulls `dataSource` off the renderer
124
+ context and hands the schema to `ObjectGantt`.
125
+
126
+ ### Public exports
127
+
128
+ The package exports components, helpers and their types — not a registry map:
129
+
130
+ ```typescript
131
+ import {
132
+ ObjectGantt, // ObjectQL-integrated gantt: loads records, writes back edits
133
+ ObjectGanttRenderer, // the registered renderer for `object-gantt` / `gantt`
134
+ GanttView, // the standalone timeline component
135
+ QuickFilterBar, // the toolbar's quick-filter dropdowns
136
+ ResourceWorkload, // resource × period workload grid
137
+ normalizeDependencies, // raw dependency-field value -> GanttDependency[]
138
+ normalizeTaskType, // raw type-field value -> GanttTaskType
139
+ normalizeShiftSegments, // raw timeSegments config -> NormShiftSegments
140
+ parseHHMM,
141
+ shiftDayStart,
142
+ bandAt,
143
+ computeWorkload,
144
+ } from '@object-ui/plugin-gantt';
145
+
146
+ import type {
147
+ ObjectGanttProps,
148
+ GanttViewProps,
149
+ GanttTask,
150
+ GanttTaskType,
151
+ GanttViewMode,
152
+ GanttDependency,
153
+ GanttDependencyObject,
154
+ GanttLinkType,
155
+ GanttMarker,
156
+ QuickFilterDef,
157
+ QuickFilterBarProps,
158
+ QuickFilterField,
159
+ QuickFilterOption,
160
+ QuickFilterLabels,
161
+ ShiftSegmentsConfig,
162
+ ShiftBandConfig,
163
+ NormShiftSegments,
164
+ NormShiftBand,
165
+ ResourceWorkloadProps,
166
+ WorkloadColumn,
167
+ WorkloadOptions,
168
+ ResourceCell,
169
+ ResourceLoad,
170
+ } from '@object-ui/plugin-gantt';
171
+ ```
172
+
173
+ To serve the gantt under a registry key of your own, register the exported
174
+ renderer under that key:
106
175
 
107
176
  ```typescript
108
- import { ganttComponents } from '@object-ui/plugin-gantt';
109
177
  import { ComponentRegistry } from '@object-ui/core';
178
+ import { ObjectGanttRenderer } from '@object-ui/plugin-gantt';
110
179
 
111
- // Register gantt components
112
- Object.entries(ganttComponents).forEach(([type, component]) => {
113
- ComponentRegistry.register(type, component);
180
+ ComponentRegistry.register('my-gantt', ObjectGanttRenderer, {
181
+ namespace: 'my-app',
114
182
  });
115
183
  ```
116
184
 
@@ -118,109 +186,218 @@ Object.entries(ganttComponents).forEach(([type, component]) => {
118
186
 
119
187
  ### Gantt Chart
120
188
 
121
- Display project timeline with tasks:
189
+ The gantt node is **record-driven**: it names *where records come from* and
190
+ *which fields* carry the schedule. It does not carry a task array — the task
191
+ objects further down this page are the component's runtime shape, produced by
192
+ `ObjectGantt` from each record.
193
+
194
+ `ObjectGantt` decides what to render from exactly two reads
195
+ (`src/ObjectGantt.tsx`):
196
+
197
+ **1. Where the records come from — `getDataConfig`.** The first of these three
198
+ that is present wins; if none is present there is no data config at all and the
199
+ chart renders empty:
122
200
 
123
201
  ```typescript
124
202
  {
125
203
  type: 'gantt',
126
- tasks: GanttTask[],
127
- viewMode?: 'day' | 'week' | 'month',
128
- onTaskClick?: (task) => void,
129
- onTaskUpdate?: (task) => void,
130
- readOnly?: boolean, // disable all editing (drag/resize/inline/delete/link/undo)
131
- className?: string
204
+
205
+ // Pick ONE of the three:
206
+ objectName: 'project_tasks', // load through the host DataSource
207
+ // data: { provider: 'value', items: [ /* records */ ] }, // inline records
208
+ // staticData: [ /* records */ ], // shorthand for the above
132
209
  }
133
210
  ```
134
211
 
212
+ `data` is the spec's `ViewData` union — `{ provider: 'object', object }`,
213
+ `{ provider: 'value', items }`, `{ provider: 'api', read, write }` or
214
+ `{ provider: 'schema', schemaId }`.
215
+
216
+ **2. How the fields map — `getGanttConfig`.** Two spellings, checked in order.
217
+ Top-level keys are used **only when `startDateField` and `endDateField` are both
218
+ present**; otherwise the whole `gantt` block is read instead:
219
+
220
+ ```typescript
221
+ {
222
+ type: 'gantt',
223
+ objectName: 'project_tasks',
224
+
225
+ // (a) flat spelling — requires BOTH date fields to be taken
226
+ startDateField: 'start_date',
227
+ endDateField: 'end_date',
228
+ titleField: 'name', // defaults to 'name'
229
+ progressField: 'completion_percentage',
230
+ dependenciesField: 'dependent_task_ids',
231
+ colorField: 'bar_color',
232
+ parentField: 'parent_task',
233
+ typeField: 'task_kind',
234
+ viewMode: 'week', // 'day'|'week'|'month'|'quarter'|'year'
235
+
236
+ // (b) …or the same configuration as one block:
237
+ // gantt: { startDateField: 'start_date', endDateField: 'end_date', … }
238
+ }
239
+ ```
240
+
241
+ `viewMode` is real authoring surface (`ObjectGanttSchema`, derived from the
242
+ spec's `GanttConfigSchema.viewMode`) and is honoured by **both** renderer
243
+ branches — the timeline and the resource-workload grid. It reaches the renderer
244
+ through `getGanttConfig`, so it only takes effect alongside a taken gantt
245
+ config: as a top-level key it needs `startDateField` + `endDateField` beside it,
246
+ or it can sit inside the `gantt` block. Omitting it is meaningful — a persisted
247
+ layout then seeds the granularity before the renderer's `'day'` fallback.
248
+
249
+ #### Keys this page used to teach that the renderer never reads
250
+
251
+ Earlier revisions of this README showed a task-array schema. Those keys have no
252
+ read site anywhere in `src/` — a schema built from them renders an **empty
253
+ chart with no diagnostic**, because `type: 'gantt'` *is* a registered type, so
254
+ the node mounts and simply finds nothing to draw:
255
+
256
+ | Key shown before | Status | Use instead |
257
+ | --- | --- | --- |
258
+ | `tasks` | never read off the schema | `objectName` / `data` / `staticData` |
259
+ | `object` | never read | `objectName` |
260
+ | `nameField` | never read | `titleField` |
261
+ | `startField` | never read | `startDateField` |
262
+ | `endField` | never read | `endDateField` |
263
+ | `fields: { name, start, end, … }` | never read | the flat `*Field` keys, or the `gantt` block |
264
+ | `onTaskClick`, `onTaskUpdate` | never read *off the schema* | React props on `<ObjectGantt>` / `<GanttView>` — functions do not belong in serializable metadata |
265
+ | `className` | never read off the schema | a React prop on `<ObjectGantt>` |
266
+
267
+ Keys that **are** read but only through a cast, so they are easy to miss when
268
+ grepping — all of them genuine, all optional: `readOnly` (disables every edit
269
+ path — drag/resize/inline/delete/link/undo), `mobileReadOnly`, `markers`,
270
+ `navigation`, `skipWeekends`, `holidays`, `criticalPath`, `showBaselines`,
271
+ `persistLayout` / `viewName`, `label`.
272
+
135
273
  ### Task Structure
136
274
 
275
+ `GanttTask` is the **component's runtime** task — what `<GanttView>` renders and
276
+ what `ObjectGantt` produces from each record. Dates are real `Date` objects and
277
+ the label field is `title` (`src/GanttView.tsx`):
278
+
137
279
  ```typescript
138
280
  interface GanttTask {
139
- id: string;
140
- name: string;
141
- start: string; // ISO date string
142
- end: string; // ISO date string
143
- progress: number; // 0-100
144
- dependencies?: string[]; // Task IDs
145
- assignee?: string;
146
- color?: string; // Tailwind color class
281
+ id: string | number;
282
+ title: string;
283
+ start: Date;
284
+ end: Date;
285
+ progress: number; // 0-100
286
+ dependencies?: GanttDependency[]; // predecessor id, optionally with a link type
287
+ parent?: string | number | null; // builds the hierarchy; unknown ids render as roots
288
+ type?: GanttTaskType; // 'task' | 'summary' | 'milestone' | 'group'
289
+ color?: string; // bar fill — any CSS color, e.g. '#3b82f6'
290
+ borderColor?: string; // alert outline, fill untouched
291
+ locked?: boolean; // view-only row (still clickable)
292
+ baselineStart?: Date; // planned-vs-actual reference bar
293
+ baselineEnd?: Date;
294
+ data?: any; // the source record behind the row
147
295
  }
148
296
  ```
149
297
 
298
+ A few more optional fields (`fields` for tooltip rows, `hasOwnDates`) are
299
+ populated by `ObjectGantt` itself — see `GanttTask` in `src/GanttView.tsx` for
300
+ the full declaration.
301
+
150
302
  ## Examples
151
303
 
152
304
  ### Basic Gantt Chart
153
305
 
306
+ Inline records, no backend — `data.items` carries the records and the `*Field`
307
+ keys say which of their fields the chart reads. Note that the record field
308
+ names are yours; only the `*Field` keys are fixed vocabulary.
309
+
154
310
  ```typescript
155
311
  const schema = {
156
312
  type: 'gantt',
157
313
  viewMode: 'week',
158
- tasks: [
159
- {
160
- id: '1',
161
- name: 'Project Planning',
162
- start: '2024-01-01',
163
- end: '2024-01-07',
164
- progress: 100,
165
- color: 'bg-blue-500'
166
- },
167
- {
168
- id: '2',
169
- name: 'Design Phase',
170
- start: '2024-01-08',
171
- end: '2024-01-21',
172
- progress: 75,
173
- dependencies: ['1'],
174
- color: 'bg-purple-500'
175
- },
176
- {
177
- id: '3',
178
- name: 'Development',
179
- start: '2024-01-22',
180
- end: '2024-02-15',
181
- progress: 30,
182
- dependencies: ['2'],
183
- color: 'bg-green-500'
184
- },
185
- {
186
- id: '4',
187
- name: 'Testing',
188
- start: '2024-02-16',
189
- end: '2024-02-28',
190
- progress: 0,
191
- dependencies: ['3'],
192
- color: 'bg-orange-500'
193
- }
194
- ]
314
+ startDateField: 'start',
315
+ endDateField: 'end',
316
+ titleField: 'name',
317
+ progressField: 'progress',
318
+ dependenciesField: 'dependencies',
319
+ colorField: 'color',
320
+ data: {
321
+ provider: 'value',
322
+ items: [
323
+ {
324
+ id: '1',
325
+ name: 'Project Planning',
326
+ start: '2024-01-01',
327
+ end: '2024-01-07',
328
+ progress: 100,
329
+ // bar fill goes straight into an inline `backgroundColor` —
330
+ // it must be a CSS color, NOT a Tailwind class
331
+ color: '#3b82f6'
332
+ },
333
+ {
334
+ id: '2',
335
+ name: 'Design Phase',
336
+ start: '2024-01-08',
337
+ end: '2024-01-21',
338
+ progress: 75,
339
+ dependencies: ['1'],
340
+ color: '#a855f7'
341
+ },
342
+ {
343
+ id: '3',
344
+ name: 'Development',
345
+ start: '2024-01-22',
346
+ end: '2024-02-15',
347
+ progress: 30,
348
+ dependencies: ['2'],
349
+ color: '#22c55e'
350
+ },
351
+ {
352
+ id: '4',
353
+ name: 'Testing',
354
+ start: '2024-02-16',
355
+ end: '2024-02-28',
356
+ progress: 0,
357
+ dependencies: ['3'],
358
+ color: '#f97316'
359
+ }
360
+ ]
361
+ }
195
362
  };
196
363
  ```
197
364
 
198
365
  ### Interactive Gantt
199
366
 
200
- ```typescript
201
- const schema = {
202
- type: 'gantt',
203
- tasks: [/* tasks */],
204
- onTaskClick: (task) => {
205
- console.log('Task clicked:', task);
206
- // Show task details
207
- },
208
- onTaskUpdate: (updatedTask) => {
209
- console.log('Task updated:', updatedTask);
210
- // Save changes to backend
211
- }
212
- };
367
+ Callbacks are **not** schema keys — the schema is serializable metadata, and a
368
+ function cannot survive it. Through the registered `gantt` / `object-gantt`
369
+ types the whole CRUD lifecycle is already wired to the host `DataSource`
370
+ (create/edit/drag/delete/detail drawer), so there is usually nothing to pass.
371
+ When you render the component yourself, hand the callbacks in as React props:
372
+
373
+ ```tsx
374
+ import { ObjectGantt } from '@object-ui/plugin-gantt';
375
+
376
+ <ObjectGantt
377
+ schema={{
378
+ type: 'gantt',
379
+ objectName: 'project_tasks',
380
+ startDateField: 'start_date',
381
+ endDateField: 'end_date',
382
+ titleField: 'name',
383
+ }}
384
+ dataSource={dataSource}
385
+ onTaskClick={(record) => console.log('Task clicked:', record)}
386
+ />
213
387
  ```
214
388
 
389
+ To turn editing off from the metadata instead, set `readOnly: true` on the
390
+ schema — that one *is* read.
391
+
215
392
  ### With ObjectQL Integration
216
393
 
217
394
  ```typescript
218
395
  const schema = {
219
396
  type: 'object-gantt',
220
- object: 'project_tasks',
221
- nameField: 'name',
222
- startField: 'start_date',
223
- endField: 'end_date',
397
+ objectName: 'project_tasks',
398
+ titleField: 'name',
399
+ startDateField: 'start_date',
400
+ endDateField: 'end_date',
224
401
  progressField: 'completion_percentage',
225
402
  dependenciesField: 'dependent_task_ids'
226
403
  };
@@ -234,6 +411,7 @@ The Gantt chart renders one timeline column per unit of the active scale:
234
411
  - **week** - one column per week (starting Monday)
235
412
  - **month** - one column per calendar month
236
413
  - **quarter** - one column per quarter (Q1–Q4)
414
+ - **year** - one column per year (decade band above)
237
415
 
238
416
  A two-row header shows the grouping above the units (months above days/weeks,
239
417
  years above months/quarters). The toolbar's segmented control switches scales
@@ -243,11 +421,17 @@ Drag snapping follows the active scale: bars snap to days in day view, weeks
243
421
  in week view, and whole calendar months/quarters (duration preserved) in the
244
422
  coarse views.
245
423
 
424
+ Set the initial scale with `viewMode`. It is read through the gantt config, so
425
+ it needs the field mapping beside it (or a `gantt` block of its own):
426
+
246
427
  ```typescript
247
428
  const schema = {
248
429
  type: 'gantt',
249
430
  viewMode: 'month',
250
- tasks: [/* tasks */]
431
+ objectName: 'project_tasks',
432
+ startDateField: 'start_date',
433
+ endDateField: 'end_date',
434
+ titleField: 'name'
251
435
  };
252
436
  ```
253
437
 
@@ -266,12 +450,19 @@ Zero-duration tasks (`end <= start`) — or tasks whose `type` is
266
450
  can be dragged to move but not resized; dependency arrows anchor at the
267
451
  diamond center.
268
452
 
453
+ These are runtime `GanttTask` objects (what `<GanttView>` takes directly) —
454
+ dates are real `Date`s and the label field is `title`. Coming from records
455
+ instead, the same tree is configured with `parentField` / `typeField`.
456
+
269
457
  ```typescript
270
- const tasks = [
271
- { id: 'phase1', title: 'Phase 1', start: '…', end: '…', progress: 0 }, // summary (has children)
272
- { id: 't1', title: 'Design', parent: 'phase1', start: '…', end: '…', progress: 80 },
273
- { id: 't2', title: 'Build', parent: 'phase1', start: '…', end: '…', progress: 20 },
274
- { id: 'launch', title: 'Launch', type: 'milestone', start: '2024-07-01', end: '2024-07-01', progress: 0 },
458
+ import type { GanttTask } from '@object-ui/plugin-gantt';
459
+
460
+ const tasks: GanttTask[] = [
461
+ // summary (has children) — its span and progress are rolled up
462
+ { id: 'phase1', title: 'Phase 1', start: new Date('2024-06-01'), end: new Date('2024-06-30'), progress: 0 },
463
+ { id: 't1', title: 'Design', parent: 'phase1', start: new Date('2024-06-01'), end: new Date('2024-06-14'), progress: 80 },
464
+ { id: 't2', title: 'Build', parent: 'phase1', start: new Date('2024-06-15'), end: new Date('2024-06-30'), progress: 20 },
465
+ { id: 'launch', title: 'Launch', type: 'milestone', start: new Date('2024-07-01'), end: new Date('2024-07-01'), progress: 0 },
275
466
  ];
276
467
  ```
277
468
 
@@ -329,33 +520,38 @@ Through the schema, pass the same array as `markers` on the gantt node.
329
520
  Link tasks to show dependencies:
330
521
 
331
522
  ```typescript
332
- const tasks = [
523
+ import type { GanttTask } from '@object-ui/plugin-gantt';
524
+
525
+ const tasks: GanttTask[] = [
333
526
  {
334
527
  id: 'task-1',
335
- name: 'Foundation',
336
- start: '2024-01-01',
337
- end: '2024-01-10',
528
+ title: 'Foundation',
529
+ start: new Date('2024-01-01'),
530
+ end: new Date('2024-01-10'),
338
531
  progress: 100
339
532
  },
340
533
  {
341
534
  id: 'task-2',
342
- name: 'Building',
343
- start: '2024-01-11',
344
- end: '2024-01-25',
535
+ title: 'Building',
536
+ start: new Date('2024-01-11'),
537
+ end: new Date('2024-01-25'),
345
538
  progress: 50,
346
539
  dependencies: ['task-1'] // Depends on task-1
347
540
  },
348
541
  {
349
542
  id: 'task-3',
350
- name: 'Finishing',
351
- start: '2024-01-26',
352
- end: '2024-02-05',
543
+ title: 'Finishing',
544
+ start: new Date('2024-01-26'),
545
+ end: new Date('2024-02-05'),
353
546
  progress: 0,
354
547
  dependencies: ['task-2'] // Depends on task-2
355
548
  }
356
549
  ];
357
550
  ```
358
551
 
552
+ Through a data source the same links come from `dependenciesField` on the
553
+ schema, and the tree/label fields from `parentField` / `titleField`.
554
+
359
555
  Dependencies render as arrows from the predecessor bar to the dependent bar.
360
556
  Arrows follow bars live while dragging, and hovering a bar highlights its links.
361
557
 
@@ -380,8 +576,13 @@ be a CSV string (`"task1, task2"`), an array of ids, or an array of objects —
380
576
 
381
577
  ## Integration with Data Sources
382
578
 
383
- ```typescript
579
+ The adapter is **not** a schema key — it reaches the renderer through the
580
+ renderer context (or as an explicit `dataSource` prop), while the schema names
581
+ the object and the fields:
582
+
583
+ ```tsx
384
584
  import { createObjectStackAdapter } from '@object-ui/data-objectstack';
585
+ import { ObjectGantt } from '@object-ui/plugin-gantt';
385
586
 
386
587
  const dataSource = createObjectStackAdapter({
387
588
  baseUrl: 'https://api.example.com',
@@ -390,48 +591,70 @@ const dataSource = createObjectStackAdapter({
390
591
 
391
592
  const schema = {
392
593
  type: 'object-gantt',
393
- dataSource,
394
- object: 'tasks',
395
- fields: {
396
- name: 'task_name',
397
- start: 'start_date',
398
- end: 'end_date',
399
- progress: 'progress_percent'
400
- }
594
+ objectName: 'tasks',
595
+ titleField: 'task_name',
596
+ startDateField: 'start_date',
597
+ endDateField: 'end_date',
598
+ progressField: 'progress_percent'
401
599
  };
600
+
601
+ <ObjectGantt schema={schema} dataSource={dataSource} />
402
602
  ```
403
603
 
604
+ ⚠️ A `dataSource` key **on the schema node** is a different thing with the same
605
+ name: it is the spec's `PageComponentSchema.dataSource` *binding* —
606
+ `{ object, view?, filter?, sort?, limit? }`, a declarative reference resolved
607
+ against the host — **not** an adapter instance. The renderer guards against the
608
+ confusion explicitly, so putting a live adapter there does not wire anything up.
609
+
404
610
  ## TypeScript Support
405
611
 
612
+ Two different vocabularies, two different packages — don't mix them up.
613
+
614
+ **The component's runtime types** come from this package. Use them when you
615
+ render `<GanttView>` (or `ObjectGantt`) yourself in React:
616
+
406
617
  ```typescript
407
- import type { GanttSchema, GanttTask } from '@object-ui/plugin-gantt';
618
+ import type { GanttTask, GanttViewProps } from '@object-ui/plugin-gantt';
408
619
 
409
620
  const task: GanttTask = {
410
621
  id: '1',
411
- name: 'My Task',
412
- start: '2024-01-01',
413
- end: '2024-01-10',
622
+ title: 'My Task',
623
+ start: new Date('2024-01-01'),
624
+ end: new Date('2024-01-10'),
414
625
  progress: 50,
415
- dependencies: []
626
+ dependencies: [],
416
627
  };
417
628
 
418
- const gantt: GanttSchema = {
419
- type: 'gantt',
629
+ const props: GanttViewProps = {
630
+ tasks: [task],
420
631
  viewMode: 'week',
421
- tasks: [task]
422
632
  };
423
633
  ```
424
634
 
425
- <!-- release-metadata:v3.3.0 -->
635
+ **The authored (JSON metadata) types** live in `@object-ui/types` — this package
636
+ imports them and does not re-export them. There is no `GanttSchema`; the
637
+ component schema is `ObjectGanttSchema`, and it is **record-driven**: it names an
638
+ object and the fields to read, it does not carry a task array.
639
+
640
+ ```typescript
641
+ import type { ObjectGanttSchema } from '@object-ui/types';
426
642
 
427
- ## Compatibility
643
+ const gantt: ObjectGanttSchema = {
644
+ type: 'object-gantt',
645
+ objectName: 'project_tasks',
646
+ titleField: 'name',
647
+ startDateField: 'start_date',
648
+ endDateField: 'end_date',
649
+ progressField: 'completion_percentage',
650
+ dependencyField: 'dependent_task_ids',
651
+ };
652
+ ```
428
653
 
429
- - **React:** 18.x or 19.x
430
- - **Node.js:** ≥ 18
431
- - **TypeScript:** ≥ 5.0 (strict mode)
432
- - **`@objectstack/spec`:** ^3.3.0
433
- - **`@objectstack/client`:** ^3.3.0
434
- - **Tailwind CSS:** ≥ 3.4 (for packages with UI)
654
+ For a list view served under the `gantt` view type, the same configuration is a
655
+ `gantt` block on `ListViewSchema` (typed by `GanttConfig`, also from
656
+ `@object-ui/types`) rather than top-level keys — `ObjectGantt` reads either
657
+ spelling.
435
658
 
436
659
  ## Links
437
660