Skip to content

Commit

Permalink
Merge branch 'fe-dev' into feature/#371
Browse files Browse the repository at this point in the history
  • Loading branch information
jinhokim98 authored Aug 16, 2024
2 parents e9b5876 + 249fc3f commit 7fe787c
Show file tree
Hide file tree
Showing 36 changed files with 513 additions and 111 deletions.
5 changes: 3 additions & 2 deletions HDesign/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion HDesign/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "haengdong-design",
"version": "0.1.69",
"version": "0.1.74",
"description": "",
"main": "./dist/index.js",
"module": "./dist/index.js",
Expand Down
75 changes: 75 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.Input.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {css} from '@emotion/react';

import {TextSize} from '@components/Text/Text.type';

import {Theme} from '@theme/theme.type';

import TYPOGRAPHY from '@token/typography';

interface InputWrapperStyleProps {
theme: Theme;
hasFocus: boolean;
hasError: boolean;
}

interface InputStyleProps {
theme: Theme;
textSize: TextSize;
}

interface InputSizeStyleProps {
textSize: TextSize;
}

interface InputBaseStyleProps {
theme: Theme;
}

export const inputWrapperStyle = ({theme, hasFocus, hasError}: InputWrapperStyleProps) =>
css({
position: 'relative',
display: 'inline-block',

'&::after': {
content: '""',
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
height: '0.125rem',
backgroundColor: hasFocus ? theme.colors.primary : hasError ? theme.colors.error : 'transparent',
transition: 'background-color 0.2s',
transitionTimingFunction: 'cubic-bezier(0.7, 0.62, 0.62, 1.16)',
},
});

export const inputStyle = ({theme, textSize}: InputStyleProps) => [inputSizeStyle({textSize}), inputBaseStyle({theme})];

const inputSizeStyle = ({textSize}: InputSizeStyleProps) => {
const style = {
head: css(TYPOGRAPHY.head),
title: css(TYPOGRAPHY.title),
subTitle: css(TYPOGRAPHY.subTitle),
bodyBold: css(TYPOGRAPHY.bodyBold),
body: css(TYPOGRAPHY.body),
smallBodyBold: css(TYPOGRAPHY.smallBodyBold),
smallBody: css(TYPOGRAPHY.smallBody),
captionBold: css(TYPOGRAPHY.captionBold),
caption: css(TYPOGRAPHY.caption),
tiny: css(TYPOGRAPHY.tiny),
};

return [style[textSize]];
};

const inputBaseStyle = ({theme}: InputBaseStyleProps) =>
css({
border: 'none',
outline: 'none',
paddingBottom: '0.125rem',

color: theme.colors.black,
'&:placeholder': {
color: theme.colors.gray,
},
});
27 changes: 27 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/** @jsxImportSource @emotion/react */
import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react';

import {InputProps} from '@components/EditableItem/EditableItem.Input.type';

import {useTheme} from '@theme/HDesignProvider';

import {inputStyle, inputWrapperStyle} from './EditableItem.Input.style';
import useEditableItemInput from './useEditableItemInput';

export const EditableItemInput: React.FC<InputProps> = forwardRef<HTMLInputElement, InputProps>(function Input(
{textSize = 'body', hasError = false, ...htmlProps},
ref,
) {
const {theme} = useTheme();
const inputRef = useRef<HTMLInputElement>(null);
const {hasFocus} = useEditableItemInput({inputRef});
useImperativeHandle(ref, () => inputRef.current!);

return (
<div css={inputWrapperStyle({theme, hasFocus, hasError})}>
<input css={inputStyle({theme, textSize})} ref={inputRef} {...htmlProps} />
</div>
);
});

export default EditableItemInput;
18 changes: 18 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.Input.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {TextSize} from '@components/Text/Text.type';

import {Theme} from '@theme/theme.type';

export interface InputStyleProps {
hasError?: boolean;
textSize?: TextSize;
}

export interface InputCustomProps {}

export interface InputStylePropsWithTheme extends InputStyleProps {
theme: Theme;
}

export type InputOptionProps = InputStyleProps & InputCustomProps;

export type InputProps = React.ComponentProps<'input'> & InputOptionProps;
23 changes: 23 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** @jsxImportSource @emotion/react */
import {createContext, PropsWithChildren, useContext, useState} from 'react';

interface EditableItemContextProps {
hasAnyFocus: boolean;
setHasAnyFocus: React.Dispatch<React.SetStateAction<boolean>>;
}

const EditableItemContext = createContext<EditableItemContextProps | null>(null);

export const useEditableItemContext = () => {
const context = useContext(EditableItemContext);
if (!context) {
throw new Error('useEditableItemContext must be used within an EditableItemProvider');
}
return context;
};

export const EditableItemProvider: React.FC<PropsWithChildren> = ({children}: React.PropsWithChildren) => {
const [hasAnyFocus, setHasAnyFocus] = useState(false);

return <EditableItemContext.Provider value={{hasAnyFocus, setHasAnyFocus}}>{children}</EditableItemContext.Provider>;
};
44 changes: 44 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.input.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @jsxImportSource @emotion/react */
import type {Meta, StoryObj} from '@storybook/react';

