mirror of
https://github.com/docmost/docmost.git
synced 2026-08-30 02:25:01 +08:00
person cell
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useRef, useEffect, useCallback, useMemo } from "react";
|
import { useState, useRef, useEffect, useCallback, useMemo } from "react";
|
||||||
import { Popover, TextInput } from "@mantine/core";
|
import { Popover } from "@mantine/core";
|
||||||
|
import { IconX } from "@tabler/icons-react";
|
||||||
import { IBaseProperty } from "@/features/base/types/base.types";
|
import { IBaseProperty } from "@/features/base/types/base.types";
|
||||||
import { useWorkspaceMembersQuery } from "@/features/workspace/queries/workspace-query";
|
import { useWorkspaceMembersQuery } from "@/features/workspace/queries/workspace-query";
|
||||||
import { CustomAvatar } from "@/components/ui/custom-avatar";
|
import { CustomAvatar } from "@/components/ui/custom-avatar";
|
||||||
@@ -25,7 +26,6 @@ export function CellPerson({
|
|||||||
: typeof value === "string"
|
: typeof value === "string"
|
||||||
? [value]
|
? [value]
|
||||||
: [];
|
: [];
|
||||||
const selectedSet = new Set(personIds);
|
|
||||||
|
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const searchRef = useRef<HTMLInputElement>(null);
|
const searchRef = useRef<HTMLInputElement>(null);
|
||||||
@@ -37,7 +37,6 @@ export function CellPerson({
|
|||||||
}
|
}
|
||||||
}, [isEditing]);
|
}, [isEditing]);
|
||||||
|
|
||||||
// Fetch members for display (always) and search (when editing)
|
|
||||||
const { data: membersData } = useWorkspaceMembersQuery({ limit: 100 });
|
const { data: membersData } = useWorkspaceMembersQuery({ limit: 100 });
|
||||||
const members = membersData?.items ?? [];
|
const members = membersData?.items ?? [];
|
||||||
const memberMap = useMemo(() => {
|
const memberMap = useMemo(() => {
|
||||||
@@ -46,7 +45,6 @@ export function CellPerson({
|
|||||||
return map;
|
return map;
|
||||||
}, [members]);
|
}, [members]);
|
||||||
|
|
||||||
// Filtered members for editing
|
|
||||||
const filteredMembers = search
|
const filteredMembers = search
|
||||||
? members.filter(
|
? members.filter(
|
||||||
(m) =>
|
(m) =>
|
||||||
@@ -55,14 +53,31 @@ export function CellPerson({
|
|||||||
)
|
)
|
||||||
: members;
|
: members;
|
||||||
|
|
||||||
const handleToggle = useCallback(
|
const handleAdd = useCallback(
|
||||||
(memberId: string) => {
|
(memberId: string) => {
|
||||||
const newIds = selectedSet.has(memberId)
|
if (personIds.includes(memberId)) return;
|
||||||
? personIds.filter((id) => id !== memberId)
|
onCommit([...personIds, memberId]);
|
||||||
: [...personIds, memberId];
|
},
|
||||||
|
[personIds, onCommit],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRemove = useCallback(
|
||||||
|
(memberId: string) => {
|
||||||
|
const newIds = personIds.filter((id) => id !== memberId);
|
||||||
onCommit(newIds.length > 0 ? newIds : null);
|
onCommit(newIds.length > 0 ? newIds : null);
|
||||||
},
|
},
|
||||||
[personIds, selectedSet, onCommit],
|
[personIds, onCommit],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleToggle = useCallback(
|
||||||
|
(memberId: string) => {
|
||||||
|
if (personIds.includes(memberId)) {
|
||||||
|
handleRemove(memberId);
|
||||||
|
} else {
|
||||||
|
handleAdd(memberId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[personIds, handleAdd, handleRemove],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
@@ -71,11 +86,15 @@ export function CellPerson({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
|
if (e.key === "Backspace" && search === "" && personIds.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
handleRemove(personIds[personIds.length - 1]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[onCancel],
|
[onCancel, search, personIds, handleRemove],
|
||||||
);
|
);
|
||||||
|
|
||||||
const MAX_VISIBLE = 4;
|
const selectedSet = new Set(personIds);
|
||||||
|
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
return (
|
return (
|
||||||
@@ -83,85 +102,79 @@ export function CellPerson({
|
|||||||
opened
|
opened
|
||||||
onClose={onCancel}
|
onClose={onCancel}
|
||||||
position="bottom-start"
|
position="bottom-start"
|
||||||
width={260}
|
width={300}
|
||||||
trapFocus
|
trapFocus
|
||||||
>
|
>
|
||||||
<Popover.Target>
|
<Popover.Target>
|
||||||
<div style={{ width: "100%", height: "100%" }}>
|
<div style={{ width: "100%", height: "100%" }}>
|
||||||
<PersonAvatarList
|
<PersonReadList personIds={personIds} memberMap={memberMap} />
|
||||||
personIds={personIds}
|
|
||||||
memberMap={memberMap}
|
|
||||||
maxVisible={MAX_VISIBLE}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Popover.Target>
|
</Popover.Target>
|
||||||
<Popover.Dropdown p={4}>
|
<Popover.Dropdown p={0}>
|
||||||
<TextInput
|
{/* Tag input area */}
|
||||||
ref={searchRef}
|
<div className={cellClasses.personTagArea}>
|
||||||
size="xs"
|
{personIds.map((id) => {
|
||||||
placeholder="Search members..."
|
const member = memberMap.get(id);
|
||||||
value={search}
|
const name = member?.name ?? id.substring(0, 8);
|
||||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
return (
|
||||||
onKeyDown={handleKeyDown}
|
<span key={id} className={cellClasses.personTag}>
|
||||||
mb={4}
|
<CustomAvatar
|
||||||
/>
|
avatarUrl={member?.avatarUrl ?? ""}
|
||||||
|
name={name}
|
||||||
|
size={18}
|
||||||
|
radius="xl"
|
||||||
|
/>
|
||||||
|
<span className={cellClasses.personTagName}>{name}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={cellClasses.personTagRemove}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleRemove(id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconX size={10} />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<input
|
||||||
|
ref={searchRef}
|
||||||
|
className={cellClasses.personTagInput}
|
||||||
|
placeholder={personIds.length === 0 ? "Search for a person..." : ""}
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Dropdown */}
|
||||||
|
<div className={cellClasses.personDropdownDivider} />
|
||||||
|
<div className={cellClasses.personDropdownHint}>
|
||||||
|
Select as many as you like
|
||||||
|
</div>
|
||||||
<div className={cellClasses.selectDropdown}>
|
<div className={cellClasses.selectDropdown}>
|
||||||
{filteredMembers.map((member) => (
|
{filteredMembers.map((member) => (
|
||||||
<div
|
<div
|
||||||
key={member.id}
|
key={member.id}
|
||||||
className={`${cellClasses.selectOption} ${
|
className={`${cellClasses.selectOption} ${
|
||||||
selectedSet.has(member.id)
|
selectedSet.has(member.id) ? cellClasses.selectOptionActive : ""
|
||||||
? cellClasses.selectOptionActive
|
|
||||||
: ""
|
|
||||||
}`}
|
}`}
|
||||||
onClick={() => handleToggle(member.id)}
|
onClick={() => handleToggle(member.id)}
|
||||||
>
|
>
|
||||||
<div
|
<CustomAvatar
|
||||||
style={{ display: "flex", alignItems: "center", gap: 8 }}
|
avatarUrl={member.avatarUrl}
|
||||||
>
|
name={member.name}
|
||||||
<CustomAvatar
|
size={24}
|
||||||
avatarUrl={member.avatarUrl}
|
radius="xl"
|
||||||
name={member.name}
|
/>
|
||||||
size={22}
|
<span className={cellClasses.personOptionName}>
|
||||||
radius="xl"
|
{member.name}
|
||||||
/>
|
</span>
|
||||||
<div style={{ overflow: "hidden" }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: 500,
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
overflow: "hidden",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{member.name}
|
|
||||||
</div>
|
|
||||||
{member.email && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: 11,
|
|
||||||
color: "var(--mantine-color-dimmed)",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
overflow: "hidden",
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{member.email}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{filteredMembers.length === 0 && (
|
{filteredMembers.length === 0 && (
|
||||||
<div
|
<div className={cellClasses.personDropdownHint}>
|
||||||
style={{
|
|
||||||
padding: "8px 12px",
|
|
||||||
fontSize: 12,
|
|
||||||
color: "var(--mantine-color-dimmed)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
No members found
|
No members found
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -175,48 +188,36 @@ export function CellPerson({
|
|||||||
return <span className={cellClasses.emptyValue} />;
|
return <span className={cellClasses.emptyValue} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <PersonReadList personIds={personIds} memberMap={memberMap} />;
|
||||||
<PersonAvatarList
|
|
||||||
personIds={personIds}
|
|
||||||
memberMap={memberMap}
|
|
||||||
maxVisible={MAX_VISIBLE}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function PersonAvatarList({
|
function PersonReadList({
|
||||||
personIds,
|
personIds,
|
||||||
memberMap,
|
memberMap,
|
||||||
maxVisible,
|
|
||||||
}: {
|
}: {
|
||||||
personIds: string[];
|
personIds: string[];
|
||||||
memberMap: Map<
|
memberMap: Map<
|
||||||
string,
|
string,
|
||||||
{ id: string; name: string; email?: string; avatarUrl?: string }
|
{ id: string; name: string; email?: string; avatarUrl?: string }
|
||||||
>;
|
>;
|
||||||
maxVisible: number;
|
|
||||||
}) {
|
}) {
|
||||||
const visible = personIds.slice(0, maxVisible);
|
|
||||||
const overflow = personIds.length - maxVisible;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cellClasses.personGroup}>
|
<div className={cellClasses.personGroup}>
|
||||||
{visible.map((id) => {
|
{personIds.map((id) => {
|
||||||
const member = memberMap.get(id);
|
const member = memberMap.get(id);
|
||||||
const name = member?.name ?? id.substring(0, 2);
|
const name = member?.name ?? id.substring(0, 8);
|
||||||
return (
|
return (
|
||||||
<CustomAvatar
|
<div key={id} className={cellClasses.personRow}>
|
||||||
key={id}
|
<CustomAvatar
|
||||||
avatarUrl={member?.avatarUrl ?? ""}
|
avatarUrl={member?.avatarUrl ?? ""}
|
||||||
name={name}
|
name={name}
|
||||||
size={22}
|
size={20}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
/>
|
/>
|
||||||
|
<span className={cellClasses.personName}>{name}</span>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{overflow > 0 && (
|
|
||||||
<span className={cellClasses.overflowCount}>+{overflow}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { IPagination } from "@/lib/types";
|
import { IPagination } from "@/lib/types";
|
||||||
|
|
||||||
type RowCacheContext = {
|
type RowCacheContext = {
|
||||||
previous: InfiniteData<IPagination<IBaseRow>> | undefined;
|
snapshots: [readonly unknown[], InfiniteData<IPagination<IBaseRow>> | undefined][];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useBaseRowsQuery(
|
export function useBaseRowsQuery(
|
||||||
@@ -57,8 +57,8 @@ export function useCreateRowMutation() {
|
|||||||
return useMutation<IBaseRow, Error, CreateRowInput>({
|
return useMutation<IBaseRow, Error, CreateRowInput>({
|
||||||
mutationFn: (data) => createRow(data),
|
mutationFn: (data) => createRow(data),
|
||||||
onSuccess: (newRow) => {
|
onSuccess: (newRow) => {
|
||||||
queryClient.setQueryData<InfiniteData<IPagination<IBaseRow>>>(
|
queryClient.setQueriesData<InfiniteData<IPagination<IBaseRow>>>(
|
||||||
["base-rows", newRow.baseId],
|
{ queryKey: ["base-rows", newRow.baseId] },
|
||||||
(old) => {
|
(old) => {
|
||||||
if (!old) return old;
|
if (!old) return old;
|
||||||
const lastPageIndex = old.pages.length - 1;
|
const lastPageIndex = old.pages.length - 1;
|
||||||
@@ -92,12 +92,12 @@ export function useUpdateRowMutation() {
|
|||||||
queryKey: ["base-rows", variables.baseId],
|
queryKey: ["base-rows", variables.baseId],
|
||||||
});
|
});
|
||||||
|
|
||||||
const previous = queryClient.getQueryData<
|
const snapshots = queryClient.getQueriesData<
|
||||||
InfiniteData<IPagination<IBaseRow>>
|
InfiniteData<IPagination<IBaseRow>>
|
||||||
>(["base-rows", variables.baseId]);
|
>({ queryKey: ["base-rows", variables.baseId] });
|
||||||
|
|
||||||
queryClient.setQueryData<InfiniteData<IPagination<IBaseRow>>>(
|
queryClient.setQueriesData<InfiniteData<IPagination<IBaseRow>>>(
|
||||||
["base-rows", variables.baseId],
|
{ queryKey: ["base-rows", variables.baseId] },
|
||||||
(old) => {
|
(old) => {
|
||||||
if (!old) return old;
|
if (!old) return old;
|
||||||
return {
|
return {
|
||||||
@@ -117,14 +117,13 @@ export function useUpdateRowMutation() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return { previous };
|
return { snapshots };
|
||||||
},
|
},
|
||||||
onError: (_, variables, context) => {
|
onError: (_, variables, context) => {
|
||||||
if (context?.previous) {
|
if (context?.snapshots) {
|
||||||
queryClient.setQueryData(
|
for (const [key, data] of context.snapshots) {
|
||||||
["base-rows", variables.baseId],
|
queryClient.setQueryData(key, data);
|
||||||
context.previous,
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
notifications.show({
|
notifications.show({
|
||||||
message: t("Failed to update row"),
|
message: t("Failed to update row"),
|
||||||
@@ -132,8 +131,8 @@ export function useUpdateRowMutation() {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSuccess: (updatedRow) => {
|
onSuccess: (updatedRow) => {
|
||||||
queryClient.setQueryData<InfiniteData<IPagination<IBaseRow>>>(
|
queryClient.setQueriesData<InfiniteData<IPagination<IBaseRow>>>(
|
||||||
["base-rows", updatedRow.baseId],
|
{ queryKey: ["base-rows", updatedRow.baseId] },
|
||||||
(old) => {
|
(old) => {
|
||||||
if (!old) return old;
|
if (!old) return old;
|
||||||
return {
|
return {
|
||||||
@@ -162,12 +161,12 @@ export function useDeleteRowMutation() {
|
|||||||
queryKey: ["base-rows", variables.baseId],
|
queryKey: ["base-rows", variables.baseId],
|
||||||
});
|
});
|
||||||
|
|
||||||
const previous = queryClient.getQueryData<
|
const snapshots = queryClient.getQueriesData<
|
||||||
InfiniteData<IPagination<IBaseRow>>
|
InfiniteData<IPagination<IBaseRow>>
|
||||||
>(["base-rows", variables.baseId]);
|
>({ queryKey: ["base-rows", variables.baseId] });
|
||||||
|
|
||||||
queryClient.setQueryData<InfiniteData<IPagination<IBaseRow>>>(
|
queryClient.setQueriesData<InfiniteData<IPagination<IBaseRow>>>(
|
||||||
["base-rows", variables.baseId],
|
{ queryKey: ["base-rows", variables.baseId] },
|
||||||
(old) => {
|
(old) => {
|
||||||
if (!old) return old;
|
if (!old) return old;
|
||||||
return {
|
return {
|
||||||
@@ -180,14 +179,13 @@ export function useDeleteRowMutation() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return { previous };
|
return { snapshots };
|
||||||
},
|
},
|
||||||
onError: (_, variables, context) => {
|
onError: (_, variables, context) => {
|
||||||
if (context?.previous) {
|
if (context?.snapshots) {
|
||||||
queryClient.setQueryData(
|
for (const [key, data] of context.snapshots) {
|
||||||
["base-rows", variables.baseId],
|
queryClient.setQueryData(key, data);
|
||||||
context.previous,
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
notifications.show({
|
notifications.show({
|
||||||
message: t("Failed to delete row"),
|
message: t("Failed to delete row"),
|
||||||
@@ -206,12 +204,12 @@ export function useReorderRowMutation() {
|
|||||||
queryKey: ["base-rows", variables.baseId],
|
queryKey: ["base-rows", variables.baseId],
|
||||||
});
|
});
|
||||||
|
|
||||||
const previous = queryClient.getQueryData<
|
const snapshots = queryClient.getQueriesData<
|
||||||
InfiniteData<IPagination<IBaseRow>>
|
InfiniteData<IPagination<IBaseRow>>
|
||||||
>(["base-rows", variables.baseId]);
|
>({ queryKey: ["base-rows", variables.baseId] });
|
||||||
|
|
||||||
queryClient.setQueryData<InfiniteData<IPagination<IBaseRow>>>(
|
queryClient.setQueriesData<InfiniteData<IPagination<IBaseRow>>>(
|
||||||
["base-rows", variables.baseId],
|
{ queryKey: ["base-rows", variables.baseId] },
|
||||||
(old) => {
|
(old) => {
|
||||||
if (!old) return old;
|
if (!old) return old;
|
||||||
return {
|
return {
|
||||||
@@ -228,14 +226,13 @@ export function useReorderRowMutation() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return { previous };
|
return { snapshots };
|
||||||
},
|
},
|
||||||
onError: (_, variables, context) => {
|
onError: (_, variables, context) => {
|
||||||
if (context?.previous) {
|
if (context?.snapshots) {
|
||||||
queryClient.setQueryData(
|
for (const [key, data] of context.snapshots) {
|
||||||
["base-rows", variables.baseId],
|
queryClient.setQueryData(key, data);
|
||||||
context.previous,
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
notifications.show({
|
notifications.show({
|
||||||
message: t("Failed to reorder row"),
|
message: t("Failed to reorder row"),
|
||||||
|
|||||||
@@ -89,27 +89,111 @@
|
|||||||
color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4));
|
color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Person cell — read mode (vertical list like Notion) */
|
||||||
|
|
||||||
.personGroup {
|
.personGroup {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
gap: 4px;
|
gap: 2px;
|
||||||
overflow: hidden;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.personAvatar {
|
.personRow {
|
||||||
width: 22px;
|
|
||||||
height: 22px;
|
|
||||||
border-radius: 50%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
background-color: light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personName {
|
||||||
|
font-size: var(--mantine-font-size-xs);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Person cell — edit mode (tag input + dropdown) */
|
||||||
|
|
||||||
|
.personTagArea {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
min-height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personTag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 2px 4px 2px 2px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
||||||
|
font-size: var(--mantine-font-size-xs);
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personTagName {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personTagRemove {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 10px;
|
width: 16px;
|
||||||
font-weight: 600;
|
height: 16px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: transparent;
|
||||||
|
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-2));
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personTagRemove:hover {
|
||||||
|
background-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));
|
||||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.personTagInput {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 60px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: var(--mantine-font-size-xs);
|
||||||
|
font-family: inherit;
|
||||||
|
color: inherit;
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personTagInput::placeholder {
|
||||||
|
color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-3));
|
||||||
|
}
|
||||||
|
|
||||||
|
.personDropdownDivider {
|
||||||
|
height: 1px;
|
||||||
|
background-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));
|
||||||
|
}
|
||||||
|
|
||||||
|
.personDropdownHint {
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
|
||||||
|
}
|
||||||
|
|
||||||
|
.personOptionName {
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
.fileGroup {
|
.fileGroup {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -126,7 +126,7 @@
|
|||||||
.cell {
|
.cell {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 36px;
|
min-height: 36px;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
font-size: var(--mantine-font-size-sm);
|
font-size: var(--mantine-font-size-sm);
|
||||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
|
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
|
||||||
|
|||||||
Reference in New Issue
Block a user