v2.5.2
This commit is contained in:
@@ -2,54 +2,54 @@
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.narrow {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.narrow {
|
||||
background-color: #676a81;
|
||||
color: transparent;
|
||||
height: 10px !important;
|
||||
margin-top: 10px;
|
||||
border: 1px solid #63667a;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.narrow .wx-progress-percent {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.narrow .wx-progress-percent {
|
||||
background-color: #1a2630;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.narrow .wx-link {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.narrow .wx-link {
|
||||
background-color: #384047;
|
||||
border-radius: 0px;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.narrow .wx-link .wx-inner {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.narrow .wx-link .wx-inner {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.round {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.round {
|
||||
background-color: #ffeb3b;
|
||||
border: 1px solid #ffeb3b;
|
||||
color: #384047;
|
||||
border-radius: 50px;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.round .wx-progress-percent {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.round .wx-progress-percent {
|
||||
background-color: #ffc107;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.round .wx-progress-wrapper {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.round .wx-progress-wrapper {
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.progress {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.progress {
|
||||
background-color: transparent;
|
||||
color: var(--wx-color-font);
|
||||
border-radius: 50px;
|
||||
border: 1px solid #00bcd4;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.progress .wx-progress-percent {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.progress .wx-progress-percent {
|
||||
background-color: #00bcd4;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.progress .wx-progress-wrapper {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.progress .wx-progress-wrapper {
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.urgent {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.urgent {
|
||||
background-color: #f49a82;
|
||||
border: 1px solid #f45e36;
|
||||
}
|
||||
.demo.wx-I1glfWSB > .wx-gantt .wx-bar.wx-task.urgent .wx-progress-percent {
|
||||
.demo.wx-I1glfWSB .wx-gantt .wx-bar.wx-task.urgent .wx-progress-percent {
|
||||
background-color: #f45e36;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { getTypedData, taskTypes } from '../data';
|
||||
import { Gantt, Editor } from '../../src/';
|
||||
import { Gantt, Editor, ContextMenu } from '../../src/';
|
||||
import './GanttTaskTypes.css';
|
||||
|
||||
function GanttTaskTypes(props) {
|
||||
@@ -11,14 +11,16 @@ function GanttTaskTypes(props) {
|
||||
|
||||
return (
|
||||
<div className="wx-I1glfWSB demo">
|
||||
<Gantt
|
||||
init={setApi}
|
||||
{...skinSettings}
|
||||
tasks={data.tasks}
|
||||
links={data.links}
|
||||
scales={data.scales}
|
||||
taskTypes={taskTypes}
|
||||
/>
|
||||
<ContextMenu api={api}>
|
||||
<Gantt
|
||||
init={setApi}
|
||||
{...skinSettings}
|
||||
tasks={data.tasks}
|
||||
links={data.links}
|
||||
scales={data.scales}
|
||||
taskTypes={taskTypes}
|
||||
/>
|
||||
</ContextMenu>
|
||||
{api && <Editor api={api} />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.rselect {
|
||||
width: 150px;
|
||||
}
|
||||
.wx-gantt .myMarker {
|
||||
background-color: rgba(255, 84, 84, 0.77);
|
||||
}
|
||||
.gtcell {
|
||||
position: relative;
|
||||
height: calc(100% - 48px);
|
||||
border-top: var(--wx-gantt-border);
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { getData } from '../data';
|
||||
import { Gantt, version } from '../../src/';
|
||||
import { Toolbar, registerToolbarItem } from '@svar-ui/react-toolbar';
|
||||
import { Switch, RichSelect, Segmented } from '@svar-ui/react-core';
|
||||
import { Calendar } from '@svar-ui/gantt-store';
|
||||
import './ProExport.css';
|
||||
|
||||
registerToolbarItem('switch', Switch);
|
||||
registerToolbarItem('segmented', Segmented);
|
||||
registerToolbarItem('richselect', RichSelect);
|
||||
|
||||
export default function ProExport({ skinSettings }) {
|
||||
|
||||
const [size, setSize] = useState('auto');
|
||||
const [fit, setFit] = useState(true);
|
||||
const [api, setApi] = useState();
|
||||
const [config, setConfig] = useState('basic');
|
||||
|
||||
const data = useMemo(() => {
|
||||
const advanced = config === 'advanced';
|
||||
return getData(null, {
|
||||
splitTasks: advanced,
|
||||
baselines: advanced,
|
||||
unscheduledTasks: advanced,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
const items = useMemo(() => {
|
||||
return [
|
||||
{ text: 'Page size' },
|
||||
{
|
||||
id: 'size',
|
||||
comp: 'richselect',
|
||||
css: 'rselect',
|
||||
value: size,
|
||||
options: [
|
||||
{ id: 'auto', label: 'Auto' },
|
||||
{ id: 'a4-landscape', label: 'A4 Landscape' },
|
||||
{ id: 'a4', label: 'A4 Portrait' },
|
||||
{ id: 'a3-landscape', label: 'A3 Landscape' },
|
||||
{ id: 'a3', label: 'A3 Portrait' },
|
||||
],
|
||||
handler: () => {},
|
||||
},
|
||||
{ text: 'Fit to page' },
|
||||
{
|
||||
id: 'fit',
|
||||
comp: 'switch',
|
||||
value: fit,
|
||||
handler: () => {},
|
||||
},
|
||||
{
|
||||
id: 'export-pdf',
|
||||
comp: 'button',
|
||||
text: 'To PDF',
|
||||
handler: () => exportOthers('pdf'),
|
||||
},
|
||||
{
|
||||
id: 'export-png',
|
||||
comp: 'button',
|
||||
text: 'To PNG',
|
||||
handler: () => exportOthers('png'),
|
||||
},
|
||||
{ comp: 'separator' },
|
||||
{
|
||||
id: 'export-xlsx',
|
||||
comp: 'button',
|
||||
text: 'To XLSX',
|
||||
handler: () => exportExcel(false),
|
||||
},
|
||||
{
|
||||
id: 'export-xlsx-chart',
|
||||
comp: 'button',
|
||||
text: 'To XLSX with Chart',
|
||||
handler: () => exportExcel(true),
|
||||
},
|
||||
{
|
||||
id: 'export-mspx',
|
||||
comp: 'button',
|
||||
text: 'To MS Project (XML)',
|
||||
handler: () => exportOthers('mspx'),
|
||||
},
|
||||
{ comp: 'spacer' },
|
||||
{
|
||||
id: 'config',
|
||||
comp: 'segmented',
|
||||
value: config,
|
||||
options: [
|
||||
{ id: 'basic', label: 'Basic' },
|
||||
{ id: 'advanced', label: 'Advanced' },
|
||||
],
|
||||
handler: () => {},
|
||||
},
|
||||
];
|
||||
}, [size, fit, config, api]);
|
||||
|
||||
const markers = useMemo(
|
||||
() => [
|
||||
{
|
||||
start: new Date(2026, 3, 8),
|
||||
text: 'Approval of strategy',
|
||||
css: 'myMarker',
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const calendar = useMemo(() => new Calendar(), []);
|
||||
|
||||
function handleClick({ item }) {
|
||||
const parts = item.id.split('-');
|
||||
if (parts[0] === 'export') {
|
||||
if (parts[1] === 'xlsx') {
|
||||
exportExcel(parts[2] === 'chart');
|
||||
} else {
|
||||
exportOthers(parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const url = 'https://export.svar.dev/gantt/' + version;
|
||||
|
||||
function exportExcel(visual) {
|
||||
api.exec('export-data', {
|
||||
url,
|
||||
format: 'xlsx',
|
||||
excel: {
|
||||
columns: visual
|
||||
? [
|
||||
{
|
||||
id: 'text',
|
||||
header: 'Task name',
|
||||
width: 200,
|
||||
},
|
||||
]
|
||||
: null,
|
||||
sheetNames: ['Tasks', 'Links'],
|
||||
dateFormat: 'yyyy-mmm-dd',
|
||||
visual,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function exportOthers(format) {
|
||||
const parts = size.split('-');
|
||||
const props = {
|
||||
size: parts[0],
|
||||
landscape: parts[1] === 'landscape',
|
||||
fitSize: fit && size != 'auto',
|
||||
styles: '.wx-gantt .myMarker{ background-color: rgba(255, 84, 84, 0.77);',
|
||||
};
|
||||
api.exec('export-data', {
|
||||
url,
|
||||
format,
|
||||
pdf: props,
|
||||
png: props,
|
||||
ganttConfig: {
|
||||
cellWidth: 30,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleChange({ item, value }) {
|
||||
if (item.id === 'size') setSize(value);
|
||||
else if (item.id === 'fit') setFit(value);
|
||||
else if (item.id === 'config') setConfig(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toolbar items={items} onClick={handleClick} onChange={handleChange} />
|
||||
<div className="gtcell">
|
||||
{config === 'basic' ? (
|
||||
<Gantt
|
||||
init={setApi}
|
||||
{...skinSettings}
|
||||
tasks={data.tasks}
|
||||
links={data.links}
|
||||
scales={data.scales}
|
||||
/>
|
||||
) : (
|
||||
<Gantt
|
||||
init={setApi}
|
||||
baselines={true}
|
||||
splitTasks={true}
|
||||
unscheduledTasks={true}
|
||||
markers={markers}
|
||||
calendar={calendar}
|
||||
{...skinSettings}
|
||||
tasks={data.tasks}
|
||||
links={data.links}
|
||||
scales={data.scales}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.gtcell {
|
||||
position: relative;
|
||||
height: calc(100% - 48px);
|
||||
border-top: var(--wx-gantt-border);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { getData } from "../data";
|
||||
import { Gantt } from "../../src";
|
||||
import { Toolbar, registerToolbarItem } from "@wx/react-toolbar";
|
||||
import UploadButton from "../custom/UploadButton";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
import './ProMSProject.css';
|
||||
|
||||
registerToolbarItem("upload", UploadButton);
|
||||
export default function ProMSProject({ skinSettings }) {
|
||||
const data = getData();
|
||||
const [api, setApi] = useState();
|
||||
|
||||
const handleClick = useCallback(({ item }) => {
|
||||
if (item.id === "export") {
|
||||
api.exec("export-data", { format: "mspx" });
|
||||
}
|
||||
}, [api]);
|
||||
|
||||
const importMSProject = useCallback(() => {
|
||||
const file = document.getElementById("import-file").files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
const xml = e.target.result;
|
||||
api.exec("import-data", {
|
||||
data: xml,
|
||||
});
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}, [api]);
|
||||
|
||||
const items = [
|
||||
{
|
||||
id: "export",
|
||||
comp: "button",
|
||||
text: "Download MS Project XML",
|
||||
},
|
||||
{
|
||||
id: "import",
|
||||
comp: "upload",
|
||||
text: "Upload MS Project XML",
|
||||
onChange: importMSProject,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toolbar items={items} onClick={handleClick} />
|
||||
<div className="gtcell">
|
||||
<Gantt
|
||||
init={setApi}
|
||||
{...skinSettings}
|
||||
tasks={data.tasks}
|
||||
links={data.links}
|
||||
scales={data.scales}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,109 +1,28 @@
|
||||
import { useMemo, useCallback, useRef } from 'react';
|
||||
import { useStoreLater } from '@svar-ui/lib-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { getData } from '../data';
|
||||
import {
|
||||
Gantt,
|
||||
ContextMenu,
|
||||
Editor,
|
||||
getEditorItems,
|
||||
defaultTaskTypes,
|
||||
} from '../../src';
|
||||
} from '../../src/';
|
||||
|
||||
export default function SummariesConvert({ skinSettings }) {
|
||||
const data = useMemo(() => getData(), []);
|
||||
const gApi = useRef(null);
|
||||
|
||||
const toSummary = useCallback(
|
||||
(id, self) => {
|
||||
const api = gApi.current;
|
||||
if (!api) return;
|
||||
const task = api.getTask(id);
|
||||
if (!self) id = task.parent;
|
||||
|
||||
if (id && task.type !== 'summary') {
|
||||
api.exec('update-task', {
|
||||
id,
|
||||
task: { type: 'summary' },
|
||||
});
|
||||
}
|
||||
},
|
||||
[gApi],
|
||||
);
|
||||
|
||||
const toTask = useCallback(
|
||||
(id) => {
|
||||
const api = gApi.current;
|
||||
if (!api) return;
|
||||
const obj = api.getTask(id);
|
||||
if (obj && !obj.data?.length) {
|
||||
api.exec('update-task', {
|
||||
id,
|
||||
task: { type: 'task' },
|
||||
});
|
||||
}
|
||||
},
|
||||
[gApi],
|
||||
);
|
||||
|
||||
const init = useCallback(
|
||||
(api) => {
|
||||
gApi.current = api;
|
||||
|
||||
api.getState().tasks.forEach((task) => {
|
||||
if (task.data?.length) {
|
||||
toSummary(task.id, true);
|
||||
}
|
||||
});
|
||||
|
||||
api.on('add-task', ({ id, mode }) => {
|
||||
if (mode === 'child') toSummary(id);
|
||||
});
|
||||
|
||||
api.on('move-task', ({ id, source, mode, inProgress }) => {
|
||||
if (inProgress) return;
|
||||
if (mode == 'child') toSummary(id);
|
||||
else toTask(source);
|
||||
});
|
||||
|
||||
api.on('delete-task', ({ source }) => {
|
||||
toTask(source);
|
||||
});
|
||||
},
|
||||
[toSummary, toTask],
|
||||
);
|
||||
|
||||
const activeTaskId = useStoreLater(gApi.current, 'activeTask');
|
||||
|
||||
const items = useMemo(() => {
|
||||
const api = gApi.current;
|
||||
const task = activeTaskId ? api?.getTask(activeTaskId) : null;
|
||||
if (task) {
|
||||
return getEditorItems().map((item) => {
|
||||
item = { ...item };
|
||||
if (item.comp === 'select' && item.key === 'type') {
|
||||
item.options =
|
||||
task.type !== 'summary'
|
||||
? defaultTaskTypes.filter((t) => t.id !== 'summary')
|
||||
: [];
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}, [activeTaskId, gApi.current]);
|
||||
const [api, setApi] = useState();
|
||||
|
||||
return (
|
||||
<div className="wx-TEIogFEZ gt-cell">
|
||||
<ContextMenu api={gApi.current}>
|
||||
<ContextMenu api={api}>
|
||||
<Gantt
|
||||
init={init}
|
||||
init={setApi}
|
||||
{...skinSettings}
|
||||
tasks={data.tasks}
|
||||
links={data.links}
|
||||
scales={data.scales}
|
||||
summary={{ autoConvert: true }}
|
||||
/>
|
||||
</ContextMenu>
|
||||
{gApi.current && <Editor api={gApi.current} items={items} />}
|
||||
{api && <Editor api={api} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,131 +1,29 @@
|
||||
import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
|
||||
import { useState, useMemo } from 'react';
|
||||
import { getData } from '../data';
|
||||
import { Gantt, Editor, getEditorItems, ContextMenu } from '../../src';
|
||||
import './ProSummariesProgress.css';
|
||||
import { Gantt, Editor, ContextMenu } from '../../src';
|
||||
|
||||
function SummariesProgress({ skinSettings }) {
|
||||
const data = useMemo(() => getData(), []);
|
||||
const tasks = data.tasks;
|
||||
const { tasks, links, scales } = data;
|
||||
|
||||
const [api, setApi] = useState();
|
||||
|
||||
const dayDiff = (next, prev) => {
|
||||
const d = (next - prev) / 1000 / 60 / 60 / 24;
|
||||
return Math.ceil(Math.abs(d));
|
||||
};
|
||||
|
||||
/**
|
||||
* The formula of calculation is ∑d*p / ∑d , where "d" is task duration in days,
|
||||
* "p" is the task progress and "∑" stands for a sum of all loaded tasks
|
||||
*/
|
||||
function getSummaryProgress(id) {
|
||||
const [totalProgress, totalDuration] = collectProgressFromKids(id);
|
||||
const res = totalProgress / totalDuration;
|
||||
return isNaN(res) ? 0 : Math.round(res);
|
||||
}
|
||||
|
||||
function collectProgressFromKids(id) {
|
||||
let totalProgress = 0,
|
||||
totalDuration = 0;
|
||||
const kids = api.getTask(id).data;
|
||||
|
||||
kids?.forEach((kid) => {
|
||||
let duration = 0;
|
||||
if (kid.type !== 'milestone' && kid.type !== 'summary') {
|
||||
duration = kid.duration || dayDiff(kid.end, kid.start);
|
||||
totalDuration += duration;
|
||||
totalProgress += duration * kid.progress;
|
||||
}
|
||||
|
||||
const [p, d] = collectProgressFromKids(kid.id);
|
||||
totalProgress += p;
|
||||
totalDuration += d;
|
||||
});
|
||||
return [totalProgress, totalDuration];
|
||||
}
|
||||
|
||||
function recalcSummaryProgress(id, self) {
|
||||
const { tasks } = api.getState();
|
||||
const task = api.getTask(id);
|
||||
|
||||
if (task.type != 'milestone') {
|
||||
const summary =
|
||||
self && task.type === 'summary' ? id : tasks.getSummaryId(id);
|
||||
|
||||
if (summary) {
|
||||
const progress = getSummaryProgress(summary);
|
||||
api.exec('update-task', {
|
||||
id: summary,
|
||||
task: { progress },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
const recalcRef = useRef(null);
|
||||
recalcRef.current = recalcSummaryProgress;
|
||||
|
||||
const init = useCallback((api) => {
|
||||
setApi(api);
|
||||
// auto progress calculations
|
||||
api.on('add-task', ({ id }) => {
|
||||
recalcRef.current(id);
|
||||
});
|
||||
api.on('update-task', ({ id }) => {
|
||||
recalcRef.current(id);
|
||||
});
|
||||
|
||||
api.on('delete-task', ({ source }) => {
|
||||
recalcRef.current(source, true);
|
||||
});
|
||||
api.on('copy-task', ({ id }) => {
|
||||
recalcRef.current(id);
|
||||
});
|
||||
api.on('move-task', ({ id, source, inProgress }) => {
|
||||
if (inProgress) return;
|
||||
|
||||
if (api.getTask(id).parent != source) recalcRef.current(source, true);
|
||||
recalcRef.current(id);
|
||||
});
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="wx-OeNgRLo4 wrapper">
|
||||
<>
|
||||
<ContextMenu api={api}>
|
||||
<div className="wx-OeNgRLo4 gt-cell">
|
||||
<Gantt
|
||||
{...skinSettings}
|
||||
init={init}
|
||||
init={setApi}
|
||||
tasks={tasks}
|
||||
links={data.links}
|
||||
scales={data.scales}
|
||||
links={links}
|
||||
scales={scales}
|
||||
cellWidth={30}
|
||||
summary={{ autoProgress: true }}
|
||||
/>
|
||||
</div>
|
||||
</ContextMenu>
|
||||
{api && <ConfiguredEditor api={api} />}
|
||||
</div>
|
||||
{api && <Editor api={api} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ConfiguredEditor({ api }) {
|
||||
const [editorItems, setEditorItems] = useState(getEditorItems());
|
||||
|
||||
// disabling progress slider for summary tasks
|
||||
api.on('show-editor', ({ id }) => {
|
||||
if (id) {
|
||||
const type = api.getTask(id).type;
|
||||
setEditorItems(
|
||||
getEditorItems().map((ed) => ({
|
||||
...ed,
|
||||
...(ed.key == 'progress' && {
|
||||
config: { disabled: type === 'summary' },
|
||||
}),
|
||||
})),
|
||||
);
|
||||
}
|
||||
});
|
||||
return <Editor api={api} items={editorItems} />;
|
||||
}
|
||||
|
||||
export default SummariesProgress;
|
||||
|
||||
+8
-11
@@ -17,28 +17,25 @@ function DemoExplorerContent({
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const [skin, setSkin] = useState(skins[0].id);
|
||||
const [title, setTitle] = useState('');
|
||||
const [githubLink, setGithubLink] = useState('');
|
||||
const [show, setShow] = useState(true);
|
||||
|
||||
const currentPath = window.location.hash.slice(1);
|
||||
const parts = currentPath.split('/');
|
||||
const currentPage = `/${parts[1]}/:skin`;
|
||||
const matched = links.find((a) => a[0] === currentPage);
|
||||
const title = matched ? matched[1] : '';
|
||||
const baseLink =
|
||||
'https://github.com/svar-widgets/react-' +
|
||||
productTag +
|
||||
'/tree/main/demos/cases/';
|
||||
|
||||
const name = matched ? (matched[3] || matched[1]) : '';
|
||||
const githubLink = `${baseLink}${name}.jsx`;
|
||||
|
||||
const handleRouteChange = useCallback((path) => {
|
||||
const parts = path.split('/');
|
||||
const page = parts[1];
|
||||
const newSkin = parts[2];
|
||||
setSkin(newSkin);
|
||||
|
||||
const targetPage = `/${page}/:skin`;
|
||||
const matched = links.find((a) => a[0] === targetPage);
|
||||
if (matched) {
|
||||
setTitle(matched[1]);
|
||||
const name = matched[3] || matched[1];
|
||||
setGithubLink(`${baseLink}${name}.jsx`);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSkinChange = ({ value }) => {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
.button {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: 600;
|
||||
padding: 5px 16px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
background-color: #f2f2f7;
|
||||
color: #2c2f3c;
|
||||
}
|
||||
:global(.wx-willow-dark-theme .button) {
|
||||
background-color: #384047;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import React, { useRef } from 'react';
|
||||
import './UbloadButton.css';
|
||||
|
||||
function UploadButton({ text, onChange }) {
|
||||
|
||||
const fileInput = useRef(null);
|
||||
|
||||
function handleClick() {
|
||||
fileInput.current?.click();
|
||||
}
|
||||
|
||||
function handleChange() {
|
||||
if (onChange) {
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<button className="button" onClick={handleClick}>
|
||||
{text}
|
||||
</button>
|
||||
<input
|
||||
ref={fileInput}
|
||||
id="import-file"
|
||||
type="file"
|
||||
accept=".xml"
|
||||
onChange={handleChange}
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default UploadButton;
|
||||
+32
-32
@@ -53,7 +53,7 @@ const tasks = [
|
||||
base_start: new Date(2026, 3, 2),
|
||||
base_end: new Date(2026, 3, 16),
|
||||
text: 'Project planning',
|
||||
progress: 30,
|
||||
progress: 40,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -76,7 +76,7 @@ const tasks = [
|
||||
start: new Date(2026, 3, 5),
|
||||
duration: 2,
|
||||
text: 'Discussions',
|
||||
progress: 100,
|
||||
progress: 72,
|
||||
parent: 1,
|
||||
type: 'task',
|
||||
details: 'Team discussions on project strategies.',
|
||||
@@ -139,7 +139,7 @@ const tasks = [
|
||||
base_start: new Date(2026, 3, 2),
|
||||
base_end: new Date(2026, 3, 12),
|
||||
text: 'Project management',
|
||||
progress: 10,
|
||||
progress: 8,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -191,7 +191,7 @@ const tasks = [
|
||||
base_start: new Date(2026, 3, 9),
|
||||
base_end: new Date(2026, 3, 30),
|
||||
text: 'Development',
|
||||
progress: 30,
|
||||
progress: 2,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -202,7 +202,7 @@ const tasks = [
|
||||
start: new Date(2026, 3, 9),
|
||||
duration: 6,
|
||||
text: 'Prototyping',
|
||||
progress: 3,
|
||||
progress: 7,
|
||||
parent: 3,
|
||||
type: 'task',
|
||||
assigned: 3,
|
||||
@@ -233,7 +233,7 @@ const tasks = [
|
||||
base_start: new Date(2026, 3, 9),
|
||||
base_end: new Date(2026, 4, 3),
|
||||
text: 'Testing',
|
||||
progress: 3,
|
||||
progress: 4,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -244,7 +244,7 @@ const tasks = [
|
||||
start: new Date(2026, 3, 9),
|
||||
duration: 4,
|
||||
text: 'Testing prototype',
|
||||
progress: 3,
|
||||
progress: 24,
|
||||
parent: 4,
|
||||
type: 'task',
|
||||
assigned: 4,
|
||||
@@ -300,7 +300,7 @@ const calendarTasks = [
|
||||
//start: new Date(2026, 3, 2),
|
||||
//end: new Date(2026, 3, 18),
|
||||
text: 'Project planning',
|
||||
progress: 30,
|
||||
progress: 40,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -323,7 +323,7 @@ const calendarTasks = [
|
||||
start: new Date(2026, 3, 7),
|
||||
duration: 2,
|
||||
text: 'Discussions',
|
||||
progress: 100,
|
||||
progress: 72,
|
||||
parent: 1,
|
||||
type: 'task',
|
||||
details: 'Team discussions on project strategies.',
|
||||
@@ -386,7 +386,7 @@ const calendarTasks = [
|
||||
//start: new Date(2026, 3, 2),
|
||||
//end: new Date(2026, 3, 14),
|
||||
text: 'Project management',
|
||||
progress: 10,
|
||||
progress: 8,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -437,7 +437,7 @@ const calendarTasks = [
|
||||
//start: new Date(2026, 3, 9),
|
||||
//end: new Date(2026, 4, 8),
|
||||
text: 'Development',
|
||||
progress: 30,
|
||||
progress: 2,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -448,7 +448,7 @@ const calendarTasks = [
|
||||
start: new Date(2026, 3, 13),
|
||||
duration: 4,
|
||||
text: 'Prototyping',
|
||||
progress: 3,
|
||||
progress: 7,
|
||||
parent: 3,
|
||||
type: 'task',
|
||||
assigned: 3,
|
||||
@@ -479,7 +479,7 @@ const calendarTasks = [
|
||||
//start: new Date(2026, 3, 9),
|
||||
//end: new Date(2026, 4, 13),
|
||||
text: 'Testing',
|
||||
progress: 3,
|
||||
progress: 4,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -490,7 +490,7 @@ const calendarTasks = [
|
||||
start: new Date(2026, 3, 17),
|
||||
duration: 4,
|
||||
text: 'Testing prototype',
|
||||
progress: 3,
|
||||
progress: 24,
|
||||
parent: 4,
|
||||
type: 'task',
|
||||
assigned: 4,
|
||||
@@ -608,11 +608,6 @@ const links = [
|
||||
},
|
||||
];
|
||||
|
||||
const scales = [
|
||||
{ unit: 'month', step: 1, format: '%F %Y' },
|
||||
{ unit: 'day', step: 1, format: '%j', css: dayStyle },
|
||||
];
|
||||
|
||||
const tasksHour = [
|
||||
{
|
||||
id: 1,
|
||||
@@ -773,7 +768,7 @@ const critTasks = [
|
||||
start: new Date(2026, 3, 5),
|
||||
end: new Date(2026, 3, 7),
|
||||
text: 'Discussions',
|
||||
progress: 100,
|
||||
progress: 72,
|
||||
type: 'task',
|
||||
open: true,
|
||||
details: 'Team discussions on project strategies.',
|
||||
@@ -828,7 +823,7 @@ const critTasks = [
|
||||
start: new Date(2026, 3, 2),
|
||||
end: new Date(2026, 3, 12),
|
||||
text: 'Project management 1',
|
||||
progress: 10,
|
||||
progress: 8,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -882,7 +877,7 @@ const critTasks = [
|
||||
start: new Date(2026, 3, 2),
|
||||
end: new Date(2026, 3, 12),
|
||||
text: 'Project management 2',
|
||||
progress: 10,
|
||||
progress: 8,
|
||||
parent: 0,
|
||||
type: 'summary',
|
||||
open: true,
|
||||
@@ -1014,6 +1009,11 @@ function addDays(d, n) {
|
||||
return new Date(new Date(d).setDate(d.getDate() + n));
|
||||
}
|
||||
|
||||
const scales = [
|
||||
{ unit: 'month', step: 1, format: '%F %Y' },
|
||||
{ unit: 'day', step: 1, format: '%j', css: dayStyle },
|
||||
];
|
||||
|
||||
const datasets = {
|
||||
day: { tasks, links, scales },
|
||||
hour: { tasks: tasksHour, links: linksHour, scales: scalesHour },
|
||||
@@ -1021,16 +1021,6 @@ const datasets = {
|
||||
calendar: { tasks: calendarTasks, links, scales },
|
||||
};
|
||||
|
||||
export const taskTypes = [
|
||||
{ id: 'task', label: 'Task' },
|
||||
{ id: 'summary', label: 'Summary task' },
|
||||
{ id: 'milestone', label: 'Milestone' },
|
||||
{ id: 'urgent', label: 'Urgent' },
|
||||
{ id: 'narrow', label: 'Narrow' },
|
||||
{ id: 'progress', label: 'Progress' },
|
||||
{ id: 'round', label: 'Rounded' },
|
||||
];
|
||||
|
||||
export function getData(name, config) {
|
||||
const data = datasets[name || 'day'];
|
||||
|
||||
@@ -1070,6 +1060,16 @@ export function getData(name, config) {
|
||||
return data;
|
||||
}
|
||||
|
||||
export const taskTypes = [
|
||||
{ id: 'task', label: 'Task' },
|
||||
{ id: 'summary', label: 'Summary task' },
|
||||
{ id: 'milestone', label: 'Milestone' },
|
||||
{ id: 'urgent', label: 'Urgent' },
|
||||
{ id: 'narrow', label: 'Narrow' },
|
||||
{ id: 'progress', label: 'Progress' },
|
||||
{ id: 'round', label: 'Rounded' },
|
||||
];
|
||||
|
||||
export function getTypedData() {
|
||||
const t = tasks.map((task, i) => {
|
||||
const res = { ...task };
|
||||
|
||||
+1
-1
@@ -186,7 +186,7 @@ export const links = [
|
||||
],
|
||||
[
|
||||
'/backend-provider-batch/:skin',
|
||||
'Saving to backend (batch)',
|
||||
'Saving to backend: batch request',
|
||||
GanttBatchProvider,
|
||||
'GanttBatchProvider',
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user