@tuturuuu/ui 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/biome.json +1 -1
  3. package/package.json +74 -71
  4. package/src/components/ui/accordion.tsx +1 -1
  5. package/src/components/ui/breadcrumb.tsx +1 -1
  6. package/src/components/ui/calendar-app/calendar-page-shell.tsx +4 -0
  7. package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +239 -33
  8. package/src/components/ui/calendar-app/components/load-smart-scheduling-tasks.tsx +143 -0
  9. package/src/components/ui/calendar-app/components/priority-view.tsx +10 -3
  10. package/src/components/ui/calendar-app/components/tasks-sidebar.tsx +4 -116
  11. package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +67 -2
  12. package/src/components/ui/calendar.tsx +1 -1
  13. package/src/components/ui/carousel.tsx +1 -1
  14. package/src/components/ui/chat/chat-agent-details-external-thread-panel.test.tsx +1 -1
  15. package/src/components/ui/chat/chat-agent-details-external-thread-panel.tsx +1 -1
  16. package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +1 -1
  17. package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +1 -1
  18. package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +1 -1
  19. package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +1 -1
  20. package/src/components/ui/chat/chat-agent-details-sidebar.tsx +2 -2
  21. package/src/components/ui/chat/chat-agent-details-utils.test.ts +1 -1
  22. package/src/components/ui/chat/chat-agent-details-utils.tsx +1 -1
  23. package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +2 -2
  24. package/src/components/ui/checkbox.tsx +1 -1
  25. package/src/components/ui/color-picker.tsx +1 -1
  26. package/src/components/ui/command.tsx +1 -1
  27. package/src/components/ui/context-menu.tsx +5 -1
  28. package/src/components/ui/custom/__tests__/settings-dialog-search.test.ts +78 -0
  29. package/src/components/ui/custom/__tests__/settings-dialog-shell-compile-graph.test.ts +76 -0
  30. package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +3 -0
  31. package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +46 -1
  32. package/src/components/ui/custom/combobox.test.tsx +195 -0
  33. package/src/components/ui/custom/combobox.tsx +273 -156
  34. package/src/components/ui/custom/education/modules/youtube/delete-link-button.tsx +5 -13
  35. package/src/components/ui/custom/facebook-mockup/facebook-mockup.tsx +7 -1
  36. package/src/components/ui/custom/facebook-mockup/form.tsx +1 -1
  37. package/src/components/ui/custom/facebook-mockup/image-upload-field.tsx +1 -1
  38. package/src/components/ui/custom/facebook-mockup/preview.tsx +1 -1
  39. package/src/components/ui/custom/nav-link.test.tsx +165 -0
  40. package/src/components/ui/custom/nav-link.tsx +69 -11
  41. package/src/components/ui/custom/navigation.tsx +1 -0
  42. package/src/components/ui/custom/settings/task-settings.tsx +104 -0
  43. package/src/components/ui/custom/settings-dialog-search-loader.d.ts +5 -0
  44. package/src/components/ui/custom/settings-dialog-search-loader.js +3 -0
  45. package/src/components/ui/custom/settings-dialog-search.ts +75 -0
  46. package/src/components/ui/custom/settings-dialog-shell.tsx +65 -28
  47. package/src/components/ui/custom/theme-toggle.tsx +1 -1
  48. package/src/components/ui/custom/workspace-select-helpers.ts +23 -0
  49. package/src/components/ui/custom/workspace-select.tsx +25 -19
  50. package/src/components/ui/dialog.test.tsx +52 -0
  51. package/src/components/ui/dialog.tsx +6 -2
  52. package/src/components/ui/dropdown-menu.tsx +5 -1
  53. package/src/components/ui/finance/debts/debt-loan-form.tsx +12 -5
  54. package/src/components/ui/finance/debts/debt-loan-summary.tsx +3 -2
  55. package/src/components/ui/finance/debts/debts-page.test.tsx +54 -5
  56. package/src/components/ui/finance/debts/debts-page.tsx +15 -2
  57. package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +3 -5
  58. package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +25 -5
  59. package/src/components/ui/finance/invoices/new-invoice-page.tsx +7 -2
  60. package/src/components/ui/finance/invoices/standard-invoice.tsx +4 -2
  61. package/src/components/ui/finance/invoices/subscription-invoice.tsx +4 -2
  62. package/src/components/ui/finance/invoices/utils.ts +3 -1
  63. package/src/components/ui/finance/transactions/form-content-dialog.tsx +3 -0
  64. package/src/components/ui/finance/transactions/form-types.ts +1 -0
  65. package/src/components/ui/finance/transactions/form.tsx +2 -0
  66. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +2 -0
  67. package/src/components/ui/finance/transactions/period-charts/category-breakdown-dialog.tsx +1 -1
  68. package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
  69. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
  70. package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
  71. package/src/components/ui/finance/wallets/form.test.tsx +51 -3
  72. package/src/components/ui/finance/wallets/form.tsx +15 -4
  73. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  74. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
  75. package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
  76. package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
  77. package/src/components/ui/input-otp.tsx +1 -1
  78. package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
  79. package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
  80. package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
  81. package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
  82. package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
  83. package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
  84. package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
  85. package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
  86. package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
  87. package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
  88. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
  89. package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
  90. package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
  91. package/src/components/ui/navigation-menu.tsx +1 -1
  92. package/src/components/ui/pagination.tsx +1 -1
  93. package/src/components/ui/radio-group.tsx +1 -1
  94. package/src/components/ui/select.tsx +5 -1
  95. package/src/components/ui/sheet.tsx +1 -1
  96. package/src/components/ui/sidebar.tsx +1 -1
  97. package/src/components/ui/storefront/cart-popover.tsx +61 -0
  98. package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
  99. package/src/components/ui/storefront/cart-summary.tsx +93 -154
  100. package/src/components/ui/storefront/checkout-overlay.tsx +4 -5
  101. package/src/components/ui/storefront/listing-card.tsx +1 -1
  102. package/src/components/ui/storefront/merch-sections.tsx +70 -0
  103. package/src/components/ui/storefront/product-detail.tsx +1 -1
  104. package/src/components/ui/storefront/storefront-surface.test.tsx +106 -11
  105. package/src/components/ui/storefront/storefront-surface.tsx +101 -166
  106. package/src/components/ui/storefront/types.ts +4 -0
  107. package/src/components/ui/storefront/utils.ts +6 -0
  108. package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
  109. package/src/components/ui/text-editor/background-color-extension.ts +62 -0
  110. package/src/components/ui/text-editor/color-controls.tsx +284 -0
  111. package/src/components/ui/text-editor/editor.tsx +69 -14
  112. package/src/components/ui/text-editor/extensions.ts +8 -2
  113. package/src/components/ui/text-editor/highlight-extension.ts +22 -0
  114. package/src/components/ui/text-editor/tool-bar.tsx +9 -16
  115. package/src/components/ui/toast.tsx +1 -1
  116. package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +286 -0
  117. package/src/components/ui/tu-do/boards/__tests__/task-board-form.test.tsx +12 -0
  118. package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
  119. package/src/components/ui/tu-do/boards/board-share-dialog.tsx +15 -226
  120. package/src/components/ui/tu-do/boards/board-share-settings-panel.tsx +351 -0
  121. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +121 -39
  122. package/src/components/ui/tu-do/boards/boardId/enhanced-task-list.tsx +7 -0
  123. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
  124. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
  125. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
  126. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-types.ts +3 -3
  127. package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
  128. package/src/components/ui/tu-do/boards/boardId/kanban/data/use-bulk-resources.ts +59 -5
  129. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
  130. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
  131. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/drag-preview.tsx +20 -1
  132. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +38 -9
  133. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
  134. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
  135. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +81 -30
  136. package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
  137. package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
  138. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
  139. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
  140. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
  141. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
  142. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
  143. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
  144. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
  145. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
  146. package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
  147. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +642 -5
  148. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +224 -15
  149. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +535 -53
  150. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +101 -33
  151. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +235 -113
  152. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +50 -5
  153. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +12 -2
  154. package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +10 -1
  155. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
  156. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +20 -0
  157. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +10 -0
  158. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +271 -36
  159. package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
  160. package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
  161. package/src/components/ui/tu-do/boards/boardId/task-list.tsx +22 -0
  162. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +9 -0
  163. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +9 -0
  164. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-toolbar.tsx +9 -0
  165. package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +35 -3
  166. package/src/components/ui/tu-do/boards/form.tsx +1 -1
  167. package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
  168. package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
  169. package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
  170. package/src/components/ui/tu-do/hooks/__tests__/useTaskLabelManagement.test.tsx +48 -0
  171. package/src/components/ui/tu-do/hooks/__tests__/useTaskProjectManagement.test.tsx +144 -0
  172. package/src/components/ui/tu-do/hooks/useTaskDialog.ts +7 -0
  173. package/src/components/ui/tu-do/hooks/useTaskLabelManagement.ts +115 -106
  174. package/src/components/ui/tu-do/hooks/useTaskProjectManagement.ts +115 -122
  175. package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
  176. package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
  177. package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
  178. package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
  179. package/src/components/ui/tu-do/progress/task-progress-import-panel.tsx +60 -0
  180. package/src/components/ui/tu-do/progress/task-progress-leaderboards-panel.tsx +156 -0
  181. package/src/components/ui/tu-do/progress/task-progress-page.tsx +348 -0
  182. package/src/components/ui/tu-do/progress/task-progress-panels.tsx +301 -0
  183. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +26 -0
  184. package/src/components/ui/tu-do/shared/__tests__/assignee-select.test.tsx +81 -10
  185. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +141 -1
  186. package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +377 -36
  187. package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +374 -0
  188. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +419 -5
  189. package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +38 -0
  190. package/src/components/ui/tu-do/shared/__tests__/task-cache-patches.test.ts +147 -0
  191. package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
  192. package/src/components/ui/tu-do/shared/__tests__/use-progressive-board-loader.test.tsx +3 -0
  193. package/src/components/ui/tu-do/shared/assignee-select.tsx +77 -26
  194. package/src/components/ui/tu-do/shared/board-client.tsx +15 -10
  195. package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
  196. package/src/components/ui/tu-do/shared/board-header.tsx +471 -975
  197. package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
  198. package/src/components/ui/tu-do/shared/board-switcher.tsx +244 -220
  199. package/src/components/ui/tu-do/shared/board-user-presence-avatars.tsx +18 -12
  200. package/src/components/ui/tu-do/shared/board-views.tsx +577 -85
  201. package/src/components/ui/tu-do/shared/list-view.tsx +246 -2
  202. package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
  203. package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
  204. package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
  205. package/src/components/ui/tu-do/shared/task-cache-patches.ts +394 -0
  206. package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +21 -1
  207. package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +5 -1
  208. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +25 -2
  209. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +7 -1
  210. package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
  211. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-data.ts +79 -10
  212. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +76 -77
  213. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.test.tsx +63 -0
  214. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.ts +78 -69
  215. package/src/components/ui/tu-do/shared/task-edit-dialog/personal-overrides-section.tsx +28 -8
  216. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/dependencies-section.tsx +14 -3
  217. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/parent-section.tsx +6 -1
  218. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/related-section.tsx +6 -1
  219. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/subtasks-section.tsx +6 -1
  220. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/types/task-relationships.types.ts +8 -0
  221. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
  222. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
  223. package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
  224. package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +8 -1
  225. package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.test.tsx +150 -0
  226. package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +61 -35
  227. package/src/components/ui/tu-do/shared/task-edit-dialog/task-relationships-properties.tsx +44 -2
  228. package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
  229. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +11 -1
  230. package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
  231. package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +11 -0
  232. package/src/components/ui/tu-do/shared/use-progressive-board-loader.ts +2 -0
  233. package/src/declarations.d.ts +1 -0
  234. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
  235. package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
  236. package/src/hooks/__tests__/useBoardPresence.test.tsx +191 -0
  237. package/src/hooks/__tests__/useBoardRealtime.test.tsx +24 -144
  238. package/src/hooks/use-calendar-sync.tsx +247 -243
  239. package/src/hooks/use-calendar.tsx +323 -138
  240. package/src/hooks/use-task-actions.ts +24 -0
  241. package/src/hooks/use-user-workspace-config.ts +75 -0
  242. package/src/hooks/use-workspace-currency.ts +8 -3
  243. package/src/hooks/useBoardPresence.ts +364 -0
  244. package/src/hooks/useBoardRealtimeEventHandler.ts +45 -90
  245. package/src/lib/workspace-actions.ts +2 -6
