Skip to content

Commit

Permalink
refactor(web): new field components (#1017)
Browse files Browse the repository at this point in the history
Co-authored-by: mkumbobeaty <[email protected]>
Co-authored-by: airslice <[email protected]>
  • Loading branch information
3 people authored Jul 12, 2024
1 parent 8e3d8bc commit 6c6734b
Show file tree
Hide file tree
Showing 39 changed files with 2,297 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default ({
{ id: "lat", description: t("Latitude") },
{ id: "lng", description: t("Longitude") },
],
[t("Height")]: [{ id: "height", suffix: "km" }],
[t("Height")]: [{ id: "height", unit: "km", description: t("Height") }],
[t("Rotation")]: [
{ id: "heading", description: t("Heading") },
{ id: "pitch", description: t("Pitch") },
Expand Down
8 changes: 7 additions & 1 deletion web/src/beta/components/fields/CameraField/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ import type { Camera } from "@reearth/beta/utils/value";

export type { Camera } from "@reearth/beta/utils/value";

export type RowType = { id: keyof Camera; value?: number; description?: string; suffix?: string }[];
export type RowType = {
id: keyof Camera;
value?: number;
description?: string;
suffix?: string;
unit?: string;
}[];
4 changes: 2 additions & 2 deletions web/src/beta/lib/reearth-ui/components/DatePicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ export const DatePicker: FC<DatePickerProps> = ({ value, disabled, onChange, onB
(e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const newValue = e.currentTarget.value;
setCurrentValue(newValue ?? "");
onChange?.(currentValue);
onChange?.(newValue);
},
[currentValue, onChange],
[onChange],
);

const handleBlur = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const Title = styled("div")(() => ({

const Content = styled("div")(() => ({
alignSelf: "stretch",
userSelect: "none",
}));

const ActionWrapper = styled("div")(({ theme }) => ({
Expand Down
14 changes: 10 additions & 4 deletions web/src/beta/lib/reearth-ui/components/NumberInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type NumberInputProps = {
disabled?: boolean;
placeholder?: string;
appearance?: "readonly";
extendWidth?: boolean;
unit?: string;
min?: number;
max?: number;
Expand All @@ -21,6 +22,7 @@ export const NumberInput: FC<NumberInputProps> = ({
disabled,
placeholder,
appearance,
extendWidth,
unit,
min,
max,
Expand Down Expand Up @@ -50,9 +52,8 @@ export const NumberInput: FC<NumberInputProps> = ({
validatedValue = currentValue;
}
}

setCurrentValue(validatedValue);
onChange?.(parseFloat(validatedValue));
onChange?.(currentValue === "" ? undefined : parseFloat(validatedValue));
}
},
[max, min, onChange],
Expand All @@ -76,7 +77,7 @@ export const NumberInput: FC<NumberInputProps> = ({
}, []);

return (
<Wrapper size={size} status={isFocused ? "active" : "default"}>
<Wrapper size={size} status={isFocused ? "active" : "default"} extendWidth={extendWidth}>
<StyledInput
value={currentValue}
disabled={disabled}
Expand All @@ -94,20 +95,24 @@ export const NumberInput: FC<NumberInputProps> = ({
const Wrapper = styled("div")<{
size: "normal" | "small";
status: "default" | "active";
}>(({ size, theme, status }) => {
extendWidth?: boolean;
}>(({ size, theme, status, extendWidth }) => {
return {
border:
status === "active" ? `1px solid ${theme.select.main}` : `1px solid ${theme.outline.weak}`,
borderRadius: theme.radius.small,
background: theme.bg[1],
display: "flex",
flex: 1,
gap: theme.spacing.smallest,
alignItems: "center",
padding:
size === "small"
? `0 ${theme.spacing.smallest}px`
: `${theme.spacing.smallest}px ${theme.spacing.small}px`,
boxShadow: theme.shadow.input,
width: !extendWidth ? "" : "100%",
boxSizing: "border-box",
};
});

Expand All @@ -124,6 +129,7 @@ const StyledInput = styled("input")<{
fontSize: fonts.sizes.body,
lineHeight: `${fonts.lineHeights.body}px`,
textOverflow: "ellipsis",
pointerEvents: disabled ? "none" : "inherit",
overflow: "hidden",
"::placeholder": {
color: theme.content.weak,
Expand Down
11 changes: 8 additions & 3 deletions web/src/beta/lib/reearth-ui/components/Selector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type SelectorProps = {
options: { value: string; label?: string }[];
disabled?: boolean;
placeholder?: string;
maxHeight?: number;
onChange?: (value: string | string[]) => void;
};

Expand All @@ -22,6 +23,7 @@ export const Selector: FC<SelectorProps> = ({
options,
placeholder = "Please select",
disabled,
maxHeight,
onChange,
}) => {
const theme = useTheme();
Expand Down Expand Up @@ -151,7 +153,7 @@ export const Selector: FC<SelectorProps> = ({
onOpenChange={setIsOpen}
disabled={disabled}
placement="bottom-start">
<DropDownWrapper width={selectorWidth}>
<DropDownWrapper maxHeight={maxHeight} width={selectorWidth}>
{optionValues.length === 0 ? (
<DropDownItem>
<Typography size="body" color={theme.content.weaker}>
Expand Down Expand Up @@ -203,7 +205,7 @@ const SelectInput = styled("div")<{
}px`,
cursor: disabled ? "not-allowed" : "pointer",
minWidth: width ? `${width}px` : "fit-content",
minHeight: "32px",
height: "32px",
}));

const SelectedItems = styled("div")(({ theme }) => ({
Expand All @@ -225,7 +227,8 @@ const SelectedItem = styled("div")(({ theme }) => ({

const DropDownWrapper = styled("div")<{
width?: number;
}>(({ width, theme }) => ({
maxHeight?: number;
}>(({ width, maxHeight, theme }) => ({
boxSizing: "border-box",
display: "flex",
flexDirection: "column",
Expand All @@ -236,6 +239,8 @@ const DropDownWrapper = styled("div")<{
borderRadius: `${theme.radius.small}px`,
width: width ? `${width}px` : "",
border: `1px solid ${theme.outline.weaker}`,
maxHeight: maxHeight ? `${maxHeight}px` : "",
overflowY: maxHeight ? "auto" : "hidden",
}));

const DropDownItem = styled("div")<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const UsecaseReadonly: Story = {
},
};
// TODO: use IconButton instead of MockButton
const MockButton: FC = () => <Icon icon="settings" size={12} />;
const MockButton: FC = () => <Icon icon="settings" size={12} color="#525252" />;

export const Actions: Story = {
args: {
Expand Down
13 changes: 11 additions & 2 deletions web/src/beta/lib/reearth-ui/components/TextInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type TextInputProps = {
extendWidth?: boolean;
autoFocus?: boolean;
actions?: FC[];
leftAction?: FC[];
onChange?: (text: string) => void;
onBlur?: (text: string) => void;
};
Expand All @@ -25,6 +26,7 @@ export const TextInput: FC<TextInputProps> = ({
appearance,
extendWidth,
actions,
leftAction,
autoFocus,
onChange,
onBlur,
Expand Down Expand Up @@ -60,6 +62,13 @@ export const TextInput: FC<TextInputProps> = ({
appearance={appearance}
extendWidth={extendWidth}
status={isFocused || autoFocus ? "active" : "default"}>
{leftAction && (
<ActionsWrapper>
{leftAction.map((Action, i) => (
<Action key={i} />
))}
</ActionsWrapper>
)}
<StyledInput
value={currentValue}
placeholder={placeholder}
Expand Down Expand Up @@ -102,6 +111,7 @@ const Wrapper = styled("div")<{
borderRadius: theme.radius.small,
background: appearance === "present" ? "" : theme.bg[1],
display: "flex",
flex: 1,
gap: `${theme.spacing.smallest}px`,
alignItems: "center",
padding:
Expand Down Expand Up @@ -131,6 +141,7 @@ const StyledInput = styled("input")<{
lineHeight: `${fonts.lineHeights.body}px`,
textOverflow: "ellipsis",
pointerEvents: disabled ? "none" : "inherit",
width: "100%",
overflow: "hidden",
"::placeholder": {
color: theme.content.weak,
Expand All @@ -142,6 +153,4 @@ const ActionsWrapper = styled("div")(({ theme }) => ({
alignItems: "center",
gap: `${theme.spacing.smallest}px`,
flexShrink: 0,
padding: theme.spacing.micro,
color: theme.content.weak,
}));
4 changes: 2 additions & 2 deletions web/src/beta/lib/reearth-ui/components/TimePicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ export const TimePicker: FC<TimePickerProps> = ({ value, disabled, onChange, onB
(e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const newValue = e.currentTarget.value;
setCurrentValue(newValue ?? "");
onChange?.(currentValue);
onChange?.(newValue);
},
[currentValue, onChange],
[onChange],
);

const handleBlur = useCallback(() => {
Expand Down
1 change: 1 addition & 0 deletions web/src/beta/ui/components/EntryItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ const Wrapper = styled("div")<{
borderRadius: theme.radius.small,
backgroundColor: "transparent",
minHeight: 28,
width: "100%",
cursor: "pointer",
...(hovered && {
backgroundColor: theme.bg[1],
Expand Down
136 changes: 136 additions & 0 deletions web/src/beta/ui/fields/AssetField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { FC, useCallback, useEffect, useState } from "react";

import { FILE_FORMATS, IMAGE_FORMATS } from "@reearth/beta/features/Assets/constants";
import { AcceptedFileFormat } from "@reearth/beta/features/Assets/types";
import AssetModal from "@reearth/beta/features/Modals/AssetModal";
import LayerStyleModal from "@reearth/beta/features/Modals/LayerStyleModal";
import useFileUploaderHook from "@reearth/beta/hooks/useAssetUploader/hooks";
import { TextInput, Button } from "@reearth/beta/lib/reearth-ui";
import { checkIfFileType } from "@reearth/beta/utils/util";
import { useT } from "@reearth/services/i18n";
import { useNotification, useWorkspace } from "@reearth/services/state";
import { styled } from "@reearth/services/theme";

import CommonField, { CommonFieldProps } from "./CommonField";

export type AssetFieldProps = CommonFieldProps & {
value?: string;
fileType?: "asset" | "URL" | "layerStyle";
entityType?: "image" | "file" | "layerStyle";
fileFormat?: AcceptedFileFormat;
sceneId?: string;
placeholder?: string;
onChange?: (value: string | undefined, name: string | undefined) => void;
};

const AssetField: FC<AssetFieldProps> = ({
commonTitle,
description,
value,
entityType,
fileType,
fileFormat,
sceneId,
placeholder,
onChange,
}) => {
const t = useT();
const [open, setOpen] = useState(false);
const [currentWorkspace] = useWorkspace();
const [, setNotification] = useNotification();
const [currentValue, setCurrentValue] = useState(value);

const handleChange = useCallback(
(inputValue?: string, name?: string) => {
if (!inputValue) {
setCurrentValue(inputValue);
onChange?.(inputValue, name);
} else if (
fileType === "asset" &&
!(checkIfFileType(inputValue, FILE_FORMATS) || checkIfFileType(inputValue, IMAGE_FORMATS))
) {
setNotification({
type: "error",
text: t("Wrong file format"),
});
setCurrentValue(undefined);
} else {
setCurrentValue(inputValue);
onChange?.(inputValue, name);
}
},
[fileType, onChange, setNotification, t],
);

const { handleFileUpload } = useFileUploaderHook({
workspaceId: currentWorkspace?.id,
onAssetSelect: handleChange,
assetType: entityType,
fileFormat,
});

useEffect(() => {
setCurrentValue(value ?? "");
}, [value]);

const handleClick = useCallback(() => setOpen(!open), [open]);
const handleModalClose = useCallback(() => setOpen(false), []);

return (
<CommonField commonTitle={commonTitle} description={description}>
<AssetWrapper>
<TextInput
value={currentValue}
onChange={handleChange}
placeholder={placeholder ?? t("Not set")}
/>
{fileType === "asset" && (
<ButtonWrapper>
<Button icon={"image"} size="small" title="Choose" onClick={handleClick} extendWidth />
<Button
icon={"uploadSimple"}
size="small"
title="Upload"
onClick={handleFileUpload}
extendWidth
/>
</ButtonWrapper>
)}
</AssetWrapper>
{open && entityType !== "layerStyle" && (
<AssetModal
open={open}
onModalClose={handleModalClose}
assetType={entityType}
currentWorkspace={currentWorkspace}
currentValue={currentValue}
fileFormat={fileFormat}
onSelect={handleChange}
/>
)}
{open && entityType === "layerStyle" && (
<LayerStyleModal
open={open}
sceneId={sceneId}
onClose={handleModalClose}
onSelect={handleChange}
/>
)}
</CommonField>
);
};

export default AssetField;

const AssetWrapper = styled("div")<{}>(({ theme }) => ({
display: "flex",
flexDirection: "column",
gap: `${theme.spacing.smallest}px`,
width: "100%",
}));

const ButtonWrapper = styled("div")(({ theme }) => ({
display: "flex",
flexDirection: "row",
gap: `${theme.spacing.smallest}px`,
}));
Loading

0 comments on commit 6c6734b

Please sign in to comment.