feat(tracker): Gantt — #5 tier-2 (undo/redo, saved views, bulk-select, auto-scheduling)#10857
Draft
MichaelUray wants to merge 272 commits into
Draft
feat(tracker): Gantt — #5 tier-2 (undo/redo, saved views, bulk-select, auto-scheduling)#10857MichaelUray wants to merge 272 commits into
MichaelUray wants to merge 272 commits into
Conversation
This was referenced May 18, 2026
|
Connected to Huly®: UBERF-16447 |
User-requested UX consolidation: the same compact "+ New issue" button should appear above every Tracker viewlet (List, Kanban, Gantt) instead of the bulky text+dropdown HeaderButton. - Replace HeaderButton with two stacked Button instances: * primary blue circular icon-only IconAdd (newIssue / newProject) * regular grey circular IconDropdown with secondary actions popup - Always visible regardless of viewlet (kind of a tab-bar shortcut) - showTooltip surfaces the keybinding from the existing tracker action - Draft indicator stays as small dot on top-right of the plus button Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The sidebar resize handle and the canvas pointer-pan handler both lived on the gantt-scroller, so dragging the handle started a canvas pan in parallel — the result was visible stutter and barely-controllable resize. Two fixes: - Add .resize-cell to onCanvasPanStart's element exclusion list so the pan never starts when the handle is the pointerdown target. - The resize-cell's own pointer handlers now stopPropagation + preventDefault, so even if a sibling listener picked up the bubbled event it would not double-process it. setPointerCapture stays so the drag tracks the pointer outside the cell bounds. Plus the v16 toolbar consolidation: - Move the always-visible blue + button to IssuesView.svelte (the shared wrapper for List, Kanban and Gantt). Single source — no duplication per viewlet. - NewIssueHeader stays as the original HeaderButton-based component; inline category-level + buttons in the issue list are unaffected. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…yle) The previous architecture put the canvas-stack inside one big .gantt-scroller with overflow:scroll, so the native horizontal scrollbar lived at the bottom of the entire scroll content — far below the viewport once many rows were scrolled past. Noted: this; Plane uses a separate sticky h-scrollbar at the bottom of the visible area. This commit splits the two scroll axes: - .gantt-scroller now has overflow-y:auto + overflow-x:hidden. It carries vertical scrolling for the rows. Vertical native scrollbar unchanged. - A separate .gantt-hscrollbar sits as a sibling at the bottom of .gantt-root (always visible regardless of vertical scroll position). It contains a thin track of width=totalCanvasWidth. - The canvas-cell and header-cell become overflow:hidden, with inner wrappers translated by -hScroll.scrollLeft. Horizontal scroll is therefore controlled by the proxy bar but the visual sync matches what native scrolling would do. - Pan-drag, jump-to-* and all programmatic scrolls now operate on the proxy bar instead of the gantt-scroller. - ResizeObserver watches both scrollers so changes to either side re-sync the viewport bounds. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The previous implementation had the previous v18 sticky horizontal scrollbar still
relied on a native ::-webkit-scrollbar inside an environment where
Huly globally hides scrollbars (\`* { scrollbar-width: none }\`). On
some browsers/OS combinations the bar rendered as a 6px ghost the
user could not see.
This commit replaces the native bar with a DOM-based custom thumb:
- .gantt-hscrollbar contains an .hscroll-shell (position:relative,
flex:1, min-width:0, overflow:hidden) that fits the viewport width.
- Inside the shell: an .hscroll-track-custom (overflow-x:scroll) is
the actual scroll-source — its native scrollbar is forcibly hidden
on both Firefox (scrollbar-width:none) and WebKit (display:none).
A 1px wide spacer drives its scrollWidth.
- A sibling .hscroll-thumb is absolutely positioned over the shell
and its left/width are computed from canvasViewportLeft and
totalCanvasWidth. Pointer drag on the thumb writes back to
hScrollEl.scrollLeft (and pointermove updates the position
reactively).
- Click on empty track area page-scrolls 80% of viewport.
Result: a guaranteed-visible 11px tall thumb that follows the
canvas timeline regardless of browser, OS, or Huly's global scroll
styling.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…date range Robustness fix: - .gantt-hscrollbar is now position:absolute pinned to gantt-root bottom (z-index:10) instead of relying on the flex chain to keep it visible. .gantt-scroller reserves padding-bottom:16px so the bar never paints over the last canvas row. Vertical custom scrollbar (parity with horizontal): - Same DOM-thumb pattern: hidden native scroll on .gantt-scroller, custom .vscroll-thumb absolutely positioned at the right edge. - Thumb position derives from scrollTop / vScrollMax; pointer-drag writes back to scrollerEl.scrollTop. Auto-hides when content fits. Status-driven bar colors: - Pull statusStore.byId once into statusCategoryMap; pass through GanttCanvas to each GanttBar. Categories map to fills: backlog/unstarted -> theme button-default (grey), todo -> blue, active -> amber, won -> green, lost -> muted grey. Tight time-range: - computeDateRange now pads by exactly one zoom-unit on each side (1d / 7d / 30d / 90d) instead of always 14 days. - totalCanvasWidth = ceil(scaleWidth) only — no longer inflated to fill the viewport, which previously stretched the timeline across empty quarters with no issues. If the data fits in the viewport, the canvas just doesn't horizontally scroll. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
- New StatusBadge.svelte renders a 12px coloured dot per row, mapped from the issue's status category via statusStore: backlog grey, todo blue, active amber, won green, cancelled muted. - New ganttShowStatus ToggleViewOption (default ON) joins the existing Show-issue-code and Show-title in the Customize-View dropdown. - Sidebar gains a 22px col-status column rendered before the issue-code cell. Milestone rows show a small "MS" tag instead of an issue code. - Tracker-assets en.json + 12 other locales gain GanttShowStatus additively (+1 line per file). Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Implements 8 of the 9 action items from the an external UI review
redesign review. Item 8 (Geist font swap) skipped — would override
Huly's global Inter and is out of PR2 scope.
1. Today-marker date pill: small red rounded label at the top of the
today line showing "10. May" so the marker speaks for itself.
2. Active-status bars get a stronger fill (#f59e0b amber + white
text), Won bars green with white text, ToDo bars sky-blue with
navy text — bars now read at a glance.
3. Sidebar gains a date-range subheader strip ("Feb 2026 – Mar 2027"
with « » page-nav and click-to-jump-to-today behaviour). Header
row height grows from 40 to 56px to fit the stacked layout.
4. Hover spotlight: when any row is hovered, non-hovered rows fade
to 0.55 opacity (external review-style focus).
5. Milestone reference lines shrink from full-canvas dashed lines
to a tick + filled triangle in the top 14px of the canvas —
less visual noise behind the bars.
6. Toolbar Unicode glyphs (⏮ « » ⏭) replaced with Huly's own
ArrowLeft/NavPrev/NavNext/ArrowRight icons; date input wrapped
in a label with a Calendar icon.
9. Scrollbars slimmed from 14px to 10px tracks with semi-transparent
thumbs (opacity 0.45 → 0.85 on hover, 1 on drag).
Item 7 (theme-aware bar colors) was already wired via statusCategory
Map in v25 — this commit just verifies the path.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Plane-style: dashed-top-border "+ Add issue" row at the bottom of
GanttSidebar opens the standard CreateIssue popup via showPopup.
- GanttSidebar dispatches `addIssue` from a keyboard- and click-
accessible row using the existing tracker.string.AddIssue label.
- GanttView wires it to showPopup(CreateIssue, { space, shouldSaveDraft }).
- Row is hidden when no space is available (defensive guard in
newIssue handler).
Verified on dk3 huly_v7 (gantt-v28): row renders, click opens
"New issue" dialog with Issue title field focused.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Adds Issue.startDate and Issue.dueDate to the ClassFilters mixin for tracker.class.Issue. Both attributes are TypeDate, which view-model already wires to view-resources DateFilter (with built-in range support), so this is the minimal change to expose date-range filtering in the Tracker filter dropdown — works in List, Kanban, and Gantt. NB: deploy of this model change is batched with the PR3 multi-pod deploy (transactor + workspace + tool image + workspace upgrade); front:gantt-v28 alone is not enough since the ClassFilters mixin TX lives in the workspace model store. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
GanttView computes hHasOverflow but never gated the bar on it — the
sticky-bottom scrollbar was rendered unconditionally, including in
empty workspaces where it was just visual noise. In addition, when
totalCanvasWidth was barely larger than the track, hThumbWidth could
exceed hTrackWidth and the thumb would visually overflow its container.
Fixes:
- Wrap the .gantt-hscrollbar element in `{#if hHasOverflow}` so it
disappears in the no-overflow case.
- Clamp the rendered thumb width to `Math.min(hThumbWidth, hTrackWidth)`
to defuse the degenerate over-track case.
in Playwright: empty workspace no longer renders the scrollbar.
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>
…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>
…s handler An external code review flagged GanttBar's tabindex="-1" + svelte-ignore as a potential upstream-review red flag. The behaviour is intentional: focus management is centralised in GanttView.onKey (Tab/Shift-Tab cycles scheduledIssues in visual order, Arrows shift focused bar). Native per-rect Tab navigation would cycle in DOM order which doesn't match chart order, so we route everything through moveFocus() instead. Spell that contract out in a block comment above the non-summary bar <rect>, mirroring the existing summary-path comment, with a pointer to the canonical handler location in GanttView.svelte. Pure documentation, no runtime change. Tabindex="0" + native browser focus order remains a possible follow-up if upstream requests it; leaving as Option-A (documentation) per v121.19 task scope. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Rebase --onto upstream/develop introduced conflicts that were resolved by keeping HEAD versions, losing content from later commits in the branch history. Fixed: - Restore all gantt/*.svelte and lib/*.ts to pre-rebase-backup state (upstream/develop does not touch the gantt directory) - Remove duplicate function bodies in scheduler.ts and drag-controller.ts - Restore 71 missing ViewOptions in models/tracker/src/viewlets.ts - Add missing @hcengineering/notification dep to plugins/tracker/package.json - Restore missing IntlStrings in tracker plugin index.ts (Deadline, BarLabel*, GanttBarLabel*, GanttQuickInfoOnClick, GanttSavedView*, GanttMobile*, GanttExport*, GanttFullscreen, GanttMoreActions, QuickInfoOpenFullEditor, DependencyShifted*) - Restore 27 missing en/de lang strings (GanttSavedView*, GanttMobile*, GanttExport*, GanttFullscreen, GanttMoreActions) - Fix trackerModel -> tracker in models/tracker/src/migration.ts - Restore 31 missing strings in tracker-resources/src/plugin.ts - Add DeadlineEditor usage back in ControlPanel.svelte Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ly correct cast) Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…te-check pass Add missing pure-logic modules (bulk-boundary, bulk-selection, dependency-shift-send, dependency-shift-notify, flash-store, gantt-view-options, saved-views, tree-expand-store, undo-manager, y-viewport) that GanttView.svelte imports. Fix dependency-router.ts to include full ArrowVisibility/YBounds/clippedEndpointPx exports. Add multiSelectedIssueIds + layoutMode props to GanttCanvas. Add hasDeadline/isOverdue import to GanttCanvas. Fix GanttBar resize handles to use on:pointerdown instead of undefined on:mousedown. Add coDrag field to DragEvent mousedown-bar and DragState dragging-body types. Add ShiftedIssuePayload + DependencyShiftedNotification interfaces to tracker plugin. Add GanttExpandAll/GanttCollapseAll/GanttTreeBreadcrumb i18n strings. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
At low pixels-per-day densities (month=4, quarter=1.5) the chart fits in the viewport with no horizontal overflow, so the custom h-scrollbar element is not rendered. The wheel-zoom handler early-returned on the missing element, leaving Ctrl+Wheel a no-op in those zoom levels. Fall back to scrollerEl for the cursor-anchor bounding box and skip the scrollLeft-anchor step entirely when there is no overflow (nothing to scroll). Wheel-zoom now works at every density. Also introduce adaptiveWheelFactor: 0.012 for ppd<4 (month/quarter), 0.006 for ppd>=4 (week/day). Exponential math is multiplicatively adaptive, but in the low-density bands the absolute pixel delta per notch is small enough that users perceive the same exp step as slower than at high density. The bumped factor restores subjectively equal responsiveness across all four presets. Five new unit tests cover the factor boundaries and the adaptive-vs-fixed contrast at ppd=1.5 and ppd=14. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…wlet renders its own The shared View-Options popup renders a Group-by + Order-by dropdown pair plus a list of viewlet-specific toggles (bar labels, confirm dialogs, etc.). In Gantt mode the toolbar already provides its own dedicated group-by and order-by dropdowns wired to the Gantt-internal layout, so the popup's pair is visually duplicated and not wired to anything — users see the same control twice and changing the popup version has no effect. Add a hideGroupingAndOrdering prop that propagates from ViewletSettingButton → ViewOptionsButton → ViewOptions. When true the popup skips its grouping and ordering rows (plus the separator before the toggles) and renders only the 'other' toggles, which remain wired. IssuesView passes hideGroupingAndOrdering=isGanttMode so list mode keeps the previous behaviour and Gantt mode shows only the useful toggles. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…line comments Strips internal-tracking phrases from source-code comments: Tier-N Item M, Tier-N #M, v121.NN, per Tier-N spec, plus leftover internal review markers mentions. The comments themselves are preserved — only the attribution labels are dropped so the noted constraint or hidden invariant survives. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…idebar The Extended Sidebar toggle (ganttSidebarColumnsExtended) used to ship a hardcoded identifier/title/predecessors/slack quartet with no UI to add the other supported columns. The Configure-Columns button is generic viewlet-config and has no effect on the Gantt sidebar. Add 8 per-column ViewOption toggles under the Extended Sidebar toggle in the Customize-View popup: Status / Priority / Assignee / Estimation / StartDate / DueDate / Deadline / Progress. Each toggle appends its column to the default set; the order matches the natural reading order on the sidebar (status..progress..predecessors..slack). Toggles are collapsed when the Extended Sidebar toggle is off so the popup does not grow indefinitely. Also raises the sidebar size limits: total width 600→1200 px, per-column width cap 400→600 px, so the extra columns actually have room to render. Finally adds a showConfigureColumns prop on ViewletSettingButton (default true). IssuesView passes false in Gantt mode so the configure-columns icon no longer renders there — it had no effect on the Gantt sidebar anyway. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
After the PR3a refactor dropped the duplicate \« \» date-nav buttons from the corner-range, the remaining tree-toggle buttons (collapse-all, expand-all) still inherited the old `justify-content: center` and ended up visually floating in the middle of the corner cell. Switch to `flex-start` so the buttons hug the left edge of the sidebar grid, matching the alignment of the column-toggle chevrons in the rows below. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…r gantt polish tier
Reviewer-approved fix patterns applied to the pre-existing eslint errors the
CI formatting job surfaces in the PR4 polish-tier code, alongside the prettier /
eslint --fix normalisation after the develop merge:
- no-confusing-void-expression: block-body arrows / drop redundant void operator
(GanttCanvas dblclick, GanttView reactive recompute);
- no-non-null-assertion: drop redundant `!` on already-any-cast dragTarget;
- strict-boolean-expressions: explicit nullish handling in exporter;
- no-case-declarations: brace case blocks with lexical declarations (bar-labels);
- naming-convention: __resetConfirmGate -> resetConfirmGate (test reset helper);
- require-array-sort-compare: explicit localeCompare in build-rows test;
- balance the connector-overlay nested {#if} (prefer-optional-chain split) that
the merge left one {/if} short.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…lector types
svelte-check surfaced type errors from the cascade merge + eslint import
consolidation in the PR4 polish tier:
- './lib/types' merged into an `import type { ... }` with redundant inner
`type` modifiers (invalid) — flatten to plain names;
- descendantsWithDates dropped from the './lib/scheduler' import (used in the
scheduler/cascade paths) — restore it;
- getEventPositionElement dropped from the @hcengineering/ui import (used by
openGanttMenu) — restore it;
- exporter expandForCapture passed Element (untyped querySelector) where
HTMLElement is required — add the <HTMLElement> generic, matching the
querySelectorAll call in the same function.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Re-materialize the exact intended tip content for Gantt component and i18n files that were carried via merge commits. Linearizing onto the new develop dropped those merges; develop does not touch these files. 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>
…ion for all v1 entry kinds Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…lete via DependencyEditor 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>
…controller Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…scade pass Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…gression tests Adds a GanttUndoConflictHint i18n string and surfaces it as the notification sub-line when a Ctrl-Z gets dropped due to a conflict, so users understand why repeated Ctrl-Z does not retry the same frame. Both conflict and error paths now emit a console.warn with the original UndoEntry to make intermittent 'undo did nothing' reports debuggable in DevTools. The cascade-frame atomicity was already wired (commitCascadeBatch pushes one date-batch UndoEntry; applyInverse iterates inside one client.apply scope so commit() is all-or-nothing). Adds 4 explicit regression tests that pin that contract: 5-issue cascade applies in one commit, commit throw leaves zero partial mutations, conflict drops the frame from the stack without re-push, conflicted result carries the original entry for the debug-log payload. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…eCollection Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…w (de)serialization 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>
…line comments Strips internal-tracking phrases from source-code comments (Tier-N Item M, Tier-N #M, v121.NN, per Tier-N spec, leftover internal review markers). Comments themselves are preserved — only the attribution labels are dropped. 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>
2e27fb2 to
c7721ac
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
Adds four medium-sized productivity features on top of the polished Gantt:
UndoManagerwith apply + conflict detection acrossv1entry kinds, plus visual feedback when an entry can't apply cleanly.GanttSaveViewPopupscaffold from PR4; (de)serialization helpers + partition / filter helpers.What's in this PR (+13 incremental commits)
Undo / redo
UndoManagerwithapply+ conflict-detection for allv1entry kindsflash-storehelper for visual feedback on conflict / applyDependencyEditorremoveCollection(so activity feed stays consistent)Saved views
gantt-view-options(de)serialization helpers for saved-view round-tripGanttSavedView*strings (en + de)Bulk drag
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 → PR4.
How to review
→ Fork compare: PR4...PR5 — 13 commits, 12 files
Largest single file is
undo-manager.test.ts(~650 lines) — covers everyv1entry kind plus the cascade-atomicity regression cases.Testing
UndoManager(650-line test file).DCO
All commits are
Signed-off-by.