Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dependency-lines show in the wrong track #365

Open
LucasRotondaro opened this issue Feb 1, 2023 · 0 comments
Open

dependency-lines show in the wrong track #365

LucasRotondaro opened this issue Feb 1, 2023 · 0 comments

Comments

@LucasRotondaro
Copy link

Describe the bug
In some cases the line is not shown in the right item, i have to do a vertical scroll to see the line in the right item.
In this example the data is randomly generated, so sometimes the lines work as expected and other times they don't, I've attached the full code so you can check it yourself.
I am using and modified code version of this example:
https://gantt-schedule-timeline-calendar.neuronet.io/gstc/examples/one-month/index.html
If you need extra feedback from me let me know, thanks.

Code
How to reproduce this error?

index.js

/*import GSTC from '../../dist/gstc.esm.min.js';
// or when you encounter problems with wasm loader
//import GSTC from '../../dist/gstc.wasm.esm.min.js';
import { Plugin as TimelinePointer } from '../../dist/plugins/timeline-pointer.esm.min.js';
import { Plugin as Selection } from '../../dist/plugins/selection.esm.min.js';
import { Plugin as ItemMovement } from '../../dist/plugins/item-movement.esm.min.js';
import { Plugin as ItemResizing } from '../../dist/plugins/item-resizing.esm.min.js';
import { Plugin as CalendarScroll } from '../../dist/plugins/calendar-scroll.esm.min.js';
import { Plugin as HighlightWeekends } from '../../dist/plugins/highlight-weekends.esm.min.js';
import { Plugin as ProgressBar } from '../../dist/plugins/progress-bar.esm.min.js';
import { Plugin as TimeBookmarks } from '../../dist/plugins/time-bookmarks.esm.min.js';*/
//import { Plugin as DependencyLines } from '../../dist/plugins/dependency-lines.esm.min.js';
import { Plugin as TimelinePointer } from "../plugins/gstc/timeline-pointer.esm.min.js";
import { Plugin as CalendarScroll } from "../plugins/gstc/calendar-scroll.esm.min.js";
import { Plugin as TimeBookmarks } from "../plugins/gstc/time-bookmarks.esm.min.js";
import { Plugin as DependencyLines } from "../plugins/gstc/dependency-lines.esm.min.js";
import GSTC from "/js/libraries/gstc.esm.min.js";
const iterations = 31;
const GSTCID = GSTC.api.GSTCID;

function getRandomFaceImage() {
    return `https://st.depositphotos.com/1224365/2498/i/450/depositphotos_24980235-stock-photo-portrait-of-a-normal-man.jpg`;
}

const colors = ['#E74C3C', '#DA3C78', '#7E349D', '#0077C0', '#07ABA0', '#0EAC51', '#F1892D'];
function getRandomColor() {
    return '#E74C3C' //colors[Math.floor(Math.random() * colors.length)];
}
globalThis.GSTC = GSTC;

const startDate = GSTC.api.date('2020-01-01').startOf('month');
const endDate = startDate.clone().endOf('month');
const startTime = startDate.valueOf();

/**
 * @type {import('../../dist/gstc').Rows}
 */

const rows = {};
for (let i = 0; i < iterations; i++) {
    const withParent = i > 0 && i % 2 === 0;
    const id = GSTCID(String(i));
    rows[id] = {
        id,
        label: `John Doe ${i}`,
        parentId: /*withParent ? GSTCID(String(i - 1)) :*/ undefined,
        expanded: false,
        img: getRandomFaceImage(),
        progress: Math.floor(Math.random() * 100),
    };
}

rows[GSTCID('11')].label = 'NESTED TREE HERE';
rows[GSTCID('12')].parentId = GSTCID('11');
rows[GSTCID('13')].parentId = GSTCID('12');
rows[GSTCID('14')].parentId = GSTCID('13');

/**
 * @type {import('../../dist/gstc').Items}
 */
const items = {};
for (let i = 0; i < 450; i++) {
    let rowId = GSTCID(Math.floor(Math.random() * (30 - 1 + 1) + 1).toString());
    let id = GSTCID(i.toString());
    let startDayjs = GSTC.api
        .date(startTime)
        .startOf('month')
        .add(Math.floor(Math.random() * 20), 'day');
    items[id] = {
        id,
        label: `John Doe ${i}`,
        progress: Math.round(Math.random() * 100),
        style: { background: getRandomColor() },
        time: {
            start: startDayjs.startOf('day').valueOf(),
            end: startDayjs
                .clone()
                .add(1, 'day')
                .endOf('day')
                .valueOf(),
        },
        rowId,
        img: getRandomFaceImage(),
        description: 'Lorem ipsum dolor sit amet',
    };
}

