Skip to content

Commit

Permalink
feat(timer): <Timer /> 컴포넌트 추가
Browse files Browse the repository at this point in the history
* feat(timer): <Timer /> 컴포넌트 추가

* feat(timer): Hooks 추가

* fix: Timer 타입 수정 & size default: "small"

* fix: 시간 예외처리 추가

---------

Co-authored-by: baegofda <[email protected]>
  • Loading branch information
baegofda and baegofda authored Apr 6, 2023
1 parent 40f639a commit 806eb0b
Show file tree
Hide file tree
Showing 17 changed files with 334 additions and 13 deletions.
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);
};

0 comments on commit 806eb0b

Please sign in to comment.