Skip to content

Commit

Permalink
fix(web): time disappear on cancel timeline popup (#1220)
Browse files Browse the repository at this point in the history
Co-authored-by: lby <[email protected]>
  • Loading branch information
mkumbobeaty and airslice authored Nov 8, 2024
1 parent 3016a71 commit 6a96cb9
Show file tree
Hide file tree
Showing 19 changed files with 372 additions and 404 deletions.
1 change: 0 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@
"lexical": "0.12.2",
"localforage": "1.10.0",
"lodash-es": "4.17.21",
"moment-timezone": "0.5.45",
"quickjs-emscripten": "0.24.0",
"quickjs-emscripten-sync": "1.5.2",
"rc-slider": "9.7.5",
Expand Down
8 changes: 2 additions & 6 deletions web/src/beta/features/Visualizer/Crust/StoryPanel/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IconName } from "@reearth/beta/lib/reearth-ui";
import { getTimeZone } from "@reearth/beta/utils/time";
import { ValueTypes, ValueType } from "@reearth/beta/utils/value";
import type { Spacing } from "@reearth/core";
import type { Item } from "@reearth/services/api/propertyApi/utils";
Expand Down Expand Up @@ -190,18 +191,13 @@ export const convertPositionToTime = (
return Math.min(Math.max(start, start + sec), end);
};

export const getTimeZone = (time: string) => {
const zone = time.match(/([-+]\d{1,2}:\d{2})$/);
const timezoneOffset = zone?.[1];
return timezoneOffset || "";
};

export const formatISO8601 = (time: string) => {
// For backforad compatibility
// from: 2021-08-31T00:00:00 +9:00
// from: 2021-08-31T00:00:00+9:00
// to: 2021-08-31T00:00:00+09:00
const timezone = getTimeZone(time);
if (!timezone) return time;
const splitZone = timezone.split(":");
if (splitZone[0].length === 2) {
return time
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getTimeZone } from "@reearth/beta/utils/time";
import {
useVisualizer,
TickEventCallback,
Expand All @@ -10,8 +11,7 @@ import {
convertOptionToSeconds,
formatDateToSting,
formatISO8601,
formatTimezone,
getTimeZone
formatTimezone
} from "../../Crust/StoryPanel/utils";

export const getNewDate = (d?: Date) => d ?? new Date();
Expand Down Expand Up @@ -165,7 +165,7 @@ export default (timelineValues?: TimelineValues) => {
const iso8601Time = formatISO8601(timelineValues?.currentTime);
const t = getNewDate(new Date(iso8601Time)).getTime();
const timezoneOffset = getTimeZone(iso8601Time);
setTimezone(timezoneOffset);
setTimezone(timezoneOffset ?? "");
return setCurrentTime(t);
} else {
return setCurrentTime(
Expand Down
37 changes: 37 additions & 0 deletions web/src/beta/lib/reearth-ui/components/ClickAway/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FC, ReactNode, useEffect, useRef } from "react";

type ClickAwayProps = {
children: ReactNode;
onClickAway?: () => void;
};

export const ClickAway: FC<ClickAwayProps> = ({ children, onClickAway }) => {
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (!onClickAway) return;

const handleClickOutside = (event: MouseEvent | TouchEvent) => {
if (
containerRef.current &&
!containerRef.current.contains(event.target as Node)
) {
onClickAway?.();
}
};

document.addEventListener("mousedown", handleClickOutside, {
passive: true
});
document.addEventListener("touchstart", handleClickOutside, {
passive: true
});

return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("touchstart", handleClickOutside);
};
}, [onClickAway]);

return <div ref={containerRef}>{children}</div>;
};
32 changes: 17 additions & 15 deletions web/src/beta/lib/reearth-ui/components/PopupPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button } from "@reearth/beta/lib/reearth-ui";
import { Button, ClickAway } from "@reearth/beta/lib/reearth-ui";
import { fonts, styled } from "@reearth/services/theme";
import { FC, ReactNode } from "react";

