refactor(bases): guard kanban column drag from menu clicks; polish hidden chips strip

This commit is contained in:
Philipinho
2026-05-24 15:59:53 +01:00
parent a7d390932d
commit aebd54f13d
4 changed files with 28 additions and 5 deletions
@@ -1,6 +1,7 @@
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { Badge } from "@mantine/core"; import { Badge } from "@mantine/core";
import { IconPlus } from "@tabler/icons-react"; import { IconPlus } from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
import { import {
IBase, IBase,
IBaseRow, IBaseRow,
@@ -36,6 +37,7 @@ export function BaseKanban({
effectiveView, effectiveView,
onCardClick, onCardClick,
}: BaseKanbanProps) { }: BaseKanbanProps) {
const { t } = useTranslation();
const groupByPropertyId = effectiveView?.config?.groupByPropertyId; const groupByPropertyId = effectiveView?.config?.groupByPropertyId;
const property = useMemo( const property = useMemo(
() => () =>
@@ -211,13 +213,13 @@ export function BaseKanban({
return hiddenIds return hiddenIds
.map((id) => .map((id) =>
id === NO_VALUE_CHOICE_ID id === NO_VALUE_CHOICE_ID
? { id, name: "No value", color: null as string | null } ? { id, name: t("No value"), color: null as string | null }
: byId.get(id) : byId.get(id)
? { id, name: byId.get(id)!.name, color: byId.get(id)!.color as string | null } ? { id, name: byId.get(id)!.name, color: byId.get(id)!.color as string | null }
: null, : null,
) )
.filter((c): c is { id: string; name: string; color: string | null } => c !== null); .filter((c): c is { id: string; name: string; color: string | null } => c !== null);
}, [hiddenIds, isGroupable, property]); }, [hiddenIds, isGroupable, property, t]);
if (!isGroupable) { if (!isGroupable) {
return <KanbanEmptyState base={base} onPick={handlePickProperty} />; return <KanbanEmptyState base={base} onPick={handlePickProperty} />;
@@ -226,13 +228,13 @@ export function BaseKanban({
return ( return (
<> <>
{hiddenChoices.length > 0 && ( {hiddenChoices.length > 0 && (
<div style={{ padding: "6px 12px", display: "flex", gap: 6, flexWrap: "wrap" }}> <div className={classes.hiddenStrip}>
{hiddenChoices.map((c) => ( {hiddenChoices.map((c) => (
<Badge <Badge
key={c.id} key={c.id}
color={c.color ?? "gray"} color={c.color ?? "gray"}
variant="outline" variant="outline"
style={{ cursor: "pointer" }} className={classes.hiddenChip}
onClick={() => handleShowColumn(c.id)} onClick={() => handleShowColumn(c.id)}
rightSection={<IconPlus size={12} />} rightSection={<IconPlus size={12} />}
> >
@@ -50,7 +50,7 @@ export function KanbanColumnHeader({
</div> </div>
<Menu shadow="md" width={160} position="bottom-end"> <Menu shadow="md" width={160} position="bottom-end">
<Menu.Target> <Menu.Target>
<ActionIcon variant="subtle" size="sm" color="gray"> <ActionIcon variant="subtle" size="sm" color="gray" data-no-drag>
<IconDots size={14} /> <IconDots size={14} />
</ActionIcon> </ActionIcon>
</Menu.Target> </Menu.Target>
@@ -36,6 +36,16 @@ export function useKanbanColumnReorder({
return combine( return combine(
draggable({ draggable({
element: el, element: el,
canDrag: ({ input }) => {
// Don't start a drag when the user is interacting with a marked
// "no-drag" subtree (e.g. the column header's menu trigger).
const target = document.elementFromPoint(
input.clientX,
input.clientY,
) as HTMLElement | null;
if (target?.closest("[data-no-drag]")) return false;
return true;
},
getInitialData: () => ({ type: "base-kanban-column", columnKey }), getInitialData: () => ({ type: "base-kanban-column", columnKey }),
onDragStart: () => setIsDragging(true), onDragStart: () => setIsDragging(true),
onDrop: () => setIsDragging(false), onDrop: () => setIsDragging(false),
@@ -91,3 +91,14 @@
font-size: var(--mantine-font-size-xs); font-size: var(--mantine-font-size-xs);
color: var(--mantine-color-dimmed); color: var(--mantine-color-dimmed);
} }
.hiddenStrip {
padding: 6px 12px;
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.hiddenChip {
cursor: pointer;
}