feat(kanban): wide board animates, keeps header width and caps column width
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
25bb787a8e
commit
945be2c1ad
6 files changed
+30
-19
No files matched your search
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mindsift-frontend",
|
||||
"version": "0.17.0",
|
||||
"version": "0.17.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mindsift-frontend",
|
||||
"version": "0.17.0",
|
||||
"version": "0.17.1",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "5.3.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mindsift-frontend",
|
||||
"private": true,
|
||||
"version": "0.17.0",
|
||||
"version": "0.17.1",
|
||||
"license": "Unlicense",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -164,8 +164,6 @@ export const App: React.FC = () => {
|
||||
const [deck, setDeck] = useState<Thought[]>([]);
|
||||
const [lists, setLists] = useState<SharedList[]>([]);
|
||||
const [activeTab, setActiveTab] = useState<ActiveTab>('triage');
|
||||
// Kanban asked for the full window width (reported by the tasks view)
|
||||
const [tasksWide, setTasksWide] = useState(false);
|
||||
const [previousTab, setPreviousTab] = useState<ActiveTab>('triage');
|
||||
const [selectedDocId, setSelectedDocId] = useState<string | null>(null);
|
||||
const [selectedBlockId, setSelectedBlockId] = useState<string | null>(null);
|
||||
@@ -1170,9 +1168,7 @@ export const App: React.FC = () => {
|
||||
{/* Main Container */}
|
||||
{/* The workspace fills the window exactly and scrolls only inside the note, never the page */}
|
||||
<main
|
||||
className={`flex-1 w-full mx-auto px-3 sm:px-4 pt-3 pb-24 ${
|
||||
activeTab === 'tasks' && tasksWide ? 'lg:max-w-none lg:px-6' : 'max-w-6xl'
|
||||
} ${
|
||||
className={`flex-1 max-w-6xl w-full mx-auto px-3 sm:px-4 pt-3 pb-24 ${
|
||||
activeTab === 'workspace' ? 'min-h-0 flex flex-col sm:pb-0' : 'sm:pb-6'
|
||||
}`}
|
||||
>
|
||||
@@ -1196,7 +1192,6 @@ export const App: React.FC = () => {
|
||||
refreshTrigger={refreshRevision}
|
||||
initialSelectedTaskId={selectedTaskId}
|
||||
openTasksCount={stats.tasksOpenCount}
|
||||
onWideChange={setTasksWide}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -62,10 +62,25 @@ interface TasksViewProps {
|
||||
refreshTrigger?: number;
|
||||
initialSelectedTaskId?: string | null;
|
||||
openTasksCount?: number; // all open tasks incl. shared lists (same as the nav bar counter)
|
||||
onWideChange?: (wide: boolean) => void; // Kanban stretched to the full window width
|
||||
}
|
||||
|
||||
const KANBAN_WIDE_KEY = 'mindsift:kanban_wide';
|
||||
// Wide Kanban: a column grows to at most this width, the board stays centred
|
||||
const KANBAN_COL_MAX = 380;
|
||||
const KANBAN_GAP = 12; // gap-3
|
||||
|
||||
// Only the board leaves the page column (header and "new task" keep their width): its width follows
|
||||
// the columns (at most KANBAN_COL_MAX each, at most the window) and a margin keeps it centred – a
|
||||
// negative one when it is wider than the page column. Width and margin are animated.
|
||||
const kanbanBoardStyle = (wide: boolean, columns: number): React.CSSProperties => {
|
||||
if (!wide) return { '--kanban-w': '100%', '--kanban-ml': '0px' } as React.CSSProperties;
|
||||
const w = `min(100vw - 4rem, ${columns * KANBAN_COL_MAX + (columns - 1) * KANBAN_GAP}px)`;
|
||||
return {
|
||||
'--kanban-cols': columns,
|
||||
'--kanban-w': w,
|
||||
'--kanban-ml': `calc((100% - ${w}) / 2)`,
|
||||
} as React.CSSProperties;
|
||||
};
|
||||
|
||||
type TasksSession = { listId: string; viewMode: 'list' | 'kanban' };
|
||||
|
||||
@@ -76,7 +91,6 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
refreshTrigger = 0,
|
||||
initialSelectedTaskId,
|
||||
openTasksCount,
|
||||
onWideChange,
|
||||
}) => {
|
||||
// Open list and view survive switching to other tabs (like the workspace's open note)
|
||||
const [viewMode, setViewMode] = useState<'list' | 'kanban'>(() =>
|
||||
@@ -100,10 +114,6 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
}
|
||||
};
|
||||
const isKanbanWide = viewMode === 'kanban' && kanbanWide;
|
||||
useEffect(() => {
|
||||
onWideChange?.(isKanbanWide);
|
||||
}, [isKanbanWide, onWideChange]);
|
||||
useEffect(() => () => onWideChange?.(false), [onWideChange]);
|
||||
const [filter, setFilter] = useState<'open' | 'done' | 'discarded'>('open');
|
||||
const [tasks, setTasks] = useState<Thought[]>([]);
|
||||
const [taskLists, setTaskLists] = useState<TaskList[]>([]);
|
||||
@@ -2214,10 +2224,10 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
/* DYNAMIC KANBAN BOARD */
|
||||
<div className="space-y-2">
|
||||
<div
|
||||
className={`grid grid-cols-1 md:grid-cols-2 gap-3 pt-1 ${
|
||||
className={`grid grid-cols-1 md:grid-cols-2 gap-3 pt-1 lg:w-[var(--kanban-w)] lg:ml-[var(--kanban-ml)] transition-[width,margin] duration-300 ease-out ${
|
||||
isKanbanWide ? 'lg:grid-cols-[repeat(var(--kanban-cols),minmax(0,1fr))]' : 'lg:grid-cols-4'
|
||||
}`}
|
||||
style={isKanbanWide ? ({ '--kanban-cols': activeColumns.length } as React.CSSProperties) : undefined}
|
||||
style={kanbanBoardStyle(isKanbanWide, activeColumns.length)}
|
||||
>
|
||||
{activeColumns.map((col, colIdx) => {
|
||||
const colTasks = kanbanTasks.filter((t) => {
|
||||
|
||||
Reference in new issue
Block a user