Expand All @@ -20,20 +20,22 @@ export const PopupPanel: FC<PopupPanelProps> = ({
onCancel
}) => {
return (
<Wrapper width={width}>
<HeaderWrapper>
<Title>{title}</Title>
<Button
iconButton
icon="close"
size="small"
onClick={onCancel}
appearance="simple"
/>
</HeaderWrapper>
<Content>{children}</Content>
{actions && <ActionWrapper>{actions}</ActionWrapper>}
</Wrapper>
<ClickAway onClickAway={onCancel}>
<Wrapper width={width}>
<HeaderWrapper>
<Title>{title}</Title>
<Button
iconButton
icon="close"
size="small"
onClick={onCancel}
appearance="simple"
/>
</HeaderWrapper>
<Content>{children}</Content>
{actions && <ActionWrapper>{actions}</ActionWrapper>}
</Wrapper>
</ClickAway>
);
};

Expand Down
2 changes: 2 additions & 0 deletions web/src/beta/lib/reearth-ui/components/TimePicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export const TimePicker: FC<TimePickerProps> = ({
onBlur={handleBlur}
onFocus={handleFocus}
type="time"
min="00:00:00"
max="23:59:59"
step={1}
/>
</Wrapper>
Expand Down
1 change: 1 addition & 0 deletions web/src/beta/lib/reearth-ui/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export * from "./CheckBox";
export * from "./Markdown";
export * from "./Slider";
export * from "./Slide";
export * from "./ClickAway";
107 changes: 107 additions & 0 deletions web/src/beta/ui/fields/TimePeriodField/EditPanel/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { getTimeZone, isValidDateTimeFormat } from "@reearth/beta/utils/time";
import { useCallback, useEffect, useMemo, useState } from "react";

import { TimePeriodFieldProp } from "..";

type Props = {
timePeriodValues?: TimePeriodFieldProp;
onChange?: (value?: TimePeriodFieldProp) => void;
onClose?: () => void;
};

export default ({ timePeriodValues, onChange, onClose }: Props) => {
const [timeRangeInvalid, setTimeRangeInvalid] = useState(false);

const [localValue, setLocalValue] = useState(timePeriodValues);

useEffect(() => {
setLocalValue(timePeriodValues);
}, [timePeriodValues]);

const handleChange = useCallback(
(newValue: string, fieldId: string) => {
const updatedData: TimePeriodFieldProp = {
...(localValue ?? {}),
currentTime: localValue?.currentTime || "",
startTime: localValue?.startTime || "",
endTime: localValue?.endTime || ""
};

switch (fieldId) {
case "startTime":
updatedData.startTime = newValue || "";
break;
case "currentTime":
updatedData.currentTime = newValue;
if (
(updatedData.startTime &&
new Date(updatedData.currentTime.substring(0, 19)) <
new Date(updatedData.startTime.substring(0, 19))) ||
new Date(updatedData.currentTime.substring(0, 19)) >
new Date(updatedData.endTime.substring(0, 19))
) {
setTimeRangeInvalid(true);
} else {
setTimeRangeInvalid(false);
}
break;
case "endTime":
updatedData.endTime = newValue;
if (
updatedData.endTime &&
new Date(updatedData.currentTime.substring(0, 19)) >
new Date(updatedData?.endTime?.substring(0, 19))
) {
setTimeRangeInvalid(true);
} else {
setTimeRangeInvalid(false);
}
break;
default:
break;
}
setLocalValue(updatedData);
},
[localValue]
);

const handleSubmit = useCallback(() => {
if (
localValue &&
isValidDateTimeFormat(localValue?.startTime) &&
isValidDateTimeFormat(localValue?.currentTime) &&
isValidDateTimeFormat(localValue?.endTime)
) {
onChange?.(localValue);
}
onClose?.();
}, [localValue, onChange, onClose]);

const submitDisabled = useMemo(() => {
if (
!localValue?.startTime ||
!localValue?.currentTime ||
!localValue?.endTime ||
timeRangeInvalid
) {
return true;
}

const startTimezone = getTimeZone(localValue?.startTime);
const currentTimezone = getTimeZone(localValue?.currentTime);
const endTimezone = getTimeZone(localValue?.endTime);
if (startTimezone !== currentTimezone || currentTimezone !== endTimezone) {
return true;
}

return false;
}, [localValue, timeRangeInvalid]);

return {
timeRangeInvalid,
submitDisabled,
localValue,
handleChange,
handleSubmit
};
};
Original file line number Diff line number Diff line change
@@ -1,72 +1,52 @@
import { getTimeZone } from "@reearth/beta/features/Visualizer/Crust/StoryPanel/utils";
import { Button, Icon, Modal, ModalPanel } from "@reearth/beta/lib/reearth-ui";
import { useT } from "@reearth/services/i18n";
import { styled } from "@reearth/services/theme";
import { FC, useMemo } from "react";
import { FC } from "react";

import TimePointField from "../TimePointField";
import { TimePeriodFieldProp } from "..";
import TimePointField from "../../TimePointField";

import useHooks from "./hooks";

import { TimePeriodFieldProp } from ".";

type EditPanelProps = {
visible: boolean;
timePeriodValues?: TimePeriodFieldProp;
onClose?: () => void;
onChange?: (value?: TimePeriodFieldProp) => void;
setTimePeriodValues?: (value?: TimePeriodFieldProp) => void;
};

const EditModal: FC<EditPanelProps> = ({
visible,
timePeriodValues,
setTimePeriodValues,
onClose,
onChange
}) => {
const t = useT();
const {
isDisabled,
warning,
disabledFields,
setDisabledFields,
submitDisabled,
timeRangeInvalid,
localValue,
handleChange,
handleSubmit,
handleTimePointPopup,
handleClose
handleSubmit
} = useHooks({
timePeriodValues,
onChange,
onClose,
setTimePeriodValues
onClose
});

const timezoneMatches = useMemo(() => {
if (!timePeriodValues) return false;

const startTimezone = getTimeZone(timePeriodValues?.startTime);
const currentTimezone = getTimeZone(timePeriodValues?.currentTime);
const endTimezone = getTimeZone(timePeriodValues?.endTime);

const checkTimezones =
startTimezone === currentTimezone && currentTimezone === endTimezone;
return checkTimezones;
}, [timePeriodValues]);

return (
<Modal visible={visible} size="small">
<ModalPanel
title={t("Time Period Settings")}
onCancel={handleClose}
onCancel={onClose}
actions={
<>
<Button onClick={handleClose} size="normal" title="Cancel" />
<Button onClick={onClose} size="normal" title="Cancel" />
<Button
size="normal"
title="Apply"
appearance="primary"
disabled={!isDisabled || warning || !timezoneMatches}
disabled={submitDisabled}
onClick={handleSubmit}
/>
</>
Expand All @@ -77,33 +57,21 @@ const EditModal: FC<EditPanelProps> = ({
title={t("* Start Time")}
description={t("Start time for the timeline")}
onChange={(newValue) => handleChange(newValue || "", "startTime")}
value={timePeriodValues?.startTime}
fieldName={"startTime"}
disabledField={disabledFields.includes("startTime")}
setDisabledFields={setDisabledFields}
onTimePointPopupOpen={handleTimePointPopup}
value={localValue?.startTime}
/>
<TimePointField
title={t("* Current Time")}
description={t("Current time should be between start and end time")}
onChange={(newValue) => handleChange(newValue || "", "currentTime")}
value={timePeriodValues?.currentTime}
disabledField={disabledFields.includes("currentTime")}
fieldName={"currentTime"}
setDisabledFields={setDisabledFields}
onTimePointPopupOpen={handleTimePointPopup}
value={localValue?.currentTime}
/>
<TimePointField
title={t("* End Time")}
onChange={(newValue) => handleChange(newValue || "", "endTime")}
description={t("End time for the timeline")}
value={timePeriodValues?.endTime}
fieldName={"endTime"}
disabledField={disabledFields.includes("endTime")}
setDisabledFields={setDisabledFields}
onTimePointPopupOpen={handleTimePointPopup}
value={localValue?.endTime}
/>
{warning && (
{timeRangeInvalid && (
<Warning>
<Icon icon="warning" size="large" />
{t(
Expand Down
Loading

0 comments on commit 6a96cb9

Please sign in to comment.