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

LH-379/timer #11

Merged
merged 4 commits into from
Apr 6, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { TmFontSizes, createStyles } from "@travelmakers-design-v2/styles";
import { Props, TIMER_COLOR } from "./Timer";
import { TimerSize } from "./Timer.type";

const TIMER_TEXT_FONT: Record<TimerSize, TmFontSizes> = {
small: "body3",
large: "body2",
};

const TIMER_TIME_FONT: Record<TimerSize, TmFontSizes> = {
small: "body3",
large: "subhead2",
};

export default createStyles(
(theme, { type, size }: Pick<Props, "type" | "size">) => {
const { colors, typography, spacing } = theme;

return {
root: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
color: colors[TIMER_COLOR[type]],
},
title: {
display: "flex",
alignItems: "center",
},
icon: {
marginRight: spacing.spacing5,
},
text: {
...typography[TIMER_TEXT_FONT[size]],
fontWeight: 400,
},
time: {
...typography[TIMER_TIME_FONT[size]],
fontWeight: 700,
},
};
}
);
78 changes: 78 additions & 0 deletions packages/travelmakers-design-core/src/components/Timer/Timer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"use client";

import { useRemainingTimer } from "@travelmakers-design-v2/hooks";
import {
PolymorphicRef,
TmColor,
useTmTheme,
} from "@travelmakers-design-v2/styles";
import dayjs from "dayjs";
import { forwardRef } from "react";
import { Icon } from "../Icon";
import { View } from "../View";
import useStyles from "./Timer.style";
import { TimerComponent, TimerProps, TimerSize, TimerType } from "./Timer.type";

export interface Props {
type: TimerType;
size?: TimerSize;
text: "string";
time: dayjs.ConfigType;
dateTime?: string;
}

const TIMER_ICON_SIZE: Record<TimerSize, number> = {
small: 12,
large: 16,
};

export const TIMER_COLOR: Record<TimerType, TmColor> = {
navy: "primary1",
white: "white",
};

export const Timer: TimerComponent & {
displayName?: string;
} = forwardRef(
<C extends React.ElementType = "div">(
{
type,
size = "small",
text,
time,
dateTime,
className,
...props
}: TimerProps<C>,
ref: PolymorphicRef<C>
) => {
const { colors } = useTmTheme();
const { classes, cx } = useStyles({ type, size });
const { remainingTime } = useRemainingTimer(time);

return (
<View<React.ElementType>
component={"div"}
ref={ref}
className={cx(classes.root, className)}
{...props}
>
<div className={classes.title}>
<Icon
src={"IcClock"}
className={classes.icon}
width={TIMER_ICON_SIZE[size]}
height={TIMER_ICON_SIZE[size]}
color={colors[TIMER_COLOR[type]]}
/>
<strong className={classes.text}>{text}</strong>
</div>
<time className={classes.time} dateTime={dateTime}>
{remainingTime}
</time>
</View>
);
}
);

Timer.displayName = "Timer";
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
ClassNames,
PolymorphicComponentProps,
TmComponentProps,
} from "@travelmakers-design-v2/styles";
import { Props } from "./Timer";
import useStyles from "./Timer.style";

export type TimerType = "navy" | "white";
export type TimerSize = "small" | "large";

type TimerStylesNames = ClassNames<typeof useStyles>;

interface SharedTimerProps extends Props, TmComponentProps<TimerStylesNames> {}

export type TimerProps<C extends React.ElementType> = PolymorphicComponentProps<
C,
SharedTimerProps
>;

export type TimerComponent = <C extends React.ElementType = "div">(
props: TimerProps<C>
) => React.ReactElement;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Timer } from "./Timer";
export type { TimerProps } from "./Timer.type";
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Meta } from "@storybook/react";
import { getDayjs } from "@travelmakers-design-v2/utils";
import { Timer } from "../Timer";

export default {
title: "@travelmakers-design-v2/core/General/Timer",
component: Timer,
argTypes: {
type: {
control: { type: "radio", options: ["navy", "white"], default: "navy" },
description: "Timer의 색상 타입",
table: {
type: {
summary: "string",
},
},
},
size: {
control: { type: "radio", options: ["small", "large"], default: "small" },
defaultValue: "small",
description: "Timer의 사이즈",
table: {
type: {
summary: "string",
},
},
},
text: {
control: { type: "text" },
defaultValue: "타임세일 워딩",
description: "타임 세일에 대한 워딩",
table: {
type: {
summary: "string",
},
},
},
time: {
control: { type: "text" },
description:
"타임 세일 기간 ex) Thu Apr 06 2023 10:52:19 GMT+0900 (한국 표준시)",
table: {
type: {
summary: "dayjs.ConfigType",
},
},
},
// https://developer.mozilla.org/ko/docs/Web/HTML/Element/time
dateTime: {
control: { type: "text" },
defaultValue: "2011-11-18",
description: "검색 엔진에서 필요한 날짜 데이터 입니다.",
table: {
type: {
summary: "string",
},
},
},
},
} as Meta;

