demo D277
Open a Microsoft Project plan in React
Read a Project .xml straight into a schedule, tasks, links, progress and people intact, drawn in the browser, and write it back out again, all with nothing sent to a server
importMSPDI · exportMSPDI
This reads a Microsoft Project XML file straight into a schedule, tasks, links, progress and people intact, draws it in the browser, and writes it back out, all with nothing sent to a server. It lets you open and save real Project plans in the page, so existing plans are not locked away in one tool.
This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/lattice-grid.min.css">
<div id="app"></div>
<script type="module">
import React from 'https://esm.sh/react@18';
import { createRoot } from 'https://esm.sh/react-dom@18/client';
import { createGantt, importMSPDI, exportMSPDI } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.71.0/modules/gantt.esm.min.js';
// mountSplit and the file round-trip are both outside what <LatticeGantt>
// covers, so this stays an imperative build against a ref'd container.
function App() {
const planRef = React.useRef(null);
const modelRef = React.useRef(null);
const ganttRef = React.useRef(null);
const [status, setStatus] = React.useState('');
React.useEffect(() => {
// importMSPDI reads a Project .xml into a { tasks, dependencies,
// resources, projectStart, calendar } model: the task tree, typed
// dependency links with their lag, per-task progress and the people on
// each task.
const xml = ''; // the text of a Project .xml document
const model = importMSPDI(xml);
modelRef.current = model;
setStatus('Read ' + model.tasks.length + ' tasks and ' + model.dependencies.length + ' links.');
const gantt = createGantt({
tasks: model.tasks, dependencies: model.dependencies, resources: model.resources,
projectStart: model.projectStart, calendar: 'weekends', autoSchedule: true,
});
ganttRef.current = gantt;
gantt.mountSplit(planRef.current, {
gridWidth: 300, rowHeight: 34, height: 420, zoom: 'day',
nonWorking: 'weekends', showArrows: true, showProgress: true,
});
return () => gantt.destroy();
}, []);
const save = () => {
// Write the plan back out as a Project file, so a round-trip keeps the
// tasks, typed links and assignments intact. gantt.toMSPDI() is the
// same over the plan.
const model = modelRef.current;
const out = exportMSPDI({
tasks: model.tasks, dependencies: model.dependencies,
schedule: ganttRef.current.schedule, resources: model.resources,
});
const url = URL.createObjectURL(new Blob([out], { type: 'application/xml' }));
const a = document.createElement('a');
a.href = url; a.download = 'plan.xml'; a.click();
URL.revokeObjectURL(url);
};
return (
<>
<div style={{ marginBottom: '10px' }}>
<button onClick={save}>Save as Project .xml</button> <span>{status}</span>
</div>
<div ref={planRef} style={{ height: '420px', overflow: 'auto' }} />
</>
);
}
createRoot(document.getElementById('app')).render(<App />);
</script>