Skip to content

Commit

Permalink
chore(web): add support for layer name editing in beta (#665)
Browse files Browse the repository at this point in the history
Co-authored-by: KaWaite <[email protected]>
  • Loading branch information
pyshx and KaWaite authored Sep 12, 2023
1 parent dfa05b1 commit 02cb8d1
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 104 deletions.
10 changes: 3 additions & 7 deletions web/src/beta/components/ListItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Placement } from "@floating-ui/react";
import { FC, ReactNode } from "react";
import { FC, MouseEvent, ReactNode } from "react";

import Icon from "@reearth/beta/components/Icon";
import * as Popover from "@reearth/beta/components/Popover";
Expand All @@ -16,7 +16,7 @@ type Props = {
isOpenAction?: boolean;
actionPlacement?: Placement;
clamp?: Clamp;
onItemClick: (id: string) => void;
onItemClick: (e?: MouseEvent<Element>) => void;
onActionClick?: () => void;
onOpenChange?: (isOpen: boolean) => void;
};
Expand All @@ -35,11 +35,7 @@ const ListItem: FC<Props> = ({
}) => {
return (
<Wrapper>
<Inner
border={border}
isSelected={isSelected}
clamp={clamp}
onClick={() => onItemClick("id")}>
<Inner border={border} isSelected={isSelected} clamp={clamp} onClick={onItemClick}>
<StyledText size="footnote">{children}</StyledText>
</Inner>
{actionContent && (
Expand Down
5 changes: 3 additions & 2 deletions web/src/beta/components/SidePanelSectionField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import Icon from "../Icon";
import Text from "../Text";

const SidePanelSectionField: React.FC<{
className?: string;
title?: string;
children?: ReactNode;
}> = ({ title, children }) => {
}> = ({ className, title, children }) => {
const theme = useTheme();
const [opened, setOpened] = useState(true);

return (
<Field>
<Field className={className}>
{title && (
<Header onClick={() => setOpened(!opened)}>
<Text size="body" color={theme.content.main}>
Expand Down
30 changes: 23 additions & 7 deletions web/src/beta/components/fields/common/TextInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@ export type Props = {
value?: string;
placeholder?: string;
timeout?: number;
autoFocus?: boolean;
onChange?: (text: string) => void;
onBlur?: () => void;
onExit?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
};

const TextInput: React.FC<Props> = ({ value, placeholder, timeout = 1000, onChange }) => {
const TextInput: React.FC<Props> = ({
value,
placeholder,
timeout = 1000,
autoFocus,
onChange,
onBlur,
onExit,
}) => {
const [currentValue, setCurrentValue] = useState(value ?? "");
const timeoutRef = useRef<NodeJS.Timeout>();

Expand All @@ -35,24 +46,29 @@ const TextInput: React.FC<Props> = ({ value, placeholder, timeout = 1000, onChan
const handleBlur = useCallback(() => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
onChange?.(currentValue);
}, [currentValue, onChange]);
onBlur?.();
}, [currentValue, onChange, onBlur]);

const handleKeyDown = useCallback(
const handleExit = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (onChange && e.key === "Enter" && currentValue !== value) {
onChange(currentValue);
if (e.key === "Escape") {
onExit?.(e);
} else if (e.key === "Enter" || e.key === "Return") {
onChange?.(currentValue);
onExit?.(e);
}
},
[value, currentValue, onChange],
[currentValue, onChange, onExit],
);

return (
<StyledInput
value={currentValue ?? ""}
placeholder={placeholder}
autoFocus={autoFocus}
onChange={handleChange}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
onKeyUp={handleExit}
/>
);
};
Expand Down
2 changes: 1 addition & 1 deletion web/src/beta/components/fields/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Props = {
const Property: React.FC<Props> = ({ name, description, children }) => {
return (
<Wrapper>
<Text size="footnote">{name ?? "Unknown field"}</Text>
{name && <Text size="footnote">{name}</Text>}
{children}
{description && (
<Description size="xFootnote" customColor>
Expand Down
17 changes: 12 additions & 5 deletions web/src/beta/features/Editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,23 @@ const Editor: React.FC<Props> = ({ sceneId, projectId, workspaceId, tab, stories
stories,
});

const { nlsLayers, selectedLayer, handleLayerAdd, handleLayerDelete, handleLayerSelect } =
useLayers({
sceneId,
});
const {
nlsLayers,
selectedLayer,
handleLayerAdd,
handleLayerDelete,
handleLayerSelect,
handleLayerNameUpdate,
} = useLayers({
sceneId,
});

const { leftPanel } = useLeftPanel({
tab,
sceneId,
nlsLayers,
selectedStory,
selectedLayer,
selectedLayerId: selectedLayer?.id,
currentPage,
onCurrentPageChange: handleCurrentPageChange,
onPageDuplicate: handlePageDuplicate,
Expand All @@ -80,6 +86,7 @@ const Editor: React.FC<Props> = ({ sceneId, projectId, workspaceId, tab, stories
onPageMove: handlePageMove,
onLayerDelete: handleLayerDelete,
onLayerSelect: handleLayerSelect,
onLayerNameUpdate: handleLayerNameUpdate,
onDataSourceManagerOpen: handleDataSourceManagerOpener,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import SidePanelSectionField from "@reearth/beta/components/SidePanelSectionField";
import Text from "@reearth/beta/components/Text";
import type { LayerNameUpdateProps } from "@reearth/beta/features/Editor/useLayers";
import type { NLSLayer } from "@reearth/services/api/layersApi/utils";
import { PropertySchemaGroup } from "@reearth/services/gql";
import { useT } from "@reearth/services/i18n";
Expand All @@ -10,37 +11,43 @@ import Layers from "../Layers";
type GroupSectionFieldProps = {
groups: PropertySchemaGroup[];
layers: NLSLayer[];
selectedLayerId?: string;
onLayerDelete: (id: string) => void;
onLayerNameUpdate: (inp: LayerNameUpdateProps) => void;
onLayerSelect: (id: string) => void;
onDataSourceManagerOpen: () => void;
};

const GroupSectionField: React.FC<GroupSectionFieldProps> = ({
groups,
layers,
selectedLayerId,
onLayerDelete,
onLayerNameUpdate,
onLayerSelect,
onDataSourceManagerOpen,
}) => {
const t = useT();

return (
<>
<SidePanelSectionField title={t("Scene")}>
<StyledSidePanelSectionField title={t("Scene")}>
{groups.map(({ schemaGroupId, title }) => (
<GroupSectionFieldText key={schemaGroupId} size="footnote">
{title}
</GroupSectionFieldText>
))}
</SidePanelSectionField>
<SidePanelSectionField title={t("Layers")}>
</StyledSidePanelSectionField>
<StyledSidePanelSectionField title={t("Layers")}>
<Layers
layers={layers}
selectedLayerId={selectedLayerId}
onLayerDelete={onLayerDelete}
onLayerNameUpdate={onLayerNameUpdate}
onLayerSelect={onLayerSelect}
onDataSourceManagerOpen={onDataSourceManagerOpen}
/>
</SidePanelSectionField>
</StyledSidePanelSectionField>
</>
);
};
Expand All @@ -51,4 +58,10 @@ const GroupSectionFieldText = styled(Text)`
cursor: pointer;
`;

const StyledSidePanelSectionField = styled(SidePanelSectionField)`
background: inherit;
border-bottom: 1px solid ${({ theme }) => theme.outline.weaker};
border-radius: 0;
`;

export default GroupSectionField;
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { MouseEvent, useCallback, useState } from "react";

import TextInput from "@reearth/beta/components/fields/common/TextInput";
import ListItem from "@reearth/beta/components/ListItem";
import PopoverMenuContent from "@reearth/beta/components/PopoverMenuContent";
import Text from "@reearth/beta/components/Text";
import type { LayerNameUpdateProps } from "@reearth/beta/features/Editor/useLayers";

type LayerItemProps = {
id: string;
layerTitle: string;
isSelected: boolean;
onDelete: () => void;
onSelect: () => void;
onLayerNameUpdate: (inp: LayerNameUpdateProps) => void;
};

const LayerItem = ({
id,
layerTitle,
isSelected,
onDelete,
onSelect,
onLayerNameUpdate,
}: LayerItemProps) => {
const [isActionOpen, setActionOpen] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [newValue, setNewValue] = useState(layerTitle);

const handleActionMenuToggle = useCallback(() => setActionOpen(prev => !prev), []);

const handleClick = useCallback(
(e?: MouseEvent<Element>) => {
if (e?.shiftKey) {
e?.stopPropagation();
setIsEditing(true);
return;
}
onSelect();
},
[onSelect],
);

const handleChange = useCallback((newTitle: string) => setNewValue(newTitle), []);

const handleTitleSubmit = useCallback(() => {
setIsEditing(false);
onLayerNameUpdate({ layerId: id, name: newValue });
}, [id, newValue, onLayerNameUpdate]);

const handleEditExit = useCallback(
(e?: React.KeyboardEvent<HTMLInputElement>) => {
if (layerTitle !== newValue && e?.key !== "Escape") {
handleTitleSubmit();
} else {
setNewValue(layerTitle);
}
setIsEditing(false);
},
[layerTitle, newValue, handleTitleSubmit],
);

return (
<ListItem
isSelected={isSelected}
isOpenAction={isActionOpen}
actionPlacement="bottom-start"
onItemClick={handleClick}
onActionClick={handleActionMenuToggle}
onOpenChange={isOpen => setActionOpen(!!isOpen)}
actionContent={
<PopoverMenuContent
size="sm"
items={[
{
name: "Delete",
icon: "bin",
onClick: onDelete,
},
]}
/>
}>
{isEditing ? (
<TextInput
value={newValue}
timeout={0}
autoFocus
onChange={handleChange}
onExit={handleEditExit}
onBlur={handleEditExit}
/>
) : (
<Text size="body">{layerTitle}</Text>
)}
</ListItem>
);
};

export default LayerItem;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Meta, StoryObj } from "@storybook/react";

import type { LayerNameUpdateProps } from "@reearth/beta/features/Editor/useLayers";
import type { NLSLayer } from "@reearth/services/api/layersApi/utils";

import Layers from ".";
Expand All @@ -15,6 +16,7 @@ function LeftPanelLayers() {
<Layers
layers={layers}
onLayerDelete={(_id: string) => {}}
onLayerNameUpdate={(_inp: LayerNameUpdateProps) => {}}
onLayerSelect={(_id: string) => {}}
onDataSourceManagerOpen={() => {}}
/>
Expand Down
Loading

0 comments on commit 02cb8d1

Please sign in to comment.