@@ -7,27 +7,39 @@ import {
7
7
  import type { Task } from '@tuturuuu/types/primitives/Task';
8
8
  import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
9
9
  import { getBoardRealtimeChannelName } from '@tuturuuu/ui/hooks/useBoardRealtime.types';
10
+ import { useLayoutEffect, useRef } from 'react';
10
11
  import type { ListStatusFilter } from '../../../../shared/board-header';
11
12
  import CursorOverlayMultiWrapper from '../../../../shared/cursor-overlay-multi-wrapper';
13
+ import type {
14
+ SpecialTaskListPin,
15
+ SpecialTaskListPinState,
16
+ } from '../../../../shared/special-task-list-pins';
12
17
  import { BoardColumn } from '../../board-column';
13
18
  import type { TaskFilters } from '../../task-filter';
14
19
  import { TaskListForm } from '../../task-list-form';
20
+ import { compareTasksByEffectiveSortKey } from '../dnd/task-sort-key';
15
21
  import type { DragPreviewPosition } from '../dnd/use-kanban-dnd';
16
22
  import { isKanbanColumnCollapsed } from '../kanban-column-collapse';
17
- import { MAX_SAFE_INTEGER_SORT } from '../kanban-constants';
18
23
  import { getKanbanColumnWidth } from './kanban-column-width';
19
24
  import {
25
+ type KanbanDeadlineCollapsedState,
20
26
  type KanbanDeadlineLabels,
21
27
  KanbanDeadlinePanels,
28
+ type KanbanDeadlineSection,
22
29
  } from './kanban-deadline-panels';
23
30
  import type { KanbanDeadlineSections } from './kanban-deadline-tasks';
24
31
 
32
+ const KANBAN_COLUMN_GAP = '0.75rem';
33
+ const COLLAPSED_SPECIAL_LIST_WIDTH = '3.5rem';
34
+
25
35
  interface KanbanColumnsProps {
26
36
  columns: TaskList[];
27
37
  tasks: Task[];
28
38
  boardId: string;
29
39
  workspaceId: string;
30
40
  isPersonalWorkspace: boolean;
41
+ canUseBoardAssignees?: boolean;
42
+ assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
31
43
  cursorsEnabled?: boolean;
32
44
  disableSort: boolean;
33
45
  selectedTasks: Set<string>;
@@ -55,6 +67,57 @@ interface KanbanColumnsProps {
55
67
  onTaskListCollapsedChange?: (listId: string, collapsed: boolean) => void;
56
68
  deadlineLabels?: KanbanDeadlineLabels;
57
69
  deadlineSections?: KanbanDeadlineSections;
70
+ deadlineSectionsLoading?: boolean;
71
+ deadlineSectionsCollapsed?: KanbanDeadlineCollapsedState;
72
+ deadlineNow?: number;
73
+ onDeadlineSectionCollapsedChange?: (
74
+ section: KanbanDeadlineSection,
75
+ collapsed: boolean
76
+ ) => void;
77
+ specialTaskListPins?: SpecialTaskListPinState;
78
+ onSpecialTaskListPinnedChange?: (
79
+ pin: SpecialTaskListPin,
80
+ pinned: boolean
81
+ ) => void;
82
+ readOnly?: boolean;
83
+ }
84
+
85
+ interface PinnedSpecialListLayout {
86
+ offsets: Record<string, string>;
87
+ totalWidth: string;
88
+ }
89
+
90
+ function toCalcExpression(parts: string[]) {
91
+ if (parts.length === 0) return '0px';
92
+ if (parts.length === 1) return parts[0] ?? '0px';
93
+ return `calc(${parts.join(' + ')})`;
94
+ }
95
+
96
+ function getSpecialListWidth(collapsed: boolean) {
97
+ return collapsed
98
+ ? COLLAPSED_SPECIAL_LIST_WIDTH
99
+ : 'var(--kanban-column-width)';
100
+ }
101
+
102
+ function getPinnedSpecialRailWidth(container: HTMLElement) {
103
+ const elements = Array.from(
104
+ container.querySelectorAll<HTMLElement>(
105
+ '[data-kanban-pinned-special="true"]'
106
+ )
107
+ );
108
+
109
+ if (elements.length === 0) return 0;
110
+
111
+ const rects = elements
112
+ .map((element) => element.getBoundingClientRect())
113
+ .filter((rect) => rect.width > 0);
114
+
115
+ if (rects.length === 0) return 0;
116
+
117
+ const left = Math.min(...rects.map((rect) => rect.left));
118
+ const right = Math.max(...rects.map((rect) => rect.right));
119
+
120
+ return Math.max(0, right - left);
58
121
  }
59
122
 
60
123
  export function KanbanColumns({
@@ -63,6 +126,8 @@ export function KanbanColumns({
63
126
  boardId,
64
127
  workspaceId,
65
128
  isPersonalWorkspace,
129
+ canUseBoardAssignees,
130
+ assigneeMemberSource,
66
131
  cursorsEnabled = true,
67
132
  disableSort,
68
133
  selectedTasks,
@@ -85,16 +150,122 @@ export function KanbanColumns({
85
150
  onTaskListCollapsedChange,
86
151
  deadlineLabels,
87
152
  deadlineSections,
153
+ deadlineSectionsLoading,
154
+ deadlineSectionsCollapsed,
155
+ deadlineNow,
156
+ onDeadlineSectionCollapsedChange,
157
+ specialTaskListPins,
158
+ onSpecialTaskListPinnedChange,
159
+ readOnly = false,
88
160
  }: KanbanColumnsProps) {
161
+ const initialScrollAnchoredBoardRef = useRef<string | null>(null);
89
162
  const realColumns = columns.filter((column) => !column.is_external_staging);
90
- const snapEdgePadding = columns.length > 0 ? '0.5rem' : '0px';
91
- const collapsedColumnCount = columns.filter(isKanbanColumnCollapsed).length;
163
+ const deadlineSectionOrder: KanbanDeadlineSection[] = ['overdue', 'upcoming'];
164
+ const deadlinePanelsEnabled =
165
+ !readOnly && Boolean(boardId && deadlineSections && deadlineLabels);
166
+ const reservedDeadlineSections = deadlinePanelsEnabled
167
+ ? deadlineSectionOrder
168
+ : [];
169
+ const snapEdgePadding =
170
+ columns.length > 0 || reservedDeadlineSections.length > 0
171
+ ? '0.5rem'
172
+ : '0px';
173
+ const collapsedColumnCount =
174
+ columns.filter(isKanbanColumnCollapsed).length +
175
+ reservedDeadlineSections.filter(
176
+ (section) => deadlineSectionsCollapsed?.[section] === true
177
+ ).length;
92
178
  const dynamicColumnWidth = getKanbanColumnWidth({
93
- columnCount: columns.length,
179
+ columnCount: columns.length + reservedDeadlineSections.length,
94
180
  collapsedColumnCount,
95
181
  snapEdgePadding,
96
182
  fillAvailableWidth: listStatusFilter === 'all',
97
183
  });
184
+ const pinnedSpecialListLayout = (() => {
185
+ const entries: { key: string; width: string }[] = [];
186
+
187
+ if (deadlinePanelsEnabled && specialTaskListPins?.overdue) {
188
+ entries.push({
189
+ key: 'deadline:overdue',
190
+ width: getSpecialListWidth(deadlineSectionsCollapsed?.overdue === true),
191
+ });
192
+ }
193
+
194
+ if (deadlinePanelsEnabled && specialTaskListPins?.upcoming) {
195
+ entries.push({
196
+ key: 'deadline:upcoming',
197
+ width: getSpecialListWidth(
198
+ deadlineSectionsCollapsed?.upcoming === true
199
+ ),
200
+ });
201
+ }
202
+
203
+ for (const column of columns) {
204
+ const pinned =
205
+ column.is_external_staging === true
206
+ ? specialTaskListPins?.external_tasks === true
207
+ : column.status === 'closed'
208
+ ? specialTaskListPins?.closed_tasks === true
209
+ : false;
210
+
211
+ if (!pinned) continue;
212
+
213
+ entries.push({
214
+ key: `column:${column.id}`,
215
+ width: getSpecialListWidth(isKanbanColumnCollapsed(column)),
216
+ });
217
+ }
218
+
219
+ const offsets: Record<string, string> = {};
220
+ const parts: string[] = [];
221
+
222
+ entries.forEach((entry, index) => {
223
+ if (index > 0) parts.push(KANBAN_COLUMN_GAP);
224
+
225
+ offsets[entry.key] = toCalcExpression(parts);
226
+ parts.push(entry.width);
227
+ });
228
+
229
+ return {
230
+ offsets,
231
+ totalWidth: toCalcExpression(parts),
232
+ } satisfies PinnedSpecialListLayout;
233
+ })();
234
+ const hasLeftSpecialColumns =
235
+ reservedDeadlineSections.length > 0 ||
236
+ columns.some((column) => column.is_external_staging);
237
+
238
+ useLayoutEffect(() => {
239
+ if (!hasLeftSpecialColumns) return;
240
+ if (initialScrollAnchoredBoardRef.current === boardId) return;
241
+
242
+ const container = boardRef.current;
243
+ if (!container) return;
244
+
245
+ const target = container.querySelector<HTMLElement>(
246
+ '[data-kanban-real-column="true"]'
247
+ );
248
+ if (!target) return;
249
+
250
+ initialScrollAnchoredBoardRef.current = boardId;
251
+
252
+ const anchor = () => {
253
+ const pinnedRailWidth = getPinnedSpecialRailWidth(container);
254
+ container.scrollLeft = Math.max(
255
+ 0,
256
+ target.offsetLeft - pinnedRailWidth - 8
257
+ );
258
+ };
259
+
260
+ anchor();
261
+
262
+ if (typeof window.requestAnimationFrame !== 'function') {
263
+ return;
264
+ }
265
+
266
+ const frame = window.requestAnimationFrame(anchor);
267
+ return () => window.cancelAnimationFrame?.(frame);
268
+ }, [boardId, boardRef, hasLeftSpecialColumns]);
98
269
 
99
270
  return (
100
271
  <div
@@ -105,7 +276,10 @@ export function KanbanColumns({
105
276
  '--kanban-snap-left-padding': snapEdgePadding,
106
277
  '--kanban-snap-right-padding': snapEdgePadding,
107
278
  '--kanban-column-width': dynamicColumnWidth,
108
- scrollPaddingLeft: 'var(--kanban-snap-left-padding)',
279
+ scrollPaddingLeft:
280
+ pinnedSpecialListLayout.totalWidth === '0px'
281
+ ? 'var(--kanban-snap-left-padding)'
282
+ : `calc(var(--kanban-snap-left-padding) + ${pinnedSpecialListLayout.totalWidth})`,
109
283
  scrollPaddingRight: 'var(--kanban-snap-right-padding)',
110
284
  } as React.CSSProperties
111
285
  }
@@ -121,19 +295,36 @@ export function KanbanColumns({
121
295
  paddingRight: 'var(--kanban-snap-right-padding)',
122
296
  }}
123
297
  >
124
- {deadlineSections && deadlineLabels && (
298
+ {deadlinePanelsEnabled && deadlineSections && deadlineLabels && (
125
299
  <KanbanDeadlinePanels
126
300
  availableLists={realColumns}
127
301
  boardId={boardId}
128
302
  bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
129
303
  isMultiSelectMode={isMultiSelectMode}
130
304
  isPersonalWorkspace={isPersonalWorkspace}
305
+ canUseBoardAssignees={canUseBoardAssignees}
306
+ assigneeMemberSource={assigneeMemberSource}
131
307
  labels={deadlineLabels}
132
308
  onClearSelection={onClearSelection}
309
+ onSectionCollapsedChange={onDeadlineSectionCollapsedChange}
133
310
  onTaskSelect={onTaskSelect}
134
311
  onUpdate={onUpdate}
135
312
  optimisticUpdateInProgress={optimisticUpdateInProgress}
136
313
  sections={deadlineSections}
314
+ loading={deadlineSectionsLoading}
315
+ collapsedSections={deadlineSectionsCollapsed}
316
+ deadlineNow={deadlineNow}
317
+ pinnedSections={{
318
+ overdue: specialTaskListPins?.overdue,
319
+ upcoming: specialTaskListPins?.upcoming,
320
+ }}
321
+ stickyOffsets={{
322
+ overdue: pinnedSpecialListLayout.offsets['deadline:overdue'],
323
+ upcoming: pinnedSpecialListLayout.offsets['deadline:upcoming'],
324
+ }}
325
+ onSectionPinnedChange={(section, pinned) =>
326
+ onSpecialTaskListPinnedChange?.(section, pinned)
327
+ }
137
328
  selectedTasks={selectedTasks}
138
329
  taskLists={columns}
139
330
  workspaceId={workspaceId}
@@ -170,14 +361,7 @@ export function KanbanColumns({
170
361
 
171
362
  // For all other lists, only sort by sort_key if parent hasn't already sorted
172
363
  if (!disableSort) {
173
- const sortA = a.sort_key ?? MAX_SAFE_INTEGER_SORT;
174
- const sortB = b.sort_key ?? MAX_SAFE_INTEGER_SORT;
175
- if (sortA !== sortB) return sortA - sortB;
176
- if (!a.created_at || !b.created_at) return 0;
177
- return (
178
- new Date(a.created_at).getTime() -
179
- new Date(b.created_at).getTime()
180
- );
364
+ return compareTasksByEffectiveSortKey(a, b);
181
365
  }
182
366
 
183
367
  return 0;
@@ -191,6 +375,8 @@ export function KanbanColumns({
191
375
  tasks={listTasks}
192
376
  availableLists={realColumns}
193
377
  isPersonalWorkspace={isPersonalWorkspace}
378
+ canUseBoardAssignees={canUseBoardAssignees}
379
+ assigneeMemberSource={assigneeMemberSource}
194
380
  onUpdate={onUpdate}
195
381
  onAddTask={() =>
196
382
  boardId && createTask(boardId, list.id, realColumns, filters)
@@ -210,10 +396,33 @@ export function KanbanColumns({
210
396
  wsId={workspaceId}
211
397
  onExternalTasksCollapsedChange={onExternalTasksCollapsedChange}
212
398
  onTaskListCollapsedChange={onTaskListCollapsedChange}
399
+ specialStickyOffset={
400
+ pinnedSpecialListLayout.offsets[`column:${list.id}`]
401
+ }
402
+ specialPinned={
403
+ list.is_external_staging
404
+ ? specialTaskListPins?.external_tasks === true
405
+ : list.status === 'closed'
406
+ ? specialTaskListPins?.closed_tasks === true
407
+ : false
408
+ }
409
+ onSpecialPinnedChange={(pinned) => {
410
+ if (list.is_external_staging) {
411
+ onSpecialTaskListPinnedChange?.('external_tasks', pinned);
412
+ return;
413
+ }
414
+
415
+ if (list.status === 'closed') {
416
+ onSpecialTaskListPinnedChange?.('closed_tasks', pinned);
417
+ }
418
+ }}
419
+ readOnly={readOnly}
213
420
  />
214
421
  );
215
422
  })}
216
- <TaskListForm boardId={boardId ?? ''} onListCreated={onUpdate} />
423
+ {!readOnly && (
424
+ <TaskListForm boardId={boardId ?? ''} onListCreated={onUpdate} />
425
+ )}
217
426
  </div>
218
427
  </SortableContext>
219
428