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
+7
-1
@@ -6,6 +6,11 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.17.1] - 2026-10-31
|
||||
|
||||
### Changed
|
||||
- Kanban maximieren: Nur noch das Board wird breiter, Listen-Reiter und „Neue Aufgabe anlegen“ behalten ihre Breite. Das Board wächst weich animiert, jede Spalte wird höchstens 380 px breit und das Board bleibt mittig (bei wenigen Spalten also schmaler als das Fenster).
|
||||
|
||||
## [0.17.0] - 2026-10-31
|
||||
|
||||
### Added
|
||||
@@ -395,7 +400,8 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
|
||||
- Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken.
|
||||
- Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung.
|
||||
|
||||
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.17.0...dev
|
||||
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.17.1...dev
|
||||
[0.17.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.17.0...v0.17.1
|
||||
[0.17.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.16.1...v0.17.0
|
||||
[0.16.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.16.0...v0.16.1
|
||||
[0.16.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.15.1...v0.16.0
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[](#)
|
||||
[](#-schnellstart)
|
||||
[](#-deine-daten-gehören-dir)
|
||||
[](CHANGELOG.md)
|
||||
[](CHANGELOG.md)
|
||||
|
||||
**MindSift** ist eine selbst gehostete Schaltstelle für spontane Gedanken, Aufgaben mit Kanban-Board, hierarchische Outliner-Notizen, die mehrere Personen gleichzeitig bearbeiten können, und gemeinsame Listen. Alles läuft als ein einzelner Container mit eingebetteter SQLite-Datenbank, auf deinem eigenen Server.
|
||||
|
||||
|
||||
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