Duration Editor
Double-click a value to edit. Slider release and unit/mode selections save automatically. For typed durations, press Enter or click outside to save a valid value. Escape discards the draft. Invalid drafts remain open; read-only cells cannot open or commit an editor.
Set durationEditor.autoApply: false to keep drafts until explicit Apply and show Apply/Cancel controls. Set durationEditor.showActions: true to show those controls while retaining automatic saves; showActions: false hides them. Clear remains available for optional values.
Source code
<template> <p> Double-click to edit. Valid changes save automatically; Escape discards the draft. </p> <RevoGrid :source="source" :columns="columns" :plugins="plugins" :theme="isDark ? 'darkMaterial' : 'material'" style="height: 340px" /></template><script setup lang="ts">import { ref } from 'vue';import { currentThemeVue } from '../composables/useRandomData';import RevoGrid from '@revolist/vue3-datagrid';import { editorColumns, editorRows } from './editor-data';const { isDark } = currentThemeVue();const source = ref(editorRows());const columns = editorColumns();const plugins = [];</script>import { currentTheme } from '../composables/useRandomData';import { defineCustomElements } from '@revolist/revogrid/loader';import { editorColumns, editorRows } from './editor-data';defineCustomElements();export function load(parentSelector: string, rows = editorRows()) { const grid = document.createElement('revo-grid'); grid.theme = currentTheme().isDark() ? 'darkMaterial' : 'material'; grid.style.height = '340px'; grid.columns = editorColumns(); document.querySelector(parentSelector)?.append(grid); grid.source = rows; return () => grid.remove();}import { currentTheme } from '../composables/useRandomData';import { useMemo } from 'react';import { RevoGrid } from '@revolist/react-datagrid';import { editorColumns, editorRows } from './editor-data';export default function EditorDuration({ rows }: { rows?: any[] }) { const columns = useMemo(editorColumns, []); const fallback = useMemo(editorRows, []); const plugins = useMemo(() => [], []); return ( <> <p> Double-click to edit. Valid changes save automatically; Escape discards the draft. </p> <RevoGrid source={rows?.length ? rows : fallback} columns={columns} plugins={plugins} theme={currentTheme().isDark() ? 'darkMaterial' : 'material'} style={{ height: '340px' }} /> </> );}import { currentTheme } from '../composables/useRandomData';import { Component, ViewEncapsulation } from '@angular/core';import { RevoGrid } from '@revolist/angular-datagrid';import { editorColumns, editorRows } from './editor-data';@Component({ selector: 'editor-duration-grid', standalone: true, imports: [RevoGrid], encapsulation: ViewEncapsulation.None, template: `<p> Double-click to edit. Valid changes save automatically; Escape discards the draft. </p> <revo-grid [source]="source" [columns]="columns" [plugins]="plugins" [theme]="theme" style="height:340px" ></revo-grid>`,})export class EditorDurationGridComponent { theme = currentTheme().isDark() ? 'darkMaterial' : 'material'; source = editorRows(); columns = editorColumns(); plugins = [];}import type { ColumnRegular } from '@revolist/revogrid';import { DurationEditor } from '@revolist/revogrid-pro';const display: ColumnRegular['cellTemplate'] = (_h, { value }) => value ? `${value.value} ${value.elapsed ? 'elapsed ' : ''}${value.unit}` : '';export const editorRows = () => [ { name: 'Planning', value: { value: 2, unit: 'day', elapsed: false }, inputOnly: { value: 4, unit: 'hour', elapsed: false }, elapsed: { value: 3, unit: 'hour', elapsed: true }, }, { name: 'Implementation', value: { value: 90, unit: 'minute', elapsed: false }, elapsed: null, inputOnly: null, },];export const editorColumns = (): ColumnRegular[] => [ { prop: 'name', name: 'Task', size: 180, readonly: true }, { prop: 'value', name: 'Working duration / effort', size: 270, editor: DurationEditor, durationEditor: { defaultUnit: 'hour', allowedUnits: ['minute', 'hour', 'day', 'week'], min: 0, slider: { max: 100, step: 0.25 }, }, cellTemplate: display, }, { prop: 'elapsed', name: 'Elapsed duration allowed', size: 280, editor: DurationEditor, durationEditor: { defaultUnit: 'hour', allowElapsed: true, min: 0 }, cellTemplate: display, }, { prop: 'inputOnly', name: 'Input only', size: 220, editor: DurationEditor, durationEditor: { defaultUnit: 'hour', slider: false }, cellTemplate: display, },];Use DurationEditor with an explicit durationEditor.defaultUnit. New general-purpose cells store { value, unit, elapsed }; clear stores null. Input accepts aliases such as 2d, 3h, 90min, zero and decimals. Bare numbers use the configured unit, and the authored unit is retained.
Configure required, numeric min/max, allowedUnits, maximumFractionDigits and allowElapsed. Negative durations are invalid; elapsed input is disabled by default. Bounds apply to the authored numeric value without implicit calendar conversions.
Gantt adapts the shared field for Duration, Remaining Duration and Work in its grid and task dialog. Gantt retains canonical hour storage and its own scheduling commands. Working-time conversion uses the project’s duration settings; elapsed duration counts nonworking time. Work never accepts elapsed input. Summary duration remains derived and read-only.
The popup includes the shared slider and compact unit buttons. Unit buttons keep the numeric amount and choose its authored unit; they do not perform calendar conversions. When allowElapsed is enabled, Working/Elapsed buttons select the duration mode.
Configure durationEditor.slider: { max: 100, step: 0.25 } for slider range and increments. The duration’s min and max validation bounds apply; the slider’s max only limits sliding when no validation maximum is configured. Text input can still enter a larger valid value. allowedUnits controls the available units. Five or fewer units use compact buttons; more than five use a dropdown. Labels can be localized with unitLabels, unitLabel, modeLabel, workingLabel and elapsedLabel.
Set slider: false for input only. To retain unit buttons without a slider, set slider: false, unitButtons: true; set unitButtons: false to show the slider and input without mode buttons. Press Enter or click outside to save valid changes. Set autoApply: false to retain explicit Apply/Cancel.
All control labels are localizable: label, placeholder, popupLabel, configurationErrorText, invalidText, unitLabels, unitLabel, modeLabel, workingLabel, elapsedLabel and partial actions. The duration expression itself uses the canonical unit aliases (for example 2d or 3 elapsed hours); translated controls do not change stored unit IDs or introduce locale-specific parsing.