items[GSTCID('0')].linkedWith = [GSTCID('1')];
items[GSTCID('0')].label = 'Task 0 linked with 1';
items[GSTCID('0')].type = 'task';
items[GSTCID('1')].label = 'Task 1 linked with 0';
items[GSTCID('1')].type = 'task';
items[GSTCID('1')].time = { ...items[GSTCID('0')].time };

items[GSTCID('0')].style = { background: colors[3] };
items[GSTCID('1')].style = { background: colors[3] };

items[GSTCID('3')].dependant = [GSTCID('5')];
items[GSTCID('3')].time.start = GSTC.api.date(startTime).add(4, 'day').startOf('day').add(5, 'day').valueOf();
items[GSTCID('3')].time.end = GSTC.api.date(items[GSTCID('3')].time.start).endOf('day').add(5, 'day').valueOf();

items[GSTCID('5')].time.start = GSTC.api.date(items[GSTCID('3')].time.end).startOf('day').add(5, 'day').valueOf();
items[GSTCID('5')].time.end = GSTC.api.date(items[GSTCID('5')].time.start).endOf('day').add(2, 'day').valueOf();
items[GSTCID('5')].dependant = [GSTCID('7'), GSTCID('9')];

items[GSTCID('7')].time.start = GSTC.api.date(items[GSTCID('5')].time.end).startOf('day').add(3, 'day').valueOf();
items[GSTCID('7')].time.end = GSTC.api.date(items[GSTCID('7')].time.start).endOf('day').add(2, 'day').valueOf();
items[GSTCID('9')].time.start = GSTC.api.date(items[GSTCID('5')].time.end).startOf('day').add(2, 'day').valueOf();
items[GSTCID('9')].time.end = GSTC.api.date(items[GSTCID('9')].time.start).endOf('day').add(3, 'day').valueOf();

const columns = {
    data: {
        [GSTCID('label')]: {
            id: GSTCID('label'),
            data: 'label',
            sortable: 'label',
            expander: true,
            isHTML: false,
            width: 315,
            header: {
                content: 'Label',
            },
        },
    },
};

const bookmarks = {
    '1-st': {
        time: GSTC.api.date(startTime).add(2, 'day').startOf('day').valueOf(),
        color: '#3498DB',
        label: '1-st',
    },
};

for (let i = 1; i < 10; i++) {
    const id = `bookmark-${i}`;
    const time = GSTC.api
        .date(startTime)
        .add(i * 4, 'day')
        .startOf('day');
    bookmarks[id] = {
        time: time.valueOf(),
        label: `Bookmark ${time.format('YYYY-MM-DD')}`,
    };
}

function itemSlot(vido, props) {
    const { html, onChange, update } = vido;

    let imageSrc = '';
    let description = '';
    onChange((newProps) => {
        props = newProps;
        if (!props || !props.item) return;
        imageSrc = props.item.img;
        description = props.item.description;
        update();
    });

    return (content) => {
        if (!props || !props.item) return content;
        return html`<div
        class="item-image"
        style="background:url(${imageSrc}),transparent;flex-shrink:0;border-radius:100%;width:34px;height:34px;vertical-align: middle;background-size: 100%;margin: 4px 11px 0px 0px;"
      ></div>
      <div class="item-text">
        <div class="item-label">${content}</div>
        <div class="item-description" style="font-size:11px;margin-top:2px;color:#fffffff0;line-height:1em;">
          ${description}
        </div>
      </div>`;
    };
}

function rowSlot(vido, props) {
    const { html, onChange, update, api } = vido;

    let img = '';
    onChange((newProps) => {
        props = newProps;
        if (!props || !props.row) return;
        img = props.row.img;
        update();
    });

    return (content) => {
        if (!props || !props.column) return;
        return api.sourceID(props.column.id) === 'label'
            ? html`<div class="row-content-wrapper" style="display:flex">
          <div class="row-content" style="flex-grow:1;">${content}</div>
          <div
            class="row-image"
            style="background:url(${img}),transparent;border-radius:100%;width:34px;height:34px;vertical-align: middle;background-size: 100%;margin: auto 10px;flex-shrink:0;"
          ></div>
        </div>`
            : content;
    };
}

