Skip to content

feat(tracker): Gantt — #3a editable viewlet (drag, resize, dnd)#10854

Draft
MichaelUray wants to merge 64 commits into
hcengineering:developfrom
MichaelUray:feat/gantt-upstream-pr3a-edit
Draft

feat(tracker): Gantt — #3a editable viewlet (drag, resize, dnd)#10854
MichaelUray wants to merge 64 commits into
hcengineering:developfrom
MichaelUray:feat/gantt-upstream-pr3a-edit

Conversation

@MichaelUray
Copy link
Copy Markdown

@MichaelUray MichaelUray commented May 18, 2026

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)

  • State machinedrag-controller reducer (idle / body-drag / resize-left / resize-right) with clamping and atomic commit; per-issue edit-permission map; activeDrag Svelte store
  • Bar interaction — edge-detect for resize handles, drag preview geometry with tint, "spotlight" dim of non-active rows during drag, direct idle→drag transition (no click-first)
  • Drag from unscheduled — drop on canvas sets both startDate and dueDate
  • Keyboard — Tab cycles bars, arrow keys shift selected bar by one day
  • Right-click context menu — on bar and on sidebar row; deny-list for irrelevant default actions; restore Open / Parent / Sub-Issue actions
  • Scheduler utilscheduler.descendantsWithDates for cycle-safe BFS (used by drag preview)
  • Parent-drag — single-source-of-truth model, correct canvasX offset, a11y roles
  • Select-first drag + confirm popup for risky moves
  • Hierarchy submenu in EditIssue + supra labels in header
  • Local "Set start date" action for the Gantt menu
  • i18n keys for set-start-date + 5 toast/tooltip strings

Stack overview

