mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
feat(base): replace property type picker with read-only display
This commit is contained in:
@@ -2,8 +2,7 @@ import { memo, useCallback, useRef } from "react";
|
|||||||
import { Header, flexRender } from "@tanstack/react-table";
|
import { Header, flexRender } from "@tanstack/react-table";
|
||||||
import { useSortable } from "@dnd-kit/sortable";
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { Loader, Popover, Tooltip } from "@mantine/core";
|
import { Popover } from "@mantine/core";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { IBaseRow, IBaseProperty, EditingCell } from "@/features/base/types/base.types";
|
import { IBaseRow, IBaseProperty, EditingCell } from "@/features/base/types/base.types";
|
||||||
import { activePropertyMenuAtom, propertyMenuDirtyAtom, editingCellAtom } from "@/features/base/atoms/base-atoms";
|
import { activePropertyMenuAtom, propertyMenuDirtyAtom, editingCellAtom } from "@/features/base/atoms/base-atoms";
|
||||||
@@ -50,14 +49,12 @@ type GridHeaderCellProps = {
|
|||||||
export const GridHeaderCell = memo(function GridHeaderCell({
|
export const GridHeaderCell = memo(function GridHeaderCell({
|
||||||
header,
|
header,
|
||||||
}: GridHeaderCellProps) {
|
}: GridHeaderCellProps) {
|
||||||
const { t } = useTranslation();
|
|
||||||
const property = header.column.columnDef.meta?.property as
|
const property = header.column.columnDef.meta?.property as
|
||||||
| IBaseProperty
|
| IBaseProperty
|
||||||
| undefined;
|
| undefined;
|
||||||
const isRowNumber = header.column.id === "__row_number";
|
const isRowNumber = header.column.id === "__row_number";
|
||||||
const isPinned = header.column.getIsPinned();
|
const isPinned = header.column.getIsPinned();
|
||||||
const pinOffset = isPinned ? header.column.getStart("left") : undefined;
|
const pinOffset = isPinned ? header.column.getStart("left") : undefined;
|
||||||
const isConverting = !!property?.pendingType;
|
|
||||||
|
|
||||||
const [activePropertyMenu, setActivePropertyMenu] = useAtom(activePropertyMenuAtom) as unknown as [string | null, (val: string | null) => void];
|
const [activePropertyMenu, setActivePropertyMenu] = useAtom(activePropertyMenuAtom) as unknown as [string | null, (val: string | null) => void];
|
||||||
const menuOpened = activePropertyMenu === header.column.id;
|
const menuOpened = activePropertyMenu === header.column.id;
|
||||||
@@ -141,20 +138,6 @@ export const GridHeaderCell = memo(function GridHeaderCell({
|
|||||||
<span className={classes.headerCellName}>
|
<span className={classes.headerCellName}>
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
</span>
|
</span>
|
||||||
{isConverting && (
|
|
||||||
<Tooltip
|
|
||||||
label={t("Converting to {{type}}…", {
|
|
||||||
type: property?.pendingType,
|
|
||||||
})}
|
|
||||||
withArrow
|
|
||||||
>
|
|
||||||
<Loader
|
|
||||||
size={12}
|
|
||||||
color="gray"
|
|
||||||
className={classes.headerConvertingSpinner}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{header.column.getCanResize() && (
|
{header.column.getCanResize() && (
|
||||||
|
|||||||
@@ -14,20 +14,16 @@ import {
|
|||||||
IconTrash,
|
IconTrash,
|
||||||
IconPencil,
|
IconPencil,
|
||||||
IconChevronRight,
|
IconChevronRight,
|
||||||
IconTransform,
|
|
||||||
IconSettings,
|
IconSettings,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import {
|
import { IBaseProperty } from "@/features/base/types/base.types";
|
||||||
IBaseProperty,
|
|
||||||
BasePropertyType,
|
|
||||||
} from "@/features/base/types/base.types";
|
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import { propertyMenuCloseRequestAtom } from "@/features/base/atoms/base-atoms";
|
import { propertyMenuCloseRequestAtom } from "@/features/base/atoms/base-atoms";
|
||||||
import {
|
import {
|
||||||
useUpdatePropertyMutation,
|
useUpdatePropertyMutation,
|
||||||
useDeletePropertyMutation,
|
useDeletePropertyMutation,
|
||||||
} from "@/features/base/queries/base-property-query";
|
} from "@/features/base/queries/base-property-query";
|
||||||
import { PropertyTypePicker } from "./property-type-picker";
|
import { propertyTypes } from "./property-type-picker";
|
||||||
import { PropertyOptions } from "./property-options";
|
import { PropertyOptions } from "./property-options";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { isSystemPropertyType } from "@/features/base/hooks/use-base-table";
|
import { isSystemPropertyType } from "@/features/base/hooks/use-base-table";
|
||||||
@@ -40,7 +36,7 @@ type PropertyMenuContentProps = {
|
|||||||
onDirtyChange?: (dirty: boolean) => void;
|
onDirtyChange?: (dirty: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MenuPanel = "main" | "rename" | "changeType" | "options" | "confirmDelete" | "confirmDiscard";
|
type MenuPanel = "main" | "rename" | "options" | "confirmDelete" | "confirmDiscard";
|
||||||
|
|
||||||
export function PropertyMenuContent({
|
export function PropertyMenuContent({
|
||||||
property,
|
property,
|
||||||
@@ -113,20 +109,6 @@ export function PropertyMenuContent({
|
|||||||
[handleRenameAndClose, onClose],
|
[handleRenameAndClose, onClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleTypeChange = useCallback(
|
|
||||||
(type: BasePropertyType) => {
|
|
||||||
if (type !== property.type) {
|
|
||||||
updatePropertyMutation.mutate({
|
|
||||||
propertyId: property.id,
|
|
||||||
baseId: property.baseId,
|
|
||||||
type,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
onClose();
|
|
||||||
},
|
|
||||||
[property, updatePropertyMutation, onClose],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOptionsUpdate = useCallback(
|
const handleOptionsUpdate = useCallback(
|
||||||
(typeOptions: Record<string, unknown>) => {
|
(typeOptions: Record<string, unknown>) => {
|
||||||
updatePropertyMutation.mutate({
|
updatePropertyMutation.mutate({
|
||||||
@@ -197,7 +179,6 @@ export function PropertyMenuContent({
|
|||||||
<MainPanel
|
<MainPanel
|
||||||
property={property}
|
property={property}
|
||||||
onRename={() => setPanel("rename")}
|
onRename={() => setPanel("rename")}
|
||||||
onChangeType={() => setPanel("changeType")}
|
|
||||||
onOptions={() => setPanel("options")}
|
onOptions={() => setPanel("options")}
|
||||||
onDelete={() => setPanel("confirmDelete")}
|
onDelete={() => setPanel("confirmDelete")}
|
||||||
/>
|
/>
|
||||||
@@ -217,13 +198,6 @@ export function PropertyMenuContent({
|
|||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
{panel === "changeType" && (
|
|
||||||
<TypePanel
|
|
||||||
currentType={property.type}
|
|
||||||
onSelect={handleTypeChange}
|
|
||||||
onBack={() => setPanel("main")}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(panel === "options" || panel === "confirmDiscard") && (
|
{(panel === "options" || panel === "confirmDiscard") && (
|
||||||
<Stack gap="xs" p="sm" style={panel === "confirmDiscard" ? { display: "none" } : undefined}>
|
<Stack gap="xs" p="sm" style={panel === "confirmDiscard" ? { display: "none" } : undefined}>
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
@@ -343,13 +317,11 @@ function MenuItem({
|
|||||||
function MainPanel({
|
function MainPanel({
|
||||||
property,
|
property,
|
||||||
onRename,
|
onRename,
|
||||||
onChangeType,
|
|
||||||
onOptions,
|
onOptions,
|
||||||
onDelete,
|
onDelete,
|
||||||
}: {
|
}: {
|
||||||
property: IBaseProperty;
|
property: IBaseProperty;
|
||||||
onRename: () => void;
|
onRename: () => void;
|
||||||
onChangeType: () => void;
|
|
||||||
onOptions: () => void;
|
onOptions: () => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
}) {
|
}) {
|
||||||
@@ -365,6 +337,9 @@ function MainPanel({
|
|||||||
property.type === "number" ||
|
property.type === "number" ||
|
||||||
property.type === "date");
|
property.type === "date");
|
||||||
|
|
||||||
|
const typeDef = propertyTypes.find((pt) => pt.type === property.type);
|
||||||
|
const TypeIcon = typeDef?.icon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap={0} p={4}>
|
<Stack gap={0} p={4}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@@ -373,12 +348,16 @@ function MainPanel({
|
|||||||
onClick={onRename}
|
onClick={onRename}
|
||||||
/>
|
/>
|
||||||
{!isSystem && (
|
{!isSystem && (
|
||||||
<MenuItem
|
<Stack gap={4} px="sm" py={6}>
|
||||||
icon={<IconTransform size={14} />}
|
<Text size="xs" c="dimmed">{t("Type")}</Text>
|
||||||
label={t("Change type")}
|
<TextInput
|
||||||
rightIcon={<IconChevronRight size={14} />}
|
size="xs"
|
||||||
onClick={onChangeType}
|
value={typeDef ? t(typeDef.labelKey) : property.type}
|
||||||
/>
|
disabled
|
||||||
|
leftSection={TypeIcon ? <TypeIcon size={14} /> : null}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
)}
|
)}
|
||||||
{hasOptions && (
|
{hasOptions && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@@ -403,41 +382,3 @@ function MainPanel({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TypePanel({
|
|
||||||
currentType,
|
|
||||||
onSelect,
|
|
||||||
onBack,
|
|
||||||
}: {
|
|
||||||
currentType: BasePropertyType;
|
|
||||||
onSelect: (type: BasePropertyType) => void;
|
|
||||||
onBack: () => void;
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack gap={0} p={4}>
|
|
||||||
<Group gap="xs" px="sm" py={6}>
|
|
||||||
<ActionIcon
|
|
||||||
variant="subtle"
|
|
||||||
color="gray"
|
|
||||||
size="xs"
|
|
||||||
onClick={onBack}
|
|
||||||
>
|
|
||||||
<IconChevronRight
|
|
||||||
size={14}
|
|
||||||
style={{ transform: "rotate(180deg)" }}
|
|
||||||
/>
|
|
||||||
</ActionIcon>
|
|
||||||
<Text size="xs" fw={600} c="dimmed">
|
|
||||||
{t("Change type")}
|
|
||||||
</Text>
|
|
||||||
</Group>
|
|
||||||
<ScrollArea.Autosize mah={300}>
|
|
||||||
<PropertyTypePicker
|
|
||||||
onSelect={onSelect}
|
|
||||||
currentType={currentType}
|
|
||||||
/>
|
|
||||||
</ScrollArea.Autosize>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -217,7 +217,6 @@ export type UpdatePropertyInput = {
|
|||||||
propertyId: string;
|
propertyId: string;
|
||||||
baseId: string;
|
baseId: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
type?: BasePropertyType;
|
|
||||||
typeOptions?: TypeOptions;
|
typeOptions?: TypeOptions;
|
||||||
requestId?: string;
|
requestId?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user