Merge branch 'develop'
This commit is contained in:
commit
3b0b5f20b8
@ -1174,6 +1174,20 @@ html[data-bs-theme="dark"] .tasks-board-group-header-sticky {
|
||||
собственную область прокрутки. contain не передаёт скролл всей странице,
|
||||
когда пользователь дошёл до начала или конца конкретной ленты. */
|
||||
@media (max-width: 79.999rem) {
|
||||
.tasks-task-card,
|
||||
.tasks-board-column-header,
|
||||
.tasks-board-subcolumn-header {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tasks-task-card img,
|
||||
.tasks-board-column-header img,
|
||||
.tasks-board-subcolumn-header img {
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
|
||||
.tasks-board-column-header,
|
||||
.tasks-board-subcolumn-header {
|
||||
box-shadow: none !important;
|
||||
|
||||
@ -1738,6 +1738,7 @@ type BoardTaskCellProps = TaskColumnCardsProps & {
|
||||
linkedColumnHighlight: boolean;
|
||||
synchronizedColumnDrag: boolean;
|
||||
fillAvailableHeight: boolean;
|
||||
embeddedInMobileLane?: boolean;
|
||||
};
|
||||
|
||||
function BoardTaskCell({
|
||||
@ -1746,6 +1747,7 @@ function BoardTaskCell({
|
||||
linkedColumnHighlight,
|
||||
synchronizedColumnDrag,
|
||||
fillAvailableHeight,
|
||||
embeddedInMobileLane = false,
|
||||
...cardsProps
|
||||
}: BoardTaskCellProps) {
|
||||
const { setNodeRef, isOver } = useDroppable({
|
||||
@ -1764,12 +1766,14 @@ function BoardTaskCell({
|
||||
return (
|
||||
<section
|
||||
ref={setNodeRef}
|
||||
className={`tasks-mobile-lane-viewport flex w-full min-w-0 flex-col transition ${
|
||||
rowId ? "min-h-0" : "min-h-[28rem]"
|
||||
className={`flex w-full min-w-0 flex-col transition ${
|
||||
embeddedInMobileLane
|
||||
? ""
|
||||
: `tasks-mobile-lane-viewport ${rowId ? "min-h-0" : "min-h-[28rem]"}`
|
||||
} xl:w-full ${fillAvailableHeight ? "xl:min-h-full" : ""}`}
|
||||
>
|
||||
<div
|
||||
className={`tasks-mobile-lane-scroll tasks-column-scroll min-h-0 flex-1 space-y-2 overflow-y-auto rounded-xl border bg-[var(--surface-primary)] p-3 transition xl:flex-none xl:overflow-visible ${columnBorderClass}`}
|
||||
className={`${embeddedInMobileLane ? "" : "tasks-mobile-lane-scroll overflow-y-auto xl:flex-none"} tasks-column-scroll min-h-0 flex-1 space-y-2 rounded-xl border bg-[var(--surface-primary)] p-3 transition xl:overflow-visible ${columnBorderClass}`}
|
||||
>
|
||||
<TaskColumnCards {...cardsProps} />
|
||||
</div>
|
||||
@ -2128,10 +2132,10 @@ function AddColumnCard({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
style={{ height: cardHeight }}
|
||||
className="group flex min-w-[18rem] self-start flex-col items-center justify-center rounded-xl border border-dashed border-[var(--border-subtle)] bg-[var(--surface-muted)] p-4 text-center transition hover:border-[var(--accent-primary)] hover:bg-[var(--surface-elevated)] xl:sticky xl:top-0"
|
||||
style={{ "--tasks-add-column-height": `${cardHeight}px` } as CSSProperties}
|
||||
className="group flex h-16 min-h-16 min-w-[18rem] self-start items-center justify-center gap-2 rounded-xl border border-dashed border-[var(--border-subtle)] bg-[var(--surface-muted)] px-4 text-center transition hover:border-[var(--accent-primary)] hover:bg-[var(--surface-elevated)] xl:sticky xl:top-0 xl:h-[var(--tasks-add-column-height)] xl:flex-col xl:gap-0 xl:p-4"
|
||||
>
|
||||
<span className="app-selected mb-3 flex h-10 w-10 items-center justify-center rounded-xl transition group-hover:scale-105">
|
||||
<span className="app-selected flex h-9 w-9 items-center justify-center rounded-xl transition group-hover:scale-105 xl:mb-3 xl:h-10 xl:w-10">
|
||||
<Plus size={18} />
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-[var(--foreground)]">
|
||||
@ -3700,6 +3704,7 @@ function TasksPageContent() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [search, setSearch] = useState("");
|
||||
const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
|
||||
const [mobileLaneLayout, setMobileLaneLayout] = useState(false);
|
||||
const [onlyMine, setOnlyMine] = useState(false);
|
||||
const [desktopWideMode, setDesktopWideMode] = useState(false);
|
||||
const [selectedColumnTarget, setSelectedColumnTarget] = useState<number | "all">("all");
|
||||
@ -3897,6 +3902,14 @@ function TasksPageContent() {
|
||||
);
|
||||
}, [user?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia("(max-width: 79.999rem)");
|
||||
const updateMobileLaneLayout = () => setMobileLaneLayout(mediaQuery.matches);
|
||||
updateMobileLaneLayout();
|
||||
mediaQuery.addEventListener("change", updateMobileLaneLayout);
|
||||
return () => mediaQuery.removeEventListener("change", updateMobileLaneLayout);
|
||||
}, []);
|
||||
|
||||
const changeDesktopWideMode = useCallback((enabled: boolean) => {
|
||||
setDesktopWideMode(enabled);
|
||||
if (user?.id) {
|
||||
@ -7738,9 +7751,10 @@ function TasksPageContent() {
|
||||
const renderTaskCell = (
|
||||
cellColumn: TaskColumn,
|
||||
rowId: number | null,
|
||||
embeddedInMobileLane = false,
|
||||
) => (
|
||||
<BoardTaskCell
|
||||
key={`cell-${cellColumn.id}:${rowId ?? "base"}`}
|
||||
key={`cell-${cellColumn.id}:${rowId ?? "base"}:${embeddedInMobileLane ? "mobile" : "desktop"}`}
|
||||
column={cellColumn}
|
||||
rowId={rowId}
|
||||
tasks={tasksByPlacement.get(taskPlacementKey(cellColumn.id, rowId)) || []}
|
||||
@ -7757,6 +7771,7 @@ function TasksPageContent() {
|
||||
linkedColumnHighlight={activeColumnDragId === cellColumn.id}
|
||||
synchronizedColumnDrag={activeColumnDragId !== null}
|
||||
fillAvailableHeight={desktopWideMode && rowId === null}
|
||||
embeddedInMobileLane={embeddedInMobileLane}
|
||||
quickTaskComposer={renderQuickTaskComposer(cellColumn, rowId)}
|
||||
/>
|
||||
);
|
||||
@ -7826,31 +7841,85 @@ function TasksPageContent() {
|
||||
subcolumnHeaders={subcolumnHeaders}
|
||||
subcolumnGridTemplate={gridTemplateColumns}
|
||||
>
|
||||
{baseColumns}
|
||||
{columnRows.length > 0 ? (
|
||||
<div className="flex min-w-0 flex-col gap-2">
|
||||
{columnRows.map((row, rowIndex) => {
|
||||
const rowTasksCount = filteredTasks.filter((task) => task.row === row.id).length;
|
||||
{mobileLaneLayout ? (
|
||||
<div
|
||||
className="grid w-max min-w-0 gap-2"
|
||||
style={{ gridTemplateColumns }}
|
||||
>
|
||||
{previewCellColumns.map((cellColumn) => {
|
||||
const baseCellTasksCount = tasksByPlacement.get(
|
||||
taskPlacementKey(cellColumn.id, null),
|
||||
)?.length || 0;
|
||||
return (
|
||||
<BoardColumnRowView
|
||||
key={row.id}
|
||||
row={{ ...row, tasks_count: rowTasksCount }}
|
||||
gridTemplateColumns={gridTemplateColumns}
|
||||
onCreateTask={() => {
|
||||
if (cellColumns[0]) startQuickTask(cellColumns[0].id, row.id);
|
||||
}}
|
||||
onEdit={() => openEditColumnRow(row)}
|
||||
onDelete={() => void deleteColumnRow(row)}
|
||||
expanded={!boardViewState.collapsed_row_ids.includes(row.id)}
|
||||
onToggle={() => toggleBoardViewState("row", row.id)}
|
||||
hasFollowingSection={rowIndex < columnRows.length - 1}
|
||||
<section
|
||||
key={`mobile-lane-${cellColumn.id}`}
|
||||
className="tasks-mobile-lane-viewport flex w-full min-w-0 flex-col"
|
||||
>
|
||||
{previewCellColumns.map((item) => renderTaskCell(item, row.id))}
|
||||
</BoardColumnRowView>
|
||||
<div className="tasks-mobile-lane-scroll tasks-column-scroll min-h-0 flex-1 space-y-2 overflow-y-auto">
|
||||
<BoardBaseColumnsView
|
||||
collapsible={columnRows.length > 0}
|
||||
tasksCount={baseCellTasksCount}
|
||||
gridTemplateColumns={TASK_SUBCOLUMN_WIDTH}
|
||||
expanded={!boardViewState.collapsed_base_column_ids.includes(column.id)}
|
||||
onToggle={() => toggleBoardViewState("base", column.id)}
|
||||
hasFollowingSection={columnRows.length > 0}
|
||||
>
|
||||
{renderTaskCell(cellColumn, null, true)}
|
||||
</BoardBaseColumnsView>
|
||||
{columnRows.map((row, rowIndex) => {
|
||||
const rowTasksCount = tasksByPlacement.get(
|
||||
taskPlacementKey(cellColumn.id, row.id),
|
||||
)?.length || 0;
|
||||
return (
|
||||
<BoardColumnRowView
|
||||
key={`${cellColumn.id}:${row.id}`}
|
||||
row={{ ...row, tasks_count: rowTasksCount }}
|
||||
gridTemplateColumns={TASK_SUBCOLUMN_WIDTH}
|
||||
onCreateTask={() => startQuickTask(cellColumn.id, row.id)}
|
||||
onEdit={() => openEditColumnRow(row)}
|
||||
onDelete={() => void deleteColumnRow(row)}
|
||||
expanded={!boardViewState.collapsed_row_ids.includes(row.id)}
|
||||
onToggle={() => toggleBoardViewState("row", row.id)}
|
||||
hasFollowingSection={rowIndex < columnRows.length - 1}
|
||||
>
|
||||
{renderTaskCell(cellColumn, row.id, true)}
|
||||
</BoardColumnRowView>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
) : (
|
||||
<>
|
||||
{baseColumns}
|
||||
{columnRows.length > 0 ? (
|
||||
<div className="flex min-w-0 flex-col gap-2">
|
||||
{columnRows.map((row, rowIndex) => {
|
||||
const rowTasksCount = filteredTasks.filter((task) => task.row === row.id).length;
|
||||
return (
|
||||
<BoardColumnRowView
|
||||
key={row.id}
|
||||
row={{ ...row, tasks_count: rowTasksCount }}
|
||||
gridTemplateColumns={gridTemplateColumns}
|
||||
onCreateTask={() => {
|
||||
if (cellColumns[0]) startQuickTask(cellColumns[0].id, row.id);
|
||||
}}
|
||||
onEdit={() => openEditColumnRow(row)}
|
||||
onDelete={() => void deleteColumnRow(row)}
|
||||
expanded={!boardViewState.collapsed_row_ids.includes(row.id)}
|
||||
onToggle={() => toggleBoardViewState("row", row.id)}
|
||||
hasFollowingSection={rowIndex < columnRows.length - 1}
|
||||
>
|
||||
{previewCellColumns.map((item) => renderTaskCell(item, row.id))}
|
||||
</BoardColumnRowView>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</BoardColumnGroup>
|
||||
);
|
||||
})}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user