Skip to content

Duration Editor

Double-click a value to edit. Apply or Ctrl/Cmd+Enter saves the draft. Cancel, Escape or outside dismissal discards it. Read-only cells cannot open or commit an editor.

Source code
Vuevue
<template>
  <p>Double-click a value to edit. Apply saves; Cancel 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>
TypeScriptts
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();
}
Reacttsx
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 a value to edit. Apply saves; Cancel discards the draft.
      </p>
      <RevoGrid
        source={rows?.length ? rows : fallback}
        columns={columns}
        plugins={plugins}
        theme={currentTheme().isDark() ? 'darkMaterial' : 'material'}
        style={{ height: '340px' }}
      />
    </>
  );
}
Angularts
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 a value to edit. Apply saves; Cancel 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 = [];
}
Editor configurationts
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. All changes remain drafts until Apply; Cancel discards them.

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.