feat(tracker): Gantt — #3a editable viewlet (drag, resize, dnd)#10854
Draft
MichaelUray wants to merge 62 commits into
Draft
feat(tracker): Gantt — #3a editable viewlet (drag, resize, dnd)#10854MichaelUray wants to merge 62 commits into
MichaelUray wants to merge 62 commits into
Conversation
|
Connected to Huly®: UBERF-16444 |
This was referenced May 18, 2026
Draft
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…reactive queries PR 2 read-only Gantt is now end-to-end functional: - Two reactive queries (issues, milestones) - buildLayout(issues) -> rows with depth + isSummary - createTimeScale(zoom, dateRange.from) -> date<->px math - Summary ranges computed per parent issue from children's date span - Local zoom state (Day/Week/Month/Quarter) - no ViewletPreference plumbing - Horizontal scroll via canvas-scroller; vertical scroll moves Sidebar via translate Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
- viewlets.ts: register IssueGantt AFTER IssueKanban so List stays default - viewlets.ts: drop showColorsViewOption (canvas does not honour it yet) - GanttBar.svelte: normalise reversed startDate>dueDate ranges UX: - GanttSidebar adds Title column with sticky two-column header - Sticky time-scale header decoupled from milestone strip (layout fix) - Per-row jump-to-bar arrow (Plane-style) when bar is offscreen - ResizeObserver initialises viewport on mount and on resize Milestones as Gantt rows + collapse: - Milestones group their issues as nested children (depth+1) - Collapsible toggle per parent row, local collapsedIds Set - Milestone summary bar uses existing GanttBar with aggregated range Icon: - Register tracker.icon.Gantt to #timeline svg (Gantt pictogram) Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
- Visible top row with [«] [Today] [»] | Day Week Month Quarter | ⚙ - Today scrolls to current date, prev/next page-scroll by 80% viewport - Settings popover toggles Issue-Code and Title columns Layout: - Bigger row height (28→36) and bar font (11→13px) for readability - Sidebar reworked into flex-column with separate clipped rows region so scrolled rows can never paint over the sticky header(layout fix) - Drag handle between sidebar and canvas (120–600px range) - ResizeObserver re-syncs viewport on drag/resize/zoom changes Interaction: - Vertical gridlines aligned to time-scale ticks (Plane-style) - Row hover highlights both sidebar and canvas, with rich HTML tooltip (issue title + start + due + duration) - Title click in sidebar dispatches openIssue → showPanel(EditIssue) - Double-click on canvas bar dispatches openIssue - Wheel forwarding from sidebar to canvas-scroller (vertical scroll works while hovering issue list) - Pointerdown + drag on empty canvas pans both axes - Jump-to-bar buttons now use the bar's true left-edge target Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
totalCanvasWidth (was viewport width), with viewBox in scroll-content coordinates. The sticky header lives in the same coordinate system as the canvas-stack so horizontal scroll no longer clips the time-axis. - showPanel uses tracker.component.EditIssue instead of the hardcoded string + 'as any' cast. - Removed unused GanttToolbar.svelte and GanttMilestoneFlag.svelte (dead files since toolbar moved into GanttView and milestone flags became rows). Toolbar: - Time-navigation cluster: ⏮ « Today » ⏭ + native date picker that jumps to a specific date. Replaces the lone Today button. Interaction polish: - Sidebar wheel forwarding now uses direct scrollTop/scrollLeft mutation with deltaMode scaling — same speed as native canvas scroll. - TodayMarker no longer hides when scrolled offscreen (SVG width handles clipping); milestone reference lines render unconditionally for the same reason. Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
- Register two ToggleViewOptions on the Gantt viewlet: ganttShowIssueCode (default OFF) and ganttShowTitle (default ON). These show up in the standard Customize-View dropdown. - Drop the per-component settings popover from GanttView; sidebar column visibility now reads viewOptions directly. - Hover tooltip always surfaces the issue code (e.g. OSTRO-31), even when the issue-code column is hidden. - IntlStrings + en/de/es/fr/it/ja/pt/ru/zh/cs translations added. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Bug: .gantt-sidebar rendered at its natural height (≈ rows × 36px), and .sidebar-host had overflow:visible, so for many issues the sidebar visually overflowed gantt-body which in turn made gantt-body's scrollHeight balloon to the sidebar's natural height. The result was that scrolling over the sidebar appeared to scroll the page area instead of just the canvas. Fix: - .sidebar-host now overflow:hidden + height:100% + flex-column - .gantt-sidebar height:100% + flex:1 1 auto so it fills the host rather than stretching past it Verified: gantt-body scrollHeight == clientHeight, only .canvas-scroller has scrollable content. Wheel-forward over the sidebar moves canvas-scroller and the sidebar transform together. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ticky) Replaces the dual-scroller architecture (sidebar + canvas as siblings, manual wheel-forwarding for the sidebar) with a single .gantt-scroller that wraps both columns in a CSS grid. Sidebar uses position:sticky left:0 so it stays at the left edge during horizontal scroll, while the time-axis header uses position:sticky top:0. Browser handles all wheel events natively at native speed. Why: the previous design rendered the sidebar at its natural height (rows × 36px), which could be much larger than the visible viewport. overflow:visible on the sidebar host let it bleed past gantt-body and made the page area appear to scroll. The wheel-forward hack only worked for synthetic events; in real browsers, wheel events over the sidebar either scrolled too slowly (deltaMode mismatch) or stopped firing. Layout (CSS grid, 2 rows × 3 cols): - (1,1) sticky top+left: corner with column titles - (1,2) sticky top: resize-corner (5px gutter) - (1,3) sticky top: time-axis header - (2,1) sticky left:0: GanttSidebar rows - (2,2) sticky left:Wpx: vertical resize-handle - (2,3) normal: GanttCanvas SVG overflow:scroll on .gantt-scroller forces both scrollbars to always render — the user explicitly asked for visible scrollbars (Plane parity). GanttSidebar drops its scrollTop prop and the transform-based row positioning; rows now flow naturally inside the scroll container. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The standard NewIssueHeader is rendered by the Tracker shell outside the viewlet. Inside the Gantt view this looked like the action was missing. Add a prominent + New issue button in the gantt-toolbar that opens CreateIssue with the current project space pre-selected, matching the behaviour of NewIssueHeader. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…onents
Resolve four pre-existing eslint errors that block the CI formatting job:
- GanttCanvas.svelte: split the milestone null-guard into nested {#if}
blocks (prefer-optional-chain) — a naive optional chain would bypass
the strict null check when range is null.
- GanttCanvas.svelte / GanttSidebar.svelte: convert void-returning
short-circuit handlers to block-body arrows (no-confusing-void-expression).
- layout.test.ts: drop the non-null assertion in favour of optional
chaining on the assertion (no-non-null-assertion).
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…sitions Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…mping Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…t/tooltip) Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…permission map Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ize overlay Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ttView Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ocus) Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…both dates Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The drag-controller reducer only accepted mousedown-bar from hover-bar state, but no DOM event ever transitioned idle → hover-bar (we never wired the mouseenter-bar event from GanttBar to the reducer). The hover state was dead code, and the first mousedown on a freshly-rendered bar was silently dropped — Playwright integration test surfaced this. Fix: reduceFromIdle accepts mousedown-bar directly with the same edge-discriminator logic as reduceFromHover. Real users always go through hover-bar first (mouseenter fires before mousedown), but synthetic event dispatch and re-render edge cases can skip it. Adds 2 tests: - mousedown-bar from idle → dragging-body - mousedown-bar (edge=left) from idle → resizing-left Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…sion 1. Context menu is too tall (~19 entries from auto-resolved Huly actions). Restructured via includedActions allow-list to 9 curated entries that make sense for a Gantt bar: - Open (Edit issue) - Status / Priority / Assignee (existing actionPopup submenus, ›) - Set start date / Set due date - Copy issue ID / URL - Duplicate / Delete Component, Milestone, Labels, SubIssue, SetParent, Relations, Move-to-project and time-report stay accessible from the standard List/Kanban menu but are hidden from Gantt's right-click. 2. Unscheduled-drag-grip in the sidebar shared pointerdown with the canvas pan handler. Pan would start before the grip's mousedown|stopPropagation fired, swallowing the drag. Extended onCanvasPanStart exclusion: now also bails on .sidebar-cell, .drag-grip, .resize-handle. The grip's pointerdown no longer triggers a stray pan. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…Sub-Issue User UX feedback 2026-05-11: the allow-list approach hid Open (Edit issue) because tracker registers a custom Open action for tracker.class.Issue with an auto-generated ID, and it also hid Set parent issue / Add sub-issue which the user explicitly needs to manage issue hierarchy from the Gantt. Switch from includedActions to excludedActions. Hide only the genuinely noisy entries for a Gantt right-click: Component, Milestone, Labels, CopyIssueTitle, Relations, NewRelatedIssue, EditRelatedTargets, MoveToProject, CopyAsMarkdownTable, UnsetParent. Result: Open / Status / Priority / Assignee / Set start date / Set due date / Add sub-issue / Set parent issue / Copy ID / Copy URL / Duplicate / Delete remain — 12 entries instead of 18, with the expandable submenus (Status, Priority, Assignee ›) keeping the visible menu compact. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…pra labels
User requirements 2026-05-11:
1. Restructure Gantt right-click menu: "Sub-issue & Parent" as a single
collapsed Hierarchy ▸ submenu containing
- Set parent issue…
- Add sub-issue
- Link existing as sub-issue… (NEW)
The old flat SetParent + NewSubIssue entries are hidden from the
parent menu (excludedActions) so the Gantt context menu stays compact.
2. EditIssue panel: add a "Set parent issue…" button visible when the
issue has no parent — previously this was only reachable via right-
click context menu, not from the open issue itself.
3. SubIssues panel: add a "Link existing as sub-issue" button next to
the existing "Add sub-issue" button so the user can attach an
already-existing issue without first opening it.
4. GanttHeader two-row labels (user feedback: "year/month above"):
- day view: month name above day number
- week view: year above Wnn
- month view: year above month name (was entirely missing)
- quarter view: year split out above Q1/Q2/… (was side-by-side
"Q1 2026" — visually noisy at dense zoom)
Supra-label renders only at segment boundaries (1st of month, first
week of year, January, Q1) so it doesn't repeat in every column.
3 new jest tests cover day/week/quarter supra cases.
- Hierarchy UiAction missing required `action` field — added no-op
(Menu.svelte routes the click via `component:` when present).
- dragging-unscheduled no longer shifts existing descendants — the
synthetic "today" origin would have shifted them by a wildly
unrelated delta. Unscheduled-drag now only commits the parent.
- LinkSubIssueActionPopup: full ancestor + transitive descendant
ignore set so the picker can't create cycles (BFS over the
parents[0].parentId edge, matching scheduler.descendantsWithDates).
- releasePointerCapture now guarded by hasPointerCapture(pointerId)
to avoid InvalidStateError when a pointerup bubbles from an
excluded child (resize-handle, drag-grip).
- openSetStartDate (context menu) now surfaces commit failures via
NotificationSeverity.Error, matching commitDrag's error handling.
- i18n: drag-grip tooltip and duration tooltip pulled out of hardcoded
English into tracker.string.GanttDragToSchedule + GanttDurationTooltip.
New files:
- plugins/tracker-resources/src/components/LinkSubIssueActionPopup.svelte
- plugins/tracker-resources/src/components/gantt/GanttHierarchySubmenu.svelte
i18n: 5 new keys (Hierarchy, LinkExistingSubIssue, SetParentIssueLabel,
GanttDragToSchedule, GanttDurationTooltip).
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…mmary-bar interactivity
User UX requirements 2026-05-11:
1. Click-to-select gate: a bar must be clicked once to become "selected"
(solid blue outline, .bar.selected) before the next mousedown
actually starts a drag/resize. Prevents accidentally dragging an
issue while panning the canvas horizontally — earlier feedback was
that fast horizontal pans would catch a bar by mistake. Plane-style
UX. Clicking the canvas background clears the selection.
2. Confirmation popup before commit: GanttConfirmCommitPopup shows a
"Move X to Y – Z?" / "Change X to Y – Z?" dialog with Cancel/Apply
buttons. Two independent ViewOption toggles (Customize-View → Display)
gate whether the popup appears:
- ganttConfirmMove (default-on) — drag-body
- ganttConfirmResize (default-on) — resize-left/right
Saves to the same per-viewlet option store as ganttShowStatus, so
each user gets their own preference. Skip the popup entirely when
the preview is unchanged (zero-delta drag).
sub-issues it renders as the MS-Project claw rather than a regular
rect. Previously the claw had no mousedown/contextmenu, so commitDrag's
parent-pulls-children path was unreachable from the UI. A transparent
`summary-hit` rect now spans the claw and wires the standard
onBarDown / onBarContextMenu handlers, plus the two resize-handles.
Milestone summaries (issueObj === undefined) stay read-only.
- Split GanttConfirm{Move,Resize} setting label from dialog title.
New keys: GanttConfirmMoveTitle = "Move issue?", GanttConfirmResizeTitle
= "Change issue dates?". Setting labels remain "Confirm before moving an
issue" etc.
- Drop unused GanttClickToSelect string.
- New jest suite `link-sub-issue-cycle.test.ts` (6 tests) fences the
ancestor/descendant/self/cycle ignore logic so future refactors can't
silently regress cycle protection.
Deploy:
- models/tracker + models/all rebuilt to ship the new ViewOption keys.
- front/transactor/workspace/tool images at gantt-v35.
- Workspace upgrade applied to all 3 dk3 workspaces — toggles appear in
Customize-View → Display.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…et + a11y roles
per explicit user preference for both select-first + confirm-popup
default-on):
1. Parent-drag now shifts the FULL descendant tree, not just children
that happen to be in the view's filtered `issues` array. Previously
a Tracker filter that hid some sub-issues caused them to stay at
the old date while their parent moved — silent tree drift. commitDrag
and shiftFocused both now run `client.findAll(tracker.class.Issue,
{ space: issue.space })` to query the full space before walking
descendantsWithDates. One extra query per commit, no impact on drag-
interactive frame rate.
2. computeCanvasX (unscheduled drag) now offsets by sidebarWidthPx +
5 px instead of just sidebarWidthPx. The 5 px is the resize-cell
column between sidebar and canvas (.resize-cell). The hscrollbar
has always used this offset (style="padding-left: {sidebarWidthPx +
5}px"); unscheduled-drag was the only consumer that missed it,
causing drops to land a few pixels off at high zoom.
3. A11y: the 6 interactive <rect>s on GanttBar (regular + summary +
resize handles, both branches) now carry role="button",
tabindex="-1", and aria-label so screen readers announce them as
actionable. The a11y-click-events-have-key-events warning is
suppressed per-rect with svelte-ignore plus a banner comment
explaining that keyboard support lives at the GanttView level
(Tab/ArrowLeft/Right). 94 → 88 warnings.
Tests: 5 suites / 60 passed.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…bels
friction observation about select-first + confirm-popup left as-is
per explicit user preference, the parent-drag performance note is a
future-large-space concern):
1. .settings-btn / .settings-popover removed from GanttView.svelte.
They were leftover from an earlier custom-popover prototype that
was replaced by Huly's standard ToggleViewOption pattern in PR2;
the CSS was orphaned. Drops 4 svelte-check warnings.
2. Resize handle ARIA labels were hardcoded English ("Resize start" /
"Resize end"). Now translated via tracker.string.GanttAriaResizeStart
and GanttAriaResizeEnd. Translation resolves async on themeStore
language change; falls back to the English default until resolved.
84 warnings (was 88).
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…comments
Strips reviewer-attribution markers ('review-N <date>') from source-code
comments. Comments are kept and rephrased as 'review note' so the noted
constraint or hidden invariant survives, only the internal attribution
is dropped.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
handleCanvasMouseUp is async; passing it directly to window add/removeEventListener triggers @typescript-eslint/no-misused-promises (Promise returned where a void return is expected). Introduce a single stable void-returning wrapper (onWindowMouseUp) used at all three sites so add and remove still reference the same listener object — a per-call arrow wrapper would leak the listener. The mouseup commit stays fire-and-forget with its existing internal error handling. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
lang.test enforces en/ru structural parity. Gantt feature keys added to en.json were missing in ru.json; filled with the en value as a placeholder pending translation (Huly convention). No existing ru translations changed. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
cbf2277 to
4e1b0f9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the Gantt viewlet editable: bars can be dragged horizontally to reschedule, edges grabbed to resize start- or end-date, and unscheduled issues dropped onto the canvas to set both dates at once. Adds keyboard navigation (Tab cycles bars, arrows shift by 1 day) and a per-bar right-click context menu. All write operations gate on per-issue edit permission.
Dependency editing (cascade, predecessor/successor links, critical path) is deferred to PR3b so this PR stays focused on bar-level interaction.
What's in this PR (+24 incremental commits)
drag-controllerreducer (idle / body-drag / resize-left / resize-right) with clamping and atomic commit; per-issue edit-permission map; activeDrag Svelte storestartDateanddueDatescheduler.descendantsWithDatesfor cycle-safe BFS (used by drag preview)Stack overview
…pr1-schema…pr2-readonly…pr3a-edit…pr3b-deps-cascade-cp…pr4-polish…pr5-tier2…pr6-tier3-virtualization…pr7-tier4MichaelUray:feat/gantt-sectionMarked as draft to signal stack dependency on PR1+PR2.
How to review
→ Fork compare: PR2...PR3a — 24 commits, 25 files
The reducer in
drag-controller.tsis the architectural centerpiece — the rest of the PR wires Svelte components into the four reducer states.Testing
DCO
All commits are
Signed-off-by.