Compare commits
3 Commits
994bf5c7e0
...
8f5bf806e1
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f5bf806e1 | |||
| 0f57279c77 | |||
| ed5b8d1644 |
@@ -1127,7 +1127,7 @@
|
||||
115
|
||||
]
|
||||
],
|
||||
"translation": "档案委员会"
|
||||
"translation": "归档看板"
|
||||
},
|
||||
"TdfEV7": {
|
||||
"message": "Archived",
|
||||
@@ -1552,7 +1552,7 @@
|
||||
125
|
||||
]
|
||||
],
|
||||
"translation": "董事会"
|
||||
"translation": "看板"
|
||||
},
|
||||
"YhvWXb": {
|
||||
"message": "Board analytics",
|
||||
@@ -1564,7 +1564,7 @@
|
||||
85
|
||||
]
|
||||
],
|
||||
"translation": "板分析"
|
||||
"translation": "看板分析"
|
||||
},
|
||||
"i0ZMQl": {
|
||||
"message": "Board archived",
|
||||
@@ -1576,7 +1576,7 @@
|
||||
47
|
||||
]
|
||||
],
|
||||
"translation": "委员会存档"
|
||||
"translation": "看板已归档"
|
||||
},
|
||||
"wE3hGS": {
|
||||
"message": "Board moved",
|
||||
@@ -1588,7 +1588,7 @@
|
||||
33
|
||||
]
|
||||
],
|
||||
"translation": "板移动"
|
||||
"translation": "看板已移动"
|
||||
},
|
||||
"wewm3j": {
|
||||
"message": "Board name cannot exceed 100 characters",
|
||||
@@ -1600,7 +1600,7 @@
|
||||
23
|
||||
]
|
||||
],
|
||||
"translation": "单板名称不能超过100个字符"
|
||||
"translation": "看板名称不能超过 100 个字符"
|
||||
},
|
||||
"R1c3Mq": {
|
||||
"message": "Board name is required",
|
||||
@@ -1612,7 +1612,7 @@
|
||||
22
|
||||
]
|
||||
],
|
||||
"translation": "董事会名称是必需的"
|
||||
"translation": "看板名称是必需的"
|
||||
},
|
||||
"jwI32v": {
|
||||
"message": "Board not found",
|
||||
@@ -1628,7 +1628,7 @@
|
||||
181
|
||||
]
|
||||
],
|
||||
"translation": "找不到板子"
|
||||
"translation": "找不到看板"
|
||||
},
|
||||
"XNxYxq": {
|
||||
"message": "Board templates",
|
||||
@@ -1640,7 +1640,7 @@
|
||||
116
|
||||
]
|
||||
],
|
||||
"translation": "板模板"
|
||||
"translation": "看板模板"
|
||||
},
|
||||
"tbNcu4": {
|
||||
"message": "Board unarchived",
|
||||
@@ -1652,7 +1652,7 @@
|
||||
47
|
||||
]
|
||||
],
|
||||
"translation": "董事会从未归档"
|
||||
"translation": "看板已取消归档"
|
||||
},
|
||||
"Xid3K6": {
|
||||
"message": "Board URL can only contain letters, numbers, and hyphens",
|
||||
@@ -1664,7 +1664,7 @@
|
||||
46
|
||||
]
|
||||
],
|
||||
"translation": "“单板URL”只能包含字母、数字和连字符"
|
||||
"translation": "看板 URL 只能包含字母、数字和连字符"
|
||||
},
|
||||
"gv5kbo": {
|
||||
"message": "Board URL cannot exceed 60 characters",
|
||||
@@ -1676,7 +1676,7 @@
|
||||
44
|
||||
]
|
||||
],
|
||||
"translation": "“单板URL”不能超过60个字符"
|
||||
"translation": "看板 URL 不能超过 60 个字符"
|
||||
},
|
||||
"8+BY11": {
|
||||
"message": "Board URL copied to clipboard",
|
||||
@@ -1720,7 +1720,7 @@
|
||||
132
|
||||
]
|
||||
],
|
||||
"translation": "董事会的可见性"
|
||||
"translation": "看板可见性"
|
||||
},
|
||||
"PTzsxH": {
|
||||
"message": "Board visibility updated",
|
||||
@@ -1732,7 +1732,7 @@
|
||||
50
|
||||
]
|
||||
],
|
||||
"translation": "董事会可见性更新"
|
||||
"translation": "看板可见性已更新"
|
||||
},
|
||||
"DaBhdY": {
|
||||
"translation": "看板",
|
||||
@@ -1844,7 +1844,7 @@
|
||||
20
|
||||
]
|
||||
],
|
||||
"translation": "会制作板子"
|
||||
"translation": "可以创建看板"
|
||||
},
|
||||
"6zeSBB": {
|
||||
"message": "Can create cards",
|
||||
@@ -1892,7 +1892,7 @@
|
||||
22
|
||||
]
|
||||
],
|
||||
"translation": "可以删除单板"
|
||||
"translation": "可以删除看板"
|
||||
},
|
||||
"jNuzV/": {
|
||||
"message": "Can delete cards",
|
||||
@@ -1972,7 +1972,7 @@
|
||||
21
|
||||
]
|
||||
],
|
||||
"translation": "会编辑版面"
|
||||
"translation": "可以编辑看板"
|
||||
},
|
||||
"I+AROe": {
|
||||
"message": "Can edit cards",
|
||||
@@ -2116,7 +2116,7 @@
|
||||
19
|
||||
]
|
||||
],
|
||||
"translation": "可以查看单板"
|
||||
"translation": "可以查看看板"
|
||||
},
|
||||
"zoEbXq": {
|
||||
"message": "Can view cards",
|
||||
@@ -2893,7 +2893,7 @@
|
||||
149
|
||||
]
|
||||
],
|
||||
"translation": "将Trello帐户连接到导入板。"
|
||||
"translation": "连接您的 Trello 账户以导入看板。"
|
||||
},
|
||||
"jfC/xh": {
|
||||
"message": "Contact",
|
||||
@@ -3009,7 +3009,7 @@
|
||||
86
|
||||
]
|
||||
],
|
||||
"translation": "控制谁可以查看和编辑您的板。"
|
||||
"translation": "控制谁可以查看和编辑您的看板。"
|
||||
},
|
||||
"zQPhSa": {
|
||||
"message": "Convert to link",
|
||||
@@ -3033,7 +3033,7 @@
|
||||
87
|
||||
]
|
||||
],
|
||||
"translation": "抄板链接"
|
||||
"translation": "复制看板链接"
|
||||
},
|
||||
"F0YmUY": {
|
||||
"message": "Copy card link",
|
||||
@@ -3447,7 +3447,7 @@
|
||||
38
|
||||
]
|
||||
],
|
||||
"translation": "自定义板模板"
|
||||
"translation": "自定义看板模板"
|
||||
},
|
||||
"A42Dqn": {
|
||||
"message": "Custom branding",
|
||||
@@ -3704,7 +3704,7 @@
|
||||
148
|
||||
]
|
||||
],
|
||||
"translation": "删除板"
|
||||
"translation": "删除看板"
|
||||
},
|
||||
"nabda1": {
|
||||
"message": "Delete card",
|
||||
@@ -4245,7 +4245,7 @@
|
||||
116
|
||||
]
|
||||
],
|
||||
"translation": "编辑板URL"
|
||||
"translation": "编辑看板 URL"
|
||||
},
|
||||
"rF8SEQ": {
|
||||
"message": "Edit comment",
|
||||
@@ -4838,7 +4838,7 @@
|
||||
33
|
||||
]
|
||||
],
|
||||
"translation": "你需要的一切,永远免费。无限的棋盘,无限的列表,无限的牌。随时升级。"
|
||||
"translation": "你需要的一切,永远免费。无限的看板、列表和卡片。随时升级。"
|
||||
},
|
||||
"t+R8+P": {
|
||||
"message": "Execution",
|
||||
@@ -4902,7 +4902,7 @@
|
||||
94
|
||||
]
|
||||
],
|
||||
"translation": "创建板失败"
|
||||
"translation": "创建看板失败"
|
||||
},
|
||||
"o4sQAg": {
|
||||
"message": "Failed to create webhook",
|
||||
@@ -5531,7 +5531,7 @@
|
||||
44
|
||||
]
|
||||
],
|
||||
"translation": "到董事会"
|
||||
"translation": "前往看板"
|
||||
},
|
||||
"XEoGkQ": {
|
||||
"message": "Go to homepage",
|
||||
@@ -5707,7 +5707,7 @@
|
||||
46
|
||||
]
|
||||
],
|
||||
"translation": "我如何导入我的Trello板?"
|
||||
"translation": "如何导入我的 Trello 看板?"
|
||||
},
|
||||
"nol/Fb": {
|
||||
"message": "How do I invite team members?",
|
||||
@@ -5871,7 +5871,7 @@
|
||||
96
|
||||
]
|
||||
],
|
||||
"translation": "导入您的Trello板并开始运行。"
|
||||
"translation": "导入您的 Trello 看板,立即开始使用。"
|
||||
},
|
||||
"c/IAuR": {
|
||||
"message": "Important",
|
||||
@@ -5907,7 +5907,7 @@
|
||||
49
|
||||
]
|
||||
],
|
||||
"translation": "将Trello板导入Kan很容易。您可以按照我们的逐步指导<0>here</0>。"
|
||||
"translation": "将 Trello 看板导入 Kan 很容易。您可以按照我们的分步指南<0>此处</0>操作。"
|
||||
},
|
||||
"02i3WU": {
|
||||
"message": "Imports",
|
||||
@@ -6881,7 +6881,7 @@
|
||||
107
|
||||
]
|
||||
],
|
||||
"translation": "移动板"
|
||||
"translation": "移动看板"
|
||||
},
|
||||
"VvCMyU": {
|
||||
"message": "Move board to another workspace",
|
||||
@@ -6893,7 +6893,7 @@
|
||||
64
|
||||
]
|
||||
],
|
||||
"translation": "把板子移到另一个工作区"
|
||||
"translation": "将看板移动到另一个工作区"
|
||||
},
|
||||
"51UCsN": {
|
||||
"message": "Move to another list",
|
||||
@@ -7314,7 +7314,7 @@
|
||||
432
|
||||
]
|
||||
],
|
||||
"translation": "未发现板子"
|
||||
"translation": "未找到看板"
|
||||
},
|
||||
"OpNhRn": {
|
||||
"message": "No credit card required",
|
||||
@@ -9013,7 +9013,7 @@
|
||||
116
|
||||
]
|
||||
],
|
||||
"translation": "使用可重复使用的板模板节省时间。"
|
||||
"translation": "使用可复用的看板模板节省时间。"
|
||||
},
|
||||
"cOh+MI": {
|
||||
"message": "Screening",
|
||||
@@ -9037,7 +9037,7 @@
|
||||
105
|
||||
]
|
||||
],
|
||||
"translation": "搜索木板和卡片……"
|
||||
"translation": "搜索看板和卡片……"
|
||||
},
|
||||
"66Jmqh": {
|
||||
"message": "Seat limit reached",
|
||||
@@ -10173,7 +10173,7 @@
|
||||
49
|
||||
]
|
||||
],
|
||||
"translation": "董事会已存档。"
|
||||
"translation": "看板已归档。"
|
||||
},
|
||||
"ZWk38w": {
|
||||
"message": "The board has been moved to {0}.",
|
||||
@@ -10189,7 +10189,7 @@
|
||||
34
|
||||
]
|
||||
],
|
||||
"translation": "该板已移至{0}。"
|
||||
"translation": "看板已移至{0}。"
|
||||
},
|
||||
"C6gv54": {
|
||||
"message": "The board has been unarchived.",
|
||||
@@ -10201,7 +10201,7 @@
|
||||
50
|
||||
]
|
||||
],
|
||||
"translation": "这块板没有存档。"
|
||||
"translation": "看板已取消归档。"
|
||||
},
|
||||
"nKBUeF": {
|
||||
"message": "The card has been duplicated.",
|
||||
@@ -10237,7 +10237,7 @@
|
||||
33
|
||||
]
|
||||
],
|
||||
"translation": "Kan和Trello的主要区别在于,Kan是开源的,任何人都可以查看、修改和贡献我们的代码。我们的云产品对个人使用的功能也没有限制,而Trello锁定了基本功能,比如你可以在付费墙后面创建的电路板数量。"
|
||||
"translation": "Kan 和 Trello 的主要区别在于,Kan 是开源的,任何人都可以查看、修改和贡献代码。我们的云服务对个人使用的功能也不设限制,而 Trello 会将一些基本功能(例如可创建的看板数量)设置为付费项目。"
|
||||
},
|
||||
"6tDFBe": {
|
||||
"message": "The member's permissions have been updated.",
|
||||
@@ -10305,7 +10305,7 @@
|
||||
50
|
||||
]
|
||||
],
|
||||
"translation": "您的板的可见性已设置为{0}。",
|
||||
"translation": "您的看板可见性已设置为{0}。",
|
||||
"obsolete": true
|
||||
},
|
||||
"7ENRJu": {
|
||||
@@ -10410,7 +10410,7 @@
|
||||
184
|
||||
]
|
||||
],
|
||||
"translation": "此板是私有的或不存在"
|
||||
"translation": "此看板是私有的或不存在"
|
||||
},
|
||||
"wY+6Ye": {
|
||||
"message": "This board URL has already been taken",
|
||||
@@ -10970,7 +10970,7 @@
|
||||
45
|
||||
]
|
||||
],
|
||||
"translation": "无法移动板子"
|
||||
"translation": "无法移动看板"
|
||||
},
|
||||
"K7k9u3": {
|
||||
"message": "Unable to move card",
|
||||
@@ -11070,7 +11070,7 @@
|
||||
68
|
||||
]
|
||||
],
|
||||
"translation": "无法更新板"
|
||||
"translation": "无法更新看板"
|
||||
},
|
||||
"XpcjLO": {
|
||||
"message": "Unable to update board URL",
|
||||
@@ -11082,7 +11082,7 @@
|
||||
72
|
||||
]
|
||||
],
|
||||
"translation": "无法更新板URL"
|
||||
"translation": "无法更新看板 URL"
|
||||
},
|
||||
"Wy6pcF": {
|
||||
"message": "Unable to update board visibility",
|
||||
@@ -11094,7 +11094,7 @@
|
||||
57
|
||||
]
|
||||
],
|
||||
"translation": "无法更新电路板可见性"
|
||||
"translation": "无法更新看板可见性"
|
||||
},
|
||||
"o9WLwO": {
|
||||
"message": "Unable to update card",
|
||||
@@ -11250,7 +11250,7 @@
|
||||
115
|
||||
]
|
||||
],
|
||||
"translation": "Unarchive董事会"
|
||||
"translation": "取消归档看板"
|
||||
},
|
||||
"E8zYtd": {
|
||||
"message": "unassigned <0>{0}</0> from the card",
|
||||
@@ -11334,7 +11334,7 @@
|
||||
36
|
||||
]
|
||||
],
|
||||
"translation": "无限的董事会"
|
||||
"translation": "无限的看板"
|
||||
},
|
||||
"ZU5IYI": {
|
||||
"message": "Unlimited boards, unlimited lists, unlimited cards. No credit card required.",
|
||||
@@ -11346,7 +11346,7 @@
|
||||
57
|
||||
]
|
||||
],
|
||||
"translation": "无限的棋盘,无限的列表,无限的牌。不需要信用卡。"
|
||||
"translation": "无限的看板、列表和卡片。不需要信用卡。"
|
||||
},
|
||||
"Zz1qkk": {
|
||||
"message": "Unlimited cards",
|
||||
@@ -12529,7 +12529,7 @@
|
||||
68
|
||||
]
|
||||
],
|
||||
"translation": "你没有任何其他的工作区来移动这个板。"
|
||||
"translation": "没有其他工作区可供移动此看板。"
|
||||
},
|
||||
"h2FKMV": {
|
||||
"message": "You don't have permission",
|
||||
@@ -12731,7 +12731,7 @@
|
||||
382
|
||||
]
|
||||
],
|
||||
"translation": "您的看板已经导入。"
|
||||
"translation": "您的看板已导入。"
|
||||
},
|
||||
"LqWW5F": {
|
||||
"message": "Your display name has been updated.",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,15 +1,17 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
HiLink,
|
||||
HiOutlineArrowRightCircle,
|
||||
HiOutlineCalendar,
|
||||
HiOutlineDocumentDuplicate,
|
||||
HiOutlineTag,
|
||||
HiOutlineTrash,
|
||||
HiOutlineUserGroup,
|
||||
HiOutlineArrowRightCircle,
|
||||
} from "react-icons/hi2";
|
||||
|
||||
import { useIsomorphicLayoutEffect } from "~/hooks/useIsomorphicLayoutEffect";
|
||||
|
||||
export type CardContextMenuAction =
|
||||
| "members"
|
||||
| "move"
|
||||
@@ -85,6 +87,55 @@ export function CardContextMenu({
|
||||
canEdit,
|
||||
}: CardContextMenuProps) {
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const [position, setPosition] = useState({ x, y });
|
||||
|
||||
const items = MENU_ITEMS.filter((item) => !item.requiresEdit || canEdit);
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
const menu = menuRef.current;
|
||||
if (!menu) return;
|
||||
|
||||
const gutter = 8;
|
||||
const updatePosition = () => {
|
||||
const { width, height } = menu.getBoundingClientRect();
|
||||
const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
|
||||
const viewportHeight =
|
||||
window.visualViewport?.height ?? window.innerHeight;
|
||||
const maxX = Math.max(gutter, viewportWidth - width - gutter);
|
||||
const maxY = Math.max(gutter, viewportHeight - height - gutter);
|
||||
const nextPosition = {
|
||||
x: Math.min(Math.max(x, gutter), maxX),
|
||||
y: Math.min(Math.max(y, gutter), maxY),
|
||||
};
|
||||
|
||||
setPosition((currentPosition) =>
|
||||
currentPosition.x === nextPosition.x &&
|
||||
currentPosition.y === nextPosition.y
|
||||
? currentPosition
|
||||
: nextPosition,
|
||||
);
|
||||
};
|
||||
|
||||
updatePosition();
|
||||
|
||||
const resizeObserver =
|
||||
typeof ResizeObserver !== "undefined"
|
||||
? new ResizeObserver(updatePosition)
|
||||
: null;
|
||||
resizeObserver?.observe(menu);
|
||||
window.addEventListener("resize", updatePosition);
|
||||
window.addEventListener("scroll", updatePosition, true);
|
||||
window.visualViewport?.addEventListener("resize", updatePosition);
|
||||
window.visualViewport?.addEventListener("scroll", updatePosition);
|
||||
|
||||
return () => {
|
||||
resizeObserver?.disconnect();
|
||||
window.removeEventListener("resize", updatePosition);
|
||||
window.removeEventListener("scroll", updatePosition, true);
|
||||
window.visualViewport?.removeEventListener("resize", updatePosition);
|
||||
window.visualViewport?.removeEventListener("scroll", updatePosition);
|
||||
};
|
||||
}, [x, y, canEdit, items.length]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
@@ -103,13 +154,11 @@ export function CardContextMenu({
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
const items = MENU_ITEMS.filter((item) => !item.requiresEdit || canEdit);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="fixed z-[200] min-w-[200px] rounded-md border border-light-200 bg-white py-1 shadow-lg dark:border-dark-400 dark:bg-dark-200"
|
||||
style={{ left: x, top: y }}
|
||||
className="fixed z-[200] max-h-[calc(100dvh-1rem)] min-w-[min(200px,calc(100vw-1rem))] max-w-[calc(100vw-1rem)] overflow-y-auto overflow-x-hidden rounded-md border border-light-200 bg-white py-1 shadow-lg dark:border-dark-400 dark:bg-dark-200"
|
||||
style={{ left: position.x, top: position.y }}
|
||||
>
|
||||
{items.map(({ action, label, icon }) => (
|
||||
<button
|
||||
@@ -119,7 +168,7 @@ export function CardContextMenu({
|
||||
onAction(action);
|
||||
onClose();
|
||||
}}
|
||||
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
|
||||
className="flex w-full items-center gap-2 break-words px-3 py-2 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
|
||||
@@ -91,21 +91,35 @@ export function registerBoardTools(server: McpServer): void {
|
||||
|
||||
server.tool(
|
||||
"create_board",
|
||||
"Create a new board in a workspace",
|
||||
"Create a new board in a workspace, optionally with initial lists and labels",
|
||||
{
|
||||
workspacePublicId: z.string().describe("The workspace's public ID"),
|
||||
name: z.string().describe("Board name"),
|
||||
slug: z.string().optional().describe("URL-friendly slug (auto-generated if omitted)"),
|
||||
visibility: z
|
||||
.enum(["public", "private"])
|
||||
lists: z
|
||||
.array(z.string().min(1))
|
||||
.default([])
|
||||
.describe("Initial list names, for example [\"Backlog\", \"In Progress\", \"Done\"]"),
|
||||
labels: z
|
||||
.array(z.string().min(1))
|
||||
.default([])
|
||||
.describe("Initial label names, for example [\"Bug\", \"Feature\"]"),
|
||||
type: z
|
||||
.enum(["regular", "template"])
|
||||
.optional()
|
||||
.describe("Board visibility (default: private)"),
|
||||
.describe("Board type (defaults to regular)"),
|
||||
sourceBoardPublicId: z
|
||||
.string()
|
||||
.min(12)
|
||||
.optional()
|
||||
.describe("Source board public ID when cloning an existing board"),
|
||||
},
|
||||
async ({ workspacePublicId, name, slug, visibility }) => {
|
||||
async ({ workspacePublicId, name, lists, labels, type, sourceBoardPublicId }) => {
|
||||
const data = await kanRequest("POST", `/workspaces/${workspacePublicId}/boards`, {
|
||||
name,
|
||||
slug,
|
||||
visibility,
|
||||
lists,
|
||||
labels,
|
||||
type,
|
||||
sourceBoardPublicId,
|
||||
});
|
||||
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user