# Branch Scope Status
PR1 …pr1-schema schema + version bump open
PR2 …pr2-readonly read-only viewlet draft
PR3a …pr3a-edit this PR — drag / resize / dnd draft
PR3b …pr3b-deps-cascade-cp dependencies + cascade + critical path draft
PR4 …pr4-polish polish draft
PR5 …pr5-tier2 undo, saved views, bulk, auto-scheduling draft
PR6 …pr6-tier3-virtualization virtualization draft
PR7 …pr7-tier4 mobile + tree + predecessor col + visual deps + notifications draft
Docs (huly-docs#70) MichaelUray:feat/gantt-section reference + screenshots open

Marked 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.ts is the architectural centerpiece — the rest of the PR wires Svelte components into the four reducer states.

Testing

  • Hand-tested all four reducer transitions (idle → body-drag / resize-left / resize-right) and the drag-from-unscheduled path.
  • Validated on a production-style deployment on 2026-05-18.

DCO

All commits are Signed-off-by.

Schema-only foundation for the upcoming Gantt-chart view in tracker.
No UI in this PR.

Changes:
- Issue.startDate: Timestamp | null (interface + IssueDraft + @prop with @Index)
- Milestone.startDate: Timestamp | null (interface + @prop, reusing the
  existing tracker.string.StartDate IntlString)
- New DependencyKind type ('finish-to-start' | 'start-to-start' |
  'finish-to-finish' | 'start-to-finish')
- New IssueRelation AttachedDoc class with kind: DependencyKind, signed
  lag: number — registered in models/tracker via TIssueRelation
- 7 new IntlString keys: IssueStartDate, GanttDependency,
  GanttDependency{FinishToStart,StartToStart,FinishToFinish,StartToFinish},
  GanttLag — all 13 locales updated
- Cross-plugin literal updates in importer + github sync to satisfy the new
  required Issue.startDate / Milestone.startDate fields:
  - packages/importer/src/importer/importer.ts: AttachedData<Issue> literal
  - services/github/pod-github/src/sync/issueBase.ts: 'startDate' added to
    GithubIssueData Omit list (github sync does not own scheduling)
  - services/github/pod-github/src/sync/issues.ts + pullrequests.ts:
    AttachedData<Issue|GithubPullRequest> literals

Out of scope (deferred to follow-up PRs):
- UI for Gantt view, drag/resize, dependency editor, critical path
- blockedBy → IssueRelation migration (ships atomically with the writer
  redirect in the dependency-UI PR)
- LinkIssues permission (tracker uses forbid-style permissions; needs
  maintainer discussion)
- Activity-feed wiring for IssueRelation (needs a producer to test against)
- IssueTemplate.startDate (template propagation semantics undecided)

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
3 tests covering migrateAddStartDate:
- writes startDate=null to Issues in DOMAIN_TASK with the right filter
- writes startDate=null to Milestones in DOMAIN_TRACKER with the right filter
- issues exactly two update calls (one per class)

Follows the MigrationClient mock pattern from
models/chat/src/__tests__/migration.test.ts.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…tion

Backfills startDate=null on existing Issues (DOMAIN_TASK) and Milestones
(DOMAIN_TRACKER) so the new schema field has a defined value on every
pre-existing document. Idempotent via the standard tryMigrate state-key
mechanism (state: 'gantt-add-startdate').

Verified domain choices against existing migration helpers:
- migrateIdentifiers / passIdentifierToParentInfo use DOMAIN_TASK for
  Issues (lines 145, 161 in this file).
- TMilestone @model decorator confirms DOMAIN_TRACKER for Milestones
  (models/tracker/src/types.ts:372).

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ghten typing

UI changes (so the new schema fields are actually editable, in chronological
order Start → Due/Target):

- New StartDateEditor.svelte (mirrors DueDateEditor.svelte for startDate)
- ControlPanel: render Start Date row above Due Date row in the issue
  side panel; both always-visible (no `!== null` guard) so users can set
  them on issues that don't have a date yet
- NewMilestone form: Start Date input above Target Date input
- Milestone list view: Start Date column before Target Date column

- TIssueRelation: tighten interface to `extends AttachedDoc<Issue, 'relations'>`
  so attachedTo + collection are statically typed. The model class
  re-declares `collection: 'relations'` to match the narrower base.
- Drop 4 unused Dependency-kind IntlString keys (FinishToFinish,
  FinishToStart, StartToFinish, StartToStart) — they had no consumer
  in PR 1; will be re-introduced in PR 4 (dependency editor).
- Simplify migration.ts comments — drop ageing line-references.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The DocAttributeBar side panel sorts attributes by attr.rank ?? toRank(_id)
(see plugins/view-resources/src/components/ClassAttributeBar.svelte:42-47),
so without explicit ranks the visible order on a Milestone was hash-based
(startDate before Status, breaking the chronological flow the user expects).

Set ranks so the side panel renders Status → Start date → Target date.
Comments and attachments stay where they are (they're collections, filtered
out of the attribute panel by categorizeFields).

Issues are unaffected — the Issue side panel is the custom ControlPanel.svelte
which renders Start date / Due date in explicit slots (see PR 1's UI commit).

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…body in chronological order

The right-side DocAttributeBar sorts attributes by attr.rank ?? toRank(_id),
giving startDate before status (toRank('startDate') < toRank('status')
lexicographically). Setting an explicit rank via @prop's third arg did not
propagate through the workspace upgrade for existing Attribute documents
in the model TX log — the rank made it into the bundled txes but the
existing Attribute creation TXes are not replaced on upgrade-workspace.

Pivot: render Status, Start date, Target date in the EditMilestone body
in explicit chronological order, and add 'status', 'startDate', 'targetDate'
to ignoreKeys so they don't appear duplicated in the side panel. This
mirrors how Issue's ControlPanel.svelte handles its date fields.

Reverts the no-op @prop rank attempt.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…aces

The fulltext-pod's compiled model version (baked into bundle/model.json via
common/scripts/version.txt at build time) lags whenever the workspaces have
been migrated to a newer patch but the pod was not rebuilt. In that state the
indexer rejects every incoming Tx with a `wrong version` warning, new issues
silently fail to land in Elasticsearch, and search returns empty results for
any document created after the migration.

Bumping `version.txt` aligns the compiled model with the workspaces. All
future builds (front, transactor, workspace, tool, fulltext) will emit
0.7.423, the indexer accepts the Tx stream again, and the deferred backlog
gets consumed automatically — no manual reindex needed.

This commit is the build-side companion to the schema migration in this
same PR. Without it the fulltext-pod cannot consume the migrated workspace's
Tx events.

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>
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>
- Remove "+ New issue" button from GanttView. PR2 stays read-only;
  issue creation lives in PR3 with edit/drag.
- Localize all visible Gantt strings via use:tooltip + Label, no more
  hardcoded English: Today, Jump to start/end, Previous/Next period,
  Jump to date, Issue, Title, Milestone, Start/Target/Due, Expand,
  Collapse, Scroll left/right to bar.
- Add 9 new IntlStrings (GanttToday, GanttJumpToStart, GanttJumpToEnd,
  GanttJumpToDate, GanttPreviousPeriod, GanttNextPeriod,
  GanttScrollLeftToBar, GanttScrollRightToBar, GanttExpand,
  GanttCollapse) on top of the existing GanttShowIssueCode and
  GanttShowTitle.
- Restore all 13 locale files to their PR1 baseline formatting and
  add only the additive Gantt entries (~17 new lines per locale,
  English fallback). ko.json, pt-br.json, tr.json now ship the same
  Gantt keys as the rest.

Tests: tracker-resources svelte-check 0 errors; tracker-assets locale
test pass.

Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
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>
…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>
@huly-github-staging
Copy link
Copy Markdown

Connected to Huly®: UBERF-16444

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

Successfully merging this pull request may close these issues.

1 participant