mirror of
https://github.com/docmost/docmost.git
synced 2026-06-10 01:52:43 +08:00
feat(bases): hide and show kanban columns from header menu
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { Badge } from "@mantine/core";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
import {
|
||||
IBase,
|
||||
IBaseRow,
|
||||
@@ -168,23 +170,91 @@ export function BaseKanban({
|
||||
[base.id, columns, effectiveView, updateViewMutation],
|
||||
);
|
||||
|
||||
const handleHideColumn = useCallback(
|
||||
(columnKey: string) => {
|
||||
if (!effectiveView) return;
|
||||
const current = effectiveView.config?.hiddenChoiceIds ?? [];
|
||||
if (current.includes(columnKey)) return;
|
||||
updateViewMutation.mutate({
|
||||
viewId: effectiveView.id,
|
||||
pageId: base.id,
|
||||
config: {
|
||||
...effectiveView.config,
|
||||
hiddenChoiceIds: [...current, columnKey],
|
||||
},
|
||||
});
|
||||
},
|
||||
[base.id, effectiveView, updateViewMutation],
|
||||
);
|
||||
|
||||
const handleShowColumn = useCallback(
|
||||
(columnKey: string) => {
|
||||
if (!effectiveView) return;
|
||||
const next = (effectiveView.config?.hiddenChoiceIds ?? []).filter(
|
||||
(id) => id !== columnKey,
|
||||
);
|
||||
updateViewMutation.mutate({
|
||||
viewId: effectiveView.id,
|
||||
pageId: base.id,
|
||||
config: { ...effectiveView.config, hiddenChoiceIds: next },
|
||||
});
|
||||
},
|
||||
[base.id, effectiveView, updateViewMutation],
|
||||
);
|
||||
|
||||
const hiddenIds = effectiveView?.config?.hiddenChoiceIds ?? [];
|
||||
const hiddenChoices = useMemo(() => {
|
||||
if (!isGroupable || hiddenIds.length === 0) return [];
|
||||
const opts = (property!.typeOptions as { choices?: Array<{ id: string; name: string; color: string }> } | undefined) ?? {};
|
||||
const choices = opts.choices ?? [];
|
||||
const byId = new Map(choices.map((c) => [c.id, c]));
|
||||
return hiddenIds
|
||||
.map((id) =>
|
||||
id === NO_VALUE_CHOICE_ID
|
||||
? { id, name: "No value", color: null as string | null }
|
||||
: byId.get(id)
|
||||
? { id, name: byId.get(id)!.name, color: byId.get(id)!.color as string | null }
|
||||
: null,
|
||||
)
|
||||
.filter((c): c is { id: string; name: string; color: string | null } => c !== null);
|
||||
}, [hiddenIds, isGroupable, property]);
|
||||
|
||||
if (!isGroupable) {
|
||||
return <KanbanEmptyState base={base} onPick={handlePickProperty} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.board}>
|
||||
{columns.map((column) => (
|
||||
<KanbanColumn
|
||||
key={column.key}
|
||||
column={column}
|
||||
primaryProperty={primaryProperty}
|
||||
onCardClick={onCardClick}
|
||||
onAddCard={handleAddCard}
|
||||
onCardDrop={handleCardDrop}
|
||||
onColumnReorder={handleColumnReorder}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<>
|
||||
{hiddenChoices.length > 0 && (
|
||||
<div style={{ padding: "6px 12px", display: "flex", gap: 6, flexWrap: "wrap" }}>
|
||||
{hiddenChoices.map((c) => (
|
||||
<Badge
|
||||
key={c.id}
|
||||
color={c.color ?? "gray"}
|
||||
variant="outline"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => handleShowColumn(c.id)}
|
||||
rightSection={<IconPlus size={12} />}
|
||||
>
|
||||
{c.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.board}>
|
||||
{columns.map((column) => (
|
||||
<KanbanColumn
|
||||
key={column.key}
|
||||
column={column}
|
||||
primaryProperty={primaryProperty}
|
||||
onCardClick={onCardClick}
|
||||
onAddCard={handleAddCard}
|
||||
onCardDrop={handleCardDrop}
|
||||
onColumnReorder={handleColumnReorder}
|
||||
onHide={handleHideColumn}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Badge, Text } from "@mantine/core";
|
||||
import { ActionIcon, Badge, Menu, Text } from "@mantine/core";
|
||||
import { IconDots, IconEyeOff } from "@tabler/icons-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useKanbanColumnReorder,
|
||||
type ColumnReorderPayload,
|
||||
@@ -12,6 +14,7 @@ type KanbanColumnHeaderProps = {
|
||||
color: string | null;
|
||||
count: number;
|
||||
onReorderDrop: (payload: ColumnReorderPayload) => void;
|
||||
onHide: (columnKey: string) => void;
|
||||
};
|
||||
|
||||
export function KanbanColumnHeader({
|
||||
@@ -20,7 +23,9 @@ export function KanbanColumnHeader({
|
||||
color,
|
||||
count,
|
||||
onReorderDrop,
|
||||
onHide,
|
||||
}: KanbanColumnHeaderProps) {
|
||||
const { t } = useTranslation();
|
||||
const { ref, isDragging, closestEdge } = useKanbanColumnReorder({
|
||||
columnKey,
|
||||
onDrop: onReorderDrop,
|
||||
@@ -43,6 +48,21 @@ export function KanbanColumnHeader({
|
||||
)}
|
||||
<span className={classes.columnCount}>{count}</span>
|
||||
</div>
|
||||
<Menu shadow="md" width={160} position="bottom-end">
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="subtle" size="sm" color="gray">
|
||||
<IconDots size={14} />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
leftSection={<IconEyeOff size={14} />}
|
||||
onClick={() => onHide(columnKey)}
|
||||
>
|
||||
{t("Hide group")}
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
{closestEdge && <BaseDropEdgeIndicator edge={closestEdge} />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ type KanbanColumnProps = {
|
||||
onAddCard: (columnKey: string) => void;
|
||||
onCardDrop: (payload: CardDropPayload) => void;
|
||||
onColumnReorder: (payload: ColumnReorderPayload) => void;
|
||||
onHide: (columnKey: string) => void;
|
||||
};
|
||||
|
||||
export function KanbanColumn({
|
||||
@@ -24,6 +25,7 @@ export function KanbanColumn({
|
||||
onAddCard,
|
||||
onCardDrop,
|
||||
onColumnReorder,
|
||||
onHide,
|
||||
}: KanbanColumnProps) {
|
||||
const { ref: bodyRef, isOver } = useKanbanColumnDrop({
|
||||
columnKey: column.key,
|
||||
@@ -38,6 +40,7 @@ export function KanbanColumn({
|
||||
color={column.color}
|
||||
count={column.rows.length}
|
||||
onReorderDrop={onColumnReorder}
|
||||
onHide={onHide}
|
||||
/>
|
||||
<div
|
||||
ref={bodyRef}
|
||||
|
||||
Reference in New Issue
Block a user