Skip to content

Commit

Permalink
feat(src): 확장자 기반으로 언어 태그 자동 생성 기능 추가 (#814)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hain-tain authored Oct 18, 2024
1 parent aacd91a commit 768bc52
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 29 deletions.
7 changes: 7 additions & 0 deletions frontend/src/components/SourceCodeEditor/SourceCodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface Props {
content: string;
onChangeFilename: (newFileName: string) => void;
onChangeContent: (newContent: string) => void;
onBlurFilename: (newFileName: string) => void;
isValidContentChange?: (newContent: string) => boolean;
handleDeleteSourceCode: () => void;
sourceCodeRef?: React.Ref<HTMLInputElement> | null;
Expand All @@ -24,6 +25,7 @@ const SourceCodeEditor = ({
filenameAutoFocus = false,
content,
onChangeFilename,
onBlurFilename,
onChangeContent,
isValidContentChange = () => true,
handleDeleteSourceCode,
Expand Down Expand Up @@ -53,11 +55,16 @@ const SourceCodeEditor = ({
previousContentRef.current = value;
};

const handleFilenameBlur = (e: React.FocusEvent<HTMLInputElement>) => {
onBlurFilename(e.target.value);
};

return (
<S.SourceCodeEditorContainer ref={sourceCodeRef}>
<S.FilenameInput
value={filename}
onChange={handleFilenameChange}
onBlur={handleFilenameBlur}
placeholder={'파일명.[확장자]'}
autoFocus={filenameAutoFocus}
/>
Expand Down
34 changes: 8 additions & 26 deletions frontend/src/components/TagInput/TagInput.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ChangeEvent, Dispatch, KeyboardEvent, SetStateAction } from 'react';
import { ChangeEvent, KeyboardEvent } from 'react';

import { Flex, Input, TagButton, Text } from '@/components';
import { ToastContext } from '@/contexts';
import { useCustomContext, useScreenReader } from '@/hooks';
import { useCustomContext } from '@/hooks';
import { validateTagLength } from '@/service/validates';
import { theme } from '@/style/theme';

Expand All @@ -11,30 +11,21 @@ interface Props {
handleValue: (e: ChangeEvent<HTMLInputElement>) => void;
resetValue: () => void;
tags: string[];
setTags: Dispatch<SetStateAction<string[]>>;
addTag: (newTag: string) => void;
deleteTag: (tag: string) => void;
}

const TagInput = ({ value, handleValue, resetValue, tags, setTags }: Props) => {
const TagInput = ({ value, handleValue, resetValue, tags, addTag, deleteTag }: Props) => {
const { failAlert } = useCustomContext(ToastContext);
const { updateScreenReaderMessage } = useScreenReader();

const handleSpaceBarAndEnterKeydown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
addTag();
addTag(value);
resetValue();
}
};

const addTag = () => {
if (value === '' || tags.includes(value)) {
return;
}

setTags((prev) => [...prev, value]);
updateScreenReaderMessage(`${value} 태그 등록`);
};

const handleTagInput = (e: ChangeEvent<HTMLInputElement>) => {
const errorMessage = validateTagLength(e.target.value);

Expand Down Expand Up @@ -66,16 +57,7 @@ const TagInput = ({ value, handleValue, resetValue, tags, setTags }: Props) => {
</Flex>
)}
<Flex gap='0.25rem' css={{ flexWrap: 'wrap', width: '100%' }}>
{tags?.map((tag, idx) => (
<TagButton
key={idx}
variant='edit'
name={tag}
onClick={() => {
setTags((prev) => prev.filter((el) => el !== tag));
}}
/>
))}
{tags?.map((tag, idx) => <TagButton key={idx} variant='edit' name={tag} onClick={() => deleteTag(tag)} />)}
</Flex>
<Input size='large' variant='outlined'>
<Input.TextField
Expand All @@ -84,7 +66,7 @@ const TagInput = ({ value, handleValue, resetValue, tags, setTags }: Props) => {
onChange={handleTagInput}
onKeyUp={handleSpaceBarAndEnterKeydown}
onBlur={() => {
addTag();
addTag(value);
resetValue();
}}
/>
Expand Down
19 changes: 17 additions & 2 deletions frontend/src/hooks/template/useTag.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import { useState } from 'react';

import { useNoSpaceInput } from '@/hooks';
import { useNoSpaceInput, useScreenReader } from '@/hooks';

export const useTag = (initTags: string[]) => {
const [tags, setTags] = useState<string[]>(initTags);
const [value, handleValue, resetValue] = useNoSpaceInput('');
const { updateScreenReaderMessage } = useScreenReader();

const addTag = (newTag: string) => {
if (newTag === '' || tags.includes(newTag)) {
return;
}

setTags((prev) => [...prev, newTag]);
updateScreenReaderMessage(`${newTag} 태그 등록`);
};

const deleteTag = (tag: string) => {
setTags((prev) => prev.filter((el) => el !== tag));
};

return {
tags,
setTags,
addTag,
deleteTag,
value,
handleValue,
resetValue,
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/pages/TemplateEditPage/TemplateEditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ICON_SIZE } from '@/style/styleConstants';
import { theme } from '@/style/theme';
import type { Template, TemplateEditRequest } from '@/types';
import { TemplateVisibility } from '@/types/template';
import { getLanguageForAutoTag } from '@/utils';

import * as S from './TemplateEditPage.style';

Expand Down Expand Up @@ -140,6 +141,7 @@ const TemplateEditPage = ({ template, toggleEditButton }: Props) => {
isValidContentChange={isValidContentChange}
onChangeContent={(newContent) => handleContentChange(newContent, index)}
onChangeFilename={(newFilename) => handleFilenameChange(newFilename, index)}
onBlurFilename={(newFilename) => tagProps.addTag(getLanguageForAutoTag(newFilename))}
handleDeleteSourceCode={() => handleDeleteSourceCode(index)}
filenameAutoFocus={index !== 0}
/>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/pages/TemplateUploadPage/TemplateUploadPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ICON_SIZE } from '@/style/styleConstants';
import { theme } from '@/style/theme';
import { TemplateUploadRequest } from '@/types';
import { TemplateVisibility } from '@/types/template';
import { getLanguageForAutoTag } from '@/utils';

import * as S from './TemplateUploadPage.style';

Expand Down Expand Up @@ -129,6 +130,7 @@ const TemplateUploadPage = () => {
isValidContentChange={isValidContentChange}
onChangeContent={(newContent) => handleContentChange(newContent, index)}
onChangeFilename={(newFilename) => handleFilenameChange(newFilename, index)}
onBlurFilename={(newFilename) => tagProps.addTag(getLanguageForAutoTag(newFilename))}
handleDeleteSourceCode={() => handleDeleteSourceCode(index)}
filenameAutoFocus={index !== 0}
/>
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/utils/getLanguageByFileName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ export const getLanguageByFilename = (filename: string) => {
return language;
};

export const getLanguageForAutoTag = (filename: string) => {
const extension = getFileExtension(filename);
const language = getLanguageByExtension(extension);

if (extension === 'jsx' || extension === 'tsx') {
return 'react';
}

if (language === 'plaintext') {
return '';
}

return language;
};

const getFileExtension = (filename: string) => {
if (filename.includes('.')) {
const parts = filename.split('.');
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { formatRelativeTime } from './formatRelativeTime';
export { formatWithK } from './formatWithK';
export { getByteSize } from './getByteSize';
export { getLanguageByFilename } from './getLanguageByFileName';
export { getLanguageByFilename, getLanguageForAutoTag } from './getLanguageByFileName';
export { remToPx } from './remToPx';
export { scroll } from './scroll';
export { getChildOfType, getChildrenWithoutTypes } from './reactChildrenHelpers';

0 comments on commit 768bc52

Please sign in to comment.