function mainOuterSlot(vido, props) {
    const { onChange, api, update, html, state, getElement } = vido;

    onChange((changedProps) => {
        // if current element is reused to display other item data just update your data so when you click you will display right alert
        props = changedProps;
    });

    let year = api.time.date(startTime).year();
    let month = api.time.date(startTime).month();
    const months = [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December',
    ];

    let loading = '';
    let overlay = '';

    function updateTime() {
        if (loading) return;
        const startTime = api.time
            .date(`${year}-${month + 1}-01`)
            .startOf('month')
            .valueOf();
        const endTime = api.time
            .date(`${year}-${month + 1}-01`)
            .endOf('month')
            .valueOf();
        loading = 'LOADING... You can load items from backend now.';
        overlay = 'overlay';
        setTimeout(() => {
            // if you have items you can change view
            state.update('config.chart.time', (time) => {
                time.from = startTime;
                time.to = endTime;
                console.log(`${year}-${month + 1}-01`, `${year}-${month + 1}-01`);
                return time;
            });
            loading = '';
            overlay = '';
        }, 250);
    }

    let listenerAdded = false;
    function getEl(element) {
        if (listenerAdded) return;
        element.addEventListener('change', (ev) => {
            if (month !== ev.target.value) {
                month = Number(ev.target.value);
                updateTime();
                update();
            }
        });
        listenerAdded = true;
    }

    function setPrevYear() {
        if (loading) return;
        year -= 1;
        updateTime();
        update();
    }

    function setNextYear() {
        if (loading) return;
        year += 1;
        updateTime();
        update();
    }

    function setPrevMonth() {
        if (loading) return;
        month -= 1;
        if (month < 0) {
            month = 11;
            year--;
        }
        updateTime();
        update();
    }

    function setNextMonth() {
        if (loading) return;
        month += 1;
        if (month > 11) {
            month = 0;
            year++;
        }
        updateTime();
        update();
    }

    function toggleHideWeekends(ev) {
        hideWeekends = ev.target.checked;
        gstc.api.time.recalculateTime();
    }

    // return render function
    return (content) =>
        html`<div class="tool-shelf" style="margin:20px;">
      Year: <button style="margin-left:20px;" @click=${setPrevYear}><</button><input type="number" .value=${year}><button style="margin-right:20px;" @click=${setNextYear}>></button>
      Month: <button id="btn-prev-month" style="margin-left:20px;" @click=${setPrevMonth}><</button><select get-element=${getElement(
            getEl
        )}>${months.map(
            (monthText, index) => html`<option value=${index} ?selected=${index === month}>${monthText}</option>`
        )}</option></select><button id="btn-next-month" style="margin-right:20px;" @click=${setNextMonth}>></button>
    <input type="checkbox" id="hide-weekends" @change=${toggleHideWeekends} /> <label for="hide-weekends">Hide weekends</label>
    </div>${content}<div class=${overlay}>${loading}</div>`;
}

let hideWeekends = false;
function onLevelDates({ dates, level, format }) {
    if (format.period !== 'day') return dates;
    if (!hideWeekends) return dates;
    return dates.filter((date) => date.leftGlobalDate.day() !== 0 && date.leftGlobalDate.day() !== 6);
}

const config = {
    licenseKey:
        "...",
    innerHeight: 800,
    plugins: [
        //HighlightWeekends(),
        TimelinePointer(), // timeline pointer must go first before selection, resizing and movement
        //Selection(),
        //ItemResizing(), // resizing must fo before movement
        //ItemMovement(),
        CalendarScroll(),
        //ProgressBar(),
        TimeBookmarks({
            bookmarks,
        }),
        DependencyLines({
            enabled: false,
            onLine: [
                (line) => {
                    line.type = GSTC.api.sourceID(line.fromItem.id) === '3' ? 'smooth' : 'square';
                    return line;
                },
            ],
        }),
    ],
    list: {
        row: {
            height: 40,
        },
        rows,
        columns,
    },
    chart: {
        time: {
            calculatedZoomMode: true,
            from: startDate.valueOf(),
            to: endDate.valueOf(),
            onLevelDates: [onLevelDates],
        },
        item: {
            height: 42,
            overlap: false
        },
        items,
    },
    scroll: {
        vertical: { precise: true },
    },
    slots: {
        'chart-timeline-items-row-item': { content: [itemSlot] },
        'list-column-row': { content: [rowSlot] },
        //main: { outer: [mainOuterSlot] },
    },
};

let gstc;
let state = GSTC.api.stateFromConfig(config);
(async function mountGSTC() {
    const element = document.getElementById('gstc');

    gstc = GSTC({
        // @ts-ignore
        element,
        state,
    });

    //@ts-ignore
    globalThis.state = state;
    //@ts-ignore
    globalThis.gstc = gstc;
})();

gantt-schedule-timeline-calendar version
What version are you using?
'3.33.13'

Screenshots or movies
Example 1
image

Example 2
before vertical scroll
image

after vertical scroll
image

@LucasRotondaro LucasRotondaro changed the title dependency-lines show in the wrong dependency-lines show in the wrong track Feb 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant