Skip to content

Commit

Permalink
chore(web): update some ux of layers item (#1032)
Browse files Browse the repository at this point in the history
  • Loading branch information
airslice authored Jun 26, 2024
1 parent a1c5719 commit 37908a0
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 78 deletions.
99 changes: 67 additions & 32 deletions web/src/beta/features/Editor/Map/LayersPanel/LayerItem.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { Dispatch, FC, ReactNode, SetStateAction, useCallback, useMemo } from "react";
import {
Dispatch,
FC,
SetStateAction,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";

import { IconButton, TextInput } from "@reearth/beta/lib/reearth-ui";
import { EntryItem } from "@reearth/beta/ui/components";
import { EntryItem, EntryItemAction } from "@reearth/beta/ui/components";
import { NLSLayer } from "@reearth/services/api/layersApi/utils";
import { styled } from "@reearth/services/theme";

Expand Down Expand Up @@ -57,26 +66,33 @@ const LayerItem: FC<LayerItemProps> = ({
[layer.id, handleLayerDelete, setEditingLayerNameId],
);

const hoverActions: ReactNode[] | undefined = useMemo(
const hoverActions: EntryItemAction[] | undefined = useMemo(
() =>
editingLayerNameId !== layer.id
? [
!layer.isSketch && layer.visible && (
<IconButton
key="zoom"
icon="crosshair"
size="small"
appearance="simple"
onClick={handleZoomToLayer}
/>
),
<IconButton
key="visible"
icon={layer.visible ? "eye" : "eyeSlash"}
size="small"
appearance="simple"
onClick={handleToggleLayerVisibility}
/>,
{
comp: !layer.isSketch && layer.visible && (
<IconButton
key="zoom"
icon="crosshair"
size="small"
appearance="simple"
onClick={handleZoomToLayer}
/>
),
},
{
comp: (
<IconButton
key="visible"
icon={layer.visible ? "eye" : "eyeSlash"}
size="small"
appearance="simple"
onClick={handleToggleLayerVisibility}
/>
),
keepVisible: !layer.visible,
},
]
: undefined,
[
Expand All @@ -89,14 +105,32 @@ const LayerItem: FC<LayerItemProps> = ({
],
);

const handleTitleUpdate = useCallback(
(title: string) => {
setEditingLayerNameId("");
if (!title || title === layer.title) return;
handleLayerNameUpdate({ layerId: layer.id, name: title });
},
[layer.id, layer.title, handleLayerNameUpdate, setEditingLayerNameId],
);
const [localTitle, setLocalTitle] = useState(layer.title);

const handleTitleUpdate = useCallback(() => {
setEditingLayerNameId("");
if (!localTitle || localTitle === layer.title) return;
handleLayerNameUpdate({ layerId: layer.id, name: localTitle });
}, [layer.id, layer.title, localTitle, handleLayerNameUpdate, setEditingLayerNameId]);

const handleLayerItemClick = useCallback(() => {
if (layer.id === selectedLayerId) return;
handleLayerSelect(layer.id);
setEditingLayerNameId("");
}, [layer.id, selectedLayerId, handleLayerSelect, setEditingLayerNameId]);

useEffect(() => {
setLocalTitle(layer.title);
}, [layer.title]);

const handleTitleUpdateRef = useRef(handleTitleUpdate);
handleTitleUpdateRef.current = handleTitleUpdate;

useEffect(() => {
if (selectedLayerId !== layer.id) {
handleTitleUpdateRef.current();
}
}, [selectedLayerId, layer.id, handleTitleUpdateRef]);

return (
<EntryItem
Expand All @@ -106,23 +140,24 @@ const LayerItem: FC<LayerItemProps> = ({
size="small"
extendWidth
autoFocus
value={layer.title}
value={localTitle}
onChange={setLocalTitle}
onBlur={handleTitleUpdate}
/>
) : (
<TitleWrapper onDoubleClick={() => setEditingLayerNameId(layer.id)}>
{layer.title}
{localTitle}
</TitleWrapper>
)
}
icon={layer?.isSketch ? "pencilSimple" : "file"}
dragHandleClassName={dragHandleClassName}
onClick={() => handleLayerSelect(layer.id)}
highlight={layer.id === selectedLayerId}
onClick={handleLayerItemClick}
highlighted={layer.id === selectedLayerId}
disableHover={isDragging}
optionsMenu={optionsMenu}
optionsMenuWidth={100}
hoverActions={hoverActions}
actions={hoverActions}
/>
);
};
Expand Down
5 changes: 1 addition & 4 deletions web/src/beta/features/Editor/Map/LayersPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,10 @@ const LAYERS_DRAG_HANDLE_CLASS_NAME = "reearth-visualizer-editor-layers-drag-han
const LayersPanel: FC = () => {
const {
layers,
// selectedLayerId,
// handleLayerDelete,
handleLayerMove,
handleLayerSelect,
openDataSourceLayerCreator,
openSketchLayerCreator,
// handleFlyTo,
} = useMapPage();

const t = useT();
Expand Down Expand Up @@ -95,7 +92,7 @@ const LayersPanel: FC = () => {
handleClassName={LAYERS_DRAG_HANDLE_CLASS_NAME}
onMoveEnd={handleMoveEnd}
onMoveStart={handleMoveStart}
dragDisabled={!!editingLayerNameId}
dragDisabled={false}
/>
<EmptySpace onClick={() => handleLayerSelect(undefined)} />
</LayersContainer>
Expand Down
2 changes: 1 addition & 1 deletion web/src/beta/features/Editor/Map/ScenePanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const ScenePanel: FC = () => {
<EntryItem
key={index}
title={handleTranslatedCollectionName(collection as ScenePropertyCollection)}
highlight={selectedSceneSetting === collection}
highlighted={selectedSceneSetting === collection}
onClick={() => handleSceneSettingSelect(collection)}
/>
),
Expand Down
11 changes: 2 additions & 9 deletions web/src/beta/lib/reearth-ui/components/TextInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, useCallback, useEffect, useState, ChangeEvent, useRef } from "react";
import { FC, useCallback, useEffect, useState, ChangeEvent } from "react";

import { fonts, styled } from "@reearth/services/theme";

Expand Down Expand Up @@ -31,7 +31,6 @@ export const TextInput: FC<TextInputProps> = ({
}) => {
const [currentValue, setCurrentValue] = useState(value ?? "");
const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
setCurrentValue(value ?? "");
Expand All @@ -55,20 +54,13 @@ export const TextInput: FC<TextInputProps> = ({
setIsFocused(true);
}, []);

useEffect(() => {
if (autoFocus && inputRef.current) {
inputRef.current.focus();
}
}, [autoFocus]);

return (
<Wrapper
size={size}
appearance={appearance}
extendWidth={extendWidth}
status={isFocused ? "active" : "default"}>
<StyledInput
ref={inputRef}
value={currentValue}
placeholder={placeholder}
disabled={disabled}
Expand All @@ -77,6 +69,7 @@ export const TextInput: FC<TextInputProps> = ({
onBlur={handleBlur}
onFocus={handleFocus}
appearance={appearance}
autoFocus={autoFocus}
/>
{actions && (
<ActionsWrapper>
Expand Down
79 changes: 60 additions & 19 deletions web/src/beta/ui/components/EntryItem/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ export const Default: Story = {
},
};

export const Highlight: Story = {
export const Highlighted: Story = {
args: {
title: "Item",
highlight: true,
highlighted: true,
},
};

Expand All @@ -41,7 +41,7 @@ export const Icon: Story = {

export const LongText: Story = {
args: {
title: "ItemNameWithALongLong Long Long LongLongLongLongName",
title: "Item With A Long Long Long Long Long Long Long Long Name",
icon: "file",
},
};
Expand All @@ -65,24 +65,65 @@ export const OptionsMenu: Story = {
},
};

export const HoverActions: Story = {
export const Actions: Story = {
args: {
title: "Item",
hoverActions: [
<IconButton
key="1"
icon="setting"
size="small"
appearance="simple"
onClick={() => console.log("Action 1 clicked")}
/>,
<IconButton
key="2"
icon="pencilSimple"
size="small"
appearance="simple"
onClick={() => console.log("Action 2 clicked")}
/>,
actions: [
{
comp: (
<IconButton
key="1"
icon="setting"
size="small"
appearance="simple"
onClick={() => console.log("Action 1 clicked")}
/>
),
},
{
comp: (
<IconButton
key="2"
icon="pencilSimple"
size="small"
appearance="simple"
onClick={() => console.log("Action 2 clicked")}
/>
),
keepVisible: true,
},
],
},
};

export const DisableHover: Story = {
args: {
title: "Item",
disableHover: true,
actions: [
{
comp: (
<IconButton
key="1"
icon="setting"
size="small"
appearance="simple"
onClick={() => console.log("Action 1 clicked")}
/>
),
},
{
comp: (
<IconButton
key="2"
icon="pencilSimple"
size="small"
appearance="simple"
onClick={() => console.log("Action 2 clicked")}
/>
),
keepVisible: true,
},
],
},
};
26 changes: 13 additions & 13 deletions web/src/beta/ui/components/EntryItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,31 @@ import { FC, MouseEvent, ReactNode, useCallback, useEffect, useState } from "rea
import { Icon, IconButton, IconName, PopupMenu, PopupMenuItem } from "@reearth/beta/lib/reearth-ui";
import { styled } from "@reearth/services/theme";

export interface EntryItemAction {
comp: ReactNode;
keepVisible?: boolean;
}
export interface EntryItemProps {
title: ReactNode;
icon?: IconName;
dragHandleClassName?: string;
highlight?: boolean;
highlighted?: boolean;
disableHover?: boolean;
optionsMenu?: PopupMenuItem[];
optionsMenuWidth?: number;
hoverActions?: ReactNode[];
actions?: EntryItemAction[];
onClick?: (e: MouseEvent) => void;
}

export const EntryItem: FC<EntryItemProps> = ({
title,
icon,
dragHandleClassName,
highlight,
highlighted,
disableHover,
optionsMenu,
optionsMenuWidth,
hoverActions,
actions,
onClick,
}) => {
const [hovered, setHovered] = useState(false);
Expand All @@ -36,6 +40,8 @@ export const EntryItem: FC<EntryItemProps> = ({
setHovered(false);
}, []);

// disable hover is used for fix this issue:
// https://stackoverflow.com/questions/11989289/css-html5-hover-state-remains-after-drag-and-drop
useEffect(() => {
if (disableHover) {
setHovered(false);
Expand All @@ -52,7 +58,7 @@ export const EntryItem: FC<EntryItemProps> = ({
onMouseLeave={handleMouseLeave}
onClick={onClick}
hovered={hovered}
highlight={highlight}
highlight={highlighted}
smallPaddingRight={!!optionsMenu}>
<MainContent className={dragHandleClassName} asDragHandle={!!dragHandleClassName}>
{icon && (
Expand All @@ -63,8 +69,8 @@ export const EntryItem: FC<EntryItemProps> = ({
{typeof title === "string" ? <Title>{title}</Title> : title}
</MainContent>
<Actions>
<HoverActions>{hovered && hoverActions}</HoverActions>
{optionsMenu && (
{actions?.map(a => (highlighted || hovered || a.keepVisible) && a.comp)}
{!!optionsMenu && (
<OptionsWrapper onClick={handleOptionsClick}>
<PopupMenu
label={<IconButton icon="dotsThreeVertical" size="small" appearance="simple" />}
Expand Down Expand Up @@ -139,12 +145,6 @@ const Actions = styled("div")(({ theme }) => ({
gap: theme.spacing.smallest,
}));

const HoverActions = styled("div")(({ theme }) => ({
display: "flex",
flexShrink: 0,
gap: theme.spacing.smallest,
}));

const OptionsWrapper = styled("div")(() => ({
flexShrink: 0,
}));
Expand Down

0 comments on commit 37908a0

Please sign in to comment.