export const Default = (props) => {
const { time, type, size, text, dateTime } = props;
const dayjs = getDayjs();
const now = dayjs();

return (
<div style={{ backgroundColor: props.type === "white" && "gray" }}>
<Timer
time={time || now}
type={type}
size={size}
text={text}
dateTime={dateTime}
/>
</div>
);
};
1 change: 1 addition & 0 deletions packages/travelmakers-design-core/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export * from "./Image";
export * from "./Menu";
export * from "./Price";
export * from "./Tag";
export * from "./Timer";
export * from "./Typography";
export * from "./View";
1 change: 1 addition & 0 deletions packages/travelmakers-design-hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from "./useForceUpdate";
export * from "./useHiddenBodyOverflow";
export * from "./useLoading";
export * from "./useRafState";
export * from "./useRemainingTimer";
export * from "./useResize";
export * from "./useTimeoutFn";
export * from "./useUpdateEffect";
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import { useEffect } from "react";

import { useRafState } from "../useRafState";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./useRemainingTimer";
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import dayjs from "dayjs";
import { useRemainingTimer } from "../useRemainingTimer";

export default {
title: "@travelmakers-design-v2/hooks/useRemainingTimer",
};

export const Default = () => {
const { remainingTime } = useRemainingTimer(dayjs());

return <div>{remainingTime}</div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getRemainingTime } from "@travelmakers-design-v2/utils";
import dayjs from "dayjs";
import { useLayoutEffect, useState } from "react";

export const useRemainingTimer = (
targetDate: dayjs.ConfigType,
format?: string,
delay?: 1000
) => {
const [remainingTime, setRemainingTime] = useState("");

useLayoutEffect(() => {
let timer: NodeJS.Timeout | null = null;

timer = setInterval(
() => setRemainingTime(getRemainingTime(targetDate, format)),
delay
);

return () => {
timer && clearInterval(timer);
};
}, []);

return { remainingTime };
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef } from 'react';
import { useCallbackRef } from '../useCallbackRef';
import { useCallback, useEffect, useRef } from "react";
import { useCallbackRef } from "../useCallbackRef";

export const useTimeoutFn = (fn: () => void, ms: number) => {
const timeoutId = useRef<ReturnType<typeof setTimeout>>();
Expand Down
12 changes: 10 additions & 2 deletions packages/travelmakers-design-hooks/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
"declaration": true,
"declarationMap": true,
"declarationDir": "lib",
"composite": true
}
"composite": true,
"paths": {
"@travelmakers-design-v2/styles": ["../travelmakers-design-styles"],
"@travelmakers-design-v2/utils": ["../travelmakers-design-utils"]
}
},
"references": [
{ "path": "../travelmakers-design-utils" },
{ "path": "../travelmakers-design-styles" }
]
}
12 changes: 10 additions & 2 deletions packages/travelmakers-design-hooks/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
"declaration": true,
"declarationMap": true,
"declarationDir": "lib",
"composite": true
}
"composite": true,
"paths": {
"@travelmakers-design-v2/styles": ["../travelmakers-design-styles"],
"@travelmakers-design-v2/utils": ["../travelmakers-design-utils"]
}
},
"references": [
{ "path": "../travelmakers-design-styles" },
{ "path": "../travelmakers-design-utils" }
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
import { TmSize } from "./TmSize";

import type { CSSProperties } from "react";
import type { DeepPartial } from "./DeepPartial";
import { CoTypography } from "../tokens/typography";
import { TmPalette } from "../tokens/palettes";
import { CoTypography } from "../tokens/typography";
import type { DeepPartial } from "./DeepPartial";
import { Tuple } from "./Tuple";

export interface HeadingStyle {
Expand Down
9 changes: 5 additions & 4 deletions packages/travelmakers-design-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export * from "./getDate";
export * from "./getPayMethod";
export * from "./getReservationsCallable";
export * from "./getTimeLineFunc";
export * from "./getTimeLineStateFunc";
export * from "./getTimeLineStateDetailFunc";
export * from "./getReservationsCallable";
export * from "./getPayMethod";
export * from "./getDate";
export * from "./getTimeLineStateFunc";
export * from "./utils/getCountDown";
export * from "./utils/getRemainingTime";
export * from "./utils/getTimeStamp";
39 changes: 39 additions & 0 deletions packages/travelmakers-design-utils/src/utils/getRemainingTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";

dayjs.extend(duration);
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.tz.setDefault("Asia/Seoul");

/**
* 현재 시간부터 목표 시간까지의 남은 시간을 구합니다.
*
* ex)
* now: "Sat Feb 25 2023 02:27:29 GMT+0900"
* targetDate: "2023-02-28 23:59:00"
* format: "DD일 HH:mm:ss"
*
* return "05일 06:31:30"
*/
export const getRemainingTime = (
targetDate: dayjs.ConfigType,
format = "DD일 HH:mm:ss"
) => {
const target = dayjs(targetDate);
const now = dayjs();
const diff = target.diff(now);
const isReachedTime = now.isSame(target) || diff <= 0;
if (isReachedTime) return "00:00:00";

const days = dayjs.duration(diff).days();

// NOTE: 당일이라면 일자는 표시x
if (days === 0) {
return dayjs(diff).format("HH:mm:ss");
}

return dayjs(diff).format(format);
};