Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(web): refactor/update Camera field #692

Merged
merged 19 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions web/src/beta/components/Icon/Icons/zoomToLayer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions web/src/beta/components/Icon/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import Install from "./Icons/install.svg";
import UploadSimple from "./Icons/uploadSimple.svg";
import Search from "./Icons/search.svg";
import AddLayerIcon from "./Icons/addLayer.svg";
import ZoomToLayer from "./Icons/zoomToLayer.svg";

// MSIC
import CheckCircle from "./Icons/checkCircle.svg";
Expand Down Expand Up @@ -109,6 +110,7 @@ import Marketplace from "./Icons/marketplace.svg";

export default {
addLayer: AddLayerIcon,
zoomToLayer: ZoomToLayer,
file: File,
dl: InfoTable,
infobox: Infobox,
Expand Down
2 changes: 1 addition & 1 deletion web/src/beta/components/Popover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
export const Trigger = React.forwardRef<HTMLElement, React.HTMLProps<HTMLElement> & TriggerProps>(
function Trigger({ children, asChild = false, className, ...props }, propRef) {
const context = usePopoverContext();
const childrenRef = (children as any).ref;

Check warning on line 35 in web/src/beta/components/Popover/index.tsx

View workflow job for this annotation

GitHub Actions / ci-web / ci

Unexpected any. Specify a different type
const ref = useMergeRefs([context.refs.setReference, propRef, childrenRef]);

// `asChild` allows the user to pass any element as the anchor
Expand Down Expand Up @@ -83,7 +83,7 @@
<FloatingPortal
// whether to render this inside the Trigger or outside the main div
root={attachToRoot ? (context.refs.domReference as React.MutableRefObject<null>) : null}>
<FloatingFocusManager context={floatingContext} modal={context.modal}>
<FloatingFocusManager context={floatingContext} modal={context.modal} initialFocus={-1}>
<div
ref={ref}
className={className}
Expand Down
2 changes: 1 addition & 1 deletion web/src/beta/components/Slider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const SliderWithTooltip = RCSlider.createSliderWithTooltip(RCSlider);
export type Props = {
min?: number;
max?: number;
} & Omit<ComponentProps<typeof SliderWithTooltip>, "defaultValue">;
} & ComponentProps<typeof SliderWithTooltip>;

const Slider: React.FC<Props> = ({ ...props }) => (
<SliderStyled disabled={props.disabled as boolean}>
Expand Down
23 changes: 7 additions & 16 deletions web/src/beta/components/Toggle/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { useCallback } from "react";

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

export type Props = {
Expand All @@ -8,20 +6,13 @@ export type Props = {
disabled?: boolean;
};

const Toggle: React.FC<Props> = ({ checked, onChange, disabled = false }) => {
const handleClick = useCallback(
() => !disabled && onChange(checked),
[checked, onChange, disabled],
);

return (
<Wrapper>
<Switch checked={checked} disabled={disabled} onClick={handleClick}>
<TopSlider checked={checked} />
</Switch>
</Wrapper>
);
};
const Toggle: React.FC<Props> = ({ checked, onChange, disabled = false }) => (
<Wrapper>
<Switch checked={checked} disabled={disabled} onClick={() => onChange(!checked)}>
<TopSlider checked={checked} />
</Switch>
</Wrapper>
);

export default Toggle;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { useMemo } from "react";

import Button from "@reearth/beta/components/Button";
import NumberInput from "@reearth/beta/components/fields/common/NumberInput";
import Text from "@reearth/beta/components/Text";
import { Camera } from "@reearth/beta/utils/value";
import { useT } from "@reearth/services/i18n";
import { styled } from "@reearth/services/theme";

import PanelCommon from "../PanelCommon";
import type { RowType } from "../types";

type Props = {
camera?: Camera;
onSave: (value?: Camera) => void;
onClose: () => void;
};

const CapturePanel: React.FC<Props> = ({ camera, onSave, onClose }) => {
const t = useT();

const panelContent: { [key: string]: RowType } = useMemo(() => {
return {
[t("Current Position")]: [
{ id: "lat", description: t("Latitude") },
{ id: "lng", description: t("Longitude") },
{ id: "height", description: t("Height") },
],
[t("Current Rotation")]: [
{ id: "heading", description: t("Heading") },
{ id: "pitch", description: t("Pitch") },
{ id: "roll", description: t("Roll") },
],
};
}, [t]);

return (
<PanelCommon title={t("Camera Position Editor")} onClose={onClose}>
{Object.keys(panelContent).map(group => (
<FieldGroup key={group}>
<Text size="footnote">{group}</Text>
<InputWrapper>
{panelContent[group].map(field => (
<StyledNumberInput
key={field.id}
inputDescription={field.description}
value={camera?.[field.id]}
disabled
/>
))}
</InputWrapper>
</FieldGroup>
))}
<Divider />
<ButtonWrapper>
<StyledButton text={t("Cancel")} size="small" onClick={onClose} />
<StyledButton
text={t("Capture")}
size="small"
buttonType="primary"
onClick={() => onSave(camera)}
/>
</ButtonWrapper>
</PanelCommon>
);
};

export default CapturePanel;

const FieldGroup = styled.div`
display: flex;
justify-content: space-between;
flex-direction: column;
gap: 4px;
padding: 4px;
`;

const InputWrapper = styled.div`
display: flex;
gap: 4px;
`;

const Divider = styled.div`
border-top: 1px solid ${({ theme }) => theme.outline.weak};
`;

const ButtonWrapper = styled.div`
display: flex;
gap: 8px;
padding: 8px;
`;

const StyledButton = styled(Button)`
flex: 1;
`;

const StyledNumberInput = styled(NumberInput)`
flex: 1;
`;
42 changes: 42 additions & 0 deletions web/src/beta/components/fields/CameraField/EditPanel/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useCallback, useMemo } from "react";

import { useT } from "@reearth/services/i18n";

import type { Camera, RowType } from "../types";

export default ({
camera,
onChange,
}: {
camera?: Camera;
onChange?: (key: keyof Camera, update?: number) => void;
}) => {
const t = useT();

const panelContent: { [key: string]: RowType } = useMemo(() => {
return {
[t("Location")]: [
{ id: "lat", description: t("Latitude") },
{ id: "lng", description: t("Longitude") },
],
[t("Height")]: [{ id: "height", suffix: "km" }],
[t("Rotation")]: [
{ id: "heading", description: t("Heading") },
{ id: "pitch", description: t("Pitch") },
{ id: "roll", description: t("Roll") },
],
};
}, [t]);

const handleChange = useCallback(
(field: keyof Camera) => (value?: number) => {
if (value === camera?.[field]) return;
onChange?.(field, value);
},
[camera, onChange],
);
return {
panelContent,
handleChange,
};
};
87 changes: 87 additions & 0 deletions web/src/beta/components/fields/CameraField/EditPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import Button from "@reearth/beta/components/Button";
import NumberInput from "@reearth/beta/components/fields/common/NumberInput";
import Text from "@reearth/beta/components/Text";
import type { Camera } from "@reearth/beta/utils/value";
import { useT } from "@reearth/services/i18n";
import { styled } from "@reearth/services/theme";

import PanelCommon from "../PanelCommon";

import useHooks from "./hooks";

type Props = {
camera?: Camera;
onSave: (value?: Camera) => void;
onChange?: (key: keyof Camera, update?: number) => void;
onClose: () => void;
};

const EditPanel: React.FC<Props> = ({ camera, onSave, onChange, onClose }) => {
const t = useT();

const { panelContent, handleChange } = useHooks({ camera, onChange });

return (
<PanelCommon title={t("Camera Position Editor")} onClose={onClose}>
{Object.keys(panelContent).map(group => (
<FieldGroup key={group}>
<Text size="footnote">{group}</Text>
<InputWrapper>
{panelContent[group].map(field => (
<StyledNumberInput
key={field.id}
inputDescription={field.description}
value={camera?.[field.id]}
suffix={field.suffix}
onChange={handleChange(field.id)}
/>
))}
</InputWrapper>
</FieldGroup>
))}
<Divider />
<ButtonWrapper>
<StyledButton text={t("Cancel")} size="small" onClick={onClose} />
<StyledButton
text={t("Apply")}
size="small"
buttonType="primary"
onClick={() => onSave(camera)}
/>
</ButtonWrapper>
</PanelCommon>
);
};

export default EditPanel;

const FieldGroup = styled.div`
display: flex;
justify-content: space-between;
flex-direction: column;
gap: 4px;
padding: 4px;
`;

const InputWrapper = styled.div`
display: flex;
gap: 4px;
`;

const Divider = styled.div`
border-top: 1px solid ${({ theme }) => theme.outline.weak};
`;

const ButtonWrapper = styled.div`
display: flex;
gap: 8px;
padding: 8px;
`;

const StyledButton = styled(Button)`
flex: 1;
`;

const StyledNumberInput = styled(NumberInput)`
flex: 1;
`;
60 changes: 60 additions & 0 deletions web/src/beta/components/fields/CameraField/PanelCommon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ReactNode } from "react";

import Icon from "@reearth/beta/components/Icon";
import Text from "@reearth/beta/components/Text";
import { styled, useTheme } from "@reearth/services/theme";

type Props = {
title: string;
children: ReactNode;
onClose: () => void;
};

const PanelCommon: React.FC<Props> = ({ title, children, onClose }) => {
const theme = useTheme();
return (
<Wrapper>
<HeaderWrapper>
<PickerTitle size="footnote" weight="regular" color={theme.content.main}>
{title}
</PickerTitle>
<CloseIcon icon="cancel" size={12} onClick={onClose} />
</HeaderWrapper>
<Content>{children}</Content>
</Wrapper>
);
};

export default PanelCommon;

const Wrapper = styled.div`
width: 286px;
border: 1px solid ${({ theme }) => theme.outline.weak};
border-radius: 4px;
background: ${({ theme }) => theme.bg[1]};
box-shadow: 4px 4px 4px 0px rgba(0, 0, 0, 0.25);
display: flex;
flex-direction: column;
justify-content: space-between;
`;

const HeaderWrapper = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
padding: 4px 8px;
border-bottom: 1px solid ${({ theme }) => theme.outline.weak};
`;

const PickerTitle = styled(Text)`
text-align: center;
margin-right: auto;
`;

const CloseIcon = styled(Icon)`
margin-left: auto;
cursor: pointer;
`;

const Content = styled.div``;
Loading
Loading