import EditableItemInput from '@components/EditableItem/EditableItem.Input';

import EditableItem from './EditableItem';
import {EditableItemProvider} from './EditableItem.context';

const meta = {
title: 'Components/EditableItemInput',
component: EditableItemInput,
tags: ['autodocs'],
parameters: {},
argTypes: {
textSize: {
description: '',
control: {type: 'select'},
},
hasError: {
description: '',
control: {type: 'boolean'},
},
},
args: {
placeholder: 'μ§€μΆœ λ‚΄μ—­',
textSize: 'body',
hasError: false,
autoFocus: true,
},
} satisfies Meta<typeof EditableItemInput>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Playground: Story = {
render: ({...args}) => {
return (
<EditableItemProvider>
<EditableItem.Input {...args} />
</EditableItemProvider>
);
},
};
44 changes: 44 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @jsxImportSource @emotion/react */
import type {Meta, StoryObj} from '@storybook/react';

import EditableItem from '@components/EditableItem/EditableItem';
import Flex from '@components/Flex/Flex';
import Text from '@components/Text/Text';

const meta = {
title: 'Components/EditableItem',
component: EditableItem,
tags: ['autodocs'],
parameters: {},
argTypes: {
backgroundColor: {
description: '',
control: {type: 'select'},
},
},
args: {
backgroundColor: 'lightGrayContainer',
},
} satisfies Meta<typeof EditableItem>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Playground: Story = {
render: ({...args}) => {
return (
<EditableItem
backgroundColor={args.backgroundColor}
onFocus={() => console.log('focus')}
onBlur={() => console.log('blur')}
>
<EditableItem.Input placeholder="μ§€μΆœ λ‚΄μ—­" textSize="bodyBold"></EditableItem.Input>
<Flex gap="0.25rem" alignItems="center">
<EditableItem.Input placeholder="0" type="number" style={{textAlign: 'right'}}></EditableItem.Input>
<Text size="caption">원</Text>
</Flex>
</EditableItem>
);
},
};
14 changes: 14 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {css} from '@emotion/react';

import {Theme} from '@theme/theme.type';

import {ColorKeys} from '@token/colors';

export const editableItemStyle = (theme: Theme, backgroundColor: ColorKeys) =>
css({
display: 'flex',
justifyContent: 'space-between',
padding: '0.5rem',
borderRadius: '0.5rem',
backgroundColor: theme.colors[backgroundColor],
});
40 changes: 40 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/** @jsxImportSource @emotion/react */
import React, {useEffect} from 'react';

import {useTheme} from '@theme/HDesignProvider';

import {editableItemStyle} from './EditableItem.style';
import EditableItemInput from './EditableItem.Input';
import {EditableItemProps} from './EditableItem.type';
import {EditableItemProvider} from './EditableItem.context';
import useEditableItem from './useEditableItem';

const EditableItemBase = ({
onInputFocus,
onInputBlur,
backgroundColor = 'white',
children,
...htmlProps
}: EditableItemProps) => {
const {theme} = useTheme();

useEditableItem({onInputFocus, onInputBlur});

return (
<div css={editableItemStyle(theme, backgroundColor)} {...htmlProps}>
{children}
</div>
);
};

export const EditableItem = (props: EditableItemProps) => {
return (
<EditableItemProvider>
<EditableItemBase {...props} />
</EditableItemProvider>
);
};

EditableItem.Input = EditableItemInput;

export default EditableItem;
20 changes: 20 additions & 0 deletions HDesign/src/components/EditableItem/EditableItem.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {Theme} from '@theme/theme.type';

import {ColorKeys} from '@token/colors';

export interface EditableItemStyleProps {
backgroundColor: ColorKeys;
}

export interface EditableItemCustomProps {
onInputFocus?: () => void;
onInputBlur?: () => void;
}

export interface EditableItemStylePropsWithTheme extends EditableItemStyleProps {
theme: Theme;
}

export type EditableItemOptionProps = EditableItemStyleProps & EditableItemCustomProps;

export type EditableItemProps = React.ComponentProps<'div'> & EditableItemOptionProps;
23 changes: 23 additions & 0 deletions HDesign/src/components/EditableItem/useEditableItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {useEffect} from 'react';

import {useEditableItemContext} from './EditableItem.context';

interface UseEditableItemProps {
onInputFocus?: () => void;
onInputBlur?: () => void;
}

const useEditableItem = ({onInputFocus, onInputBlur}: UseEditableItemProps) => {
const {hasAnyFocus} = useEditableItemContext();

useEffect(() => {
if (hasAnyFocus && onInputFocus) {
onInputFocus();
}
if (!hasAnyFocus && onInputBlur) {
onInputBlur();
}
}, [hasAnyFocus, onInputFocus, onInputBlur]);
};

export default useEditableItem;
Loading

0 comments on commit 7fe787c

Please sign in to comment.