Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
014042a
feat: 태그 norma버전 ui 구현
twossu May 11, 2026
ea39a83
fix: sm 사이즈 글자 밀림 해결
twossu May 11, 2026
33d92a5
Merge branch 'develop' of https://github.com/TEAM-CLUSTAR/CLUSTAR-CLI…
twossu May 12, 2026
fe7fc59
feat: 태그 서브 컬러 10종 추가 및 색상 매핑 정리
twossu Jun 24, 2026
5cf930b
feat: 태그 컴포넌트 리디자인 및 삭제 버튼 추가
twossu Jul 2, 2026
a045f1d
fix: 태그 컴포넌트의 스타일 수정 및 글자 가운데 맞게
twossu Jul 2, 2026
b1449de
Merge branch 'develop' of https://github.com/TEAM-CLUSTAR/CLUSTAR-CLI…
twossu Jul 2, 2026
bfac1f8
refactor: 태그 컴포넌트의 색상 타입 정의를 통합하고 불필요한 파일 삭제
twossu Jul 2, 2026
91d9ed8
refactor: 카드 컴포넌트에서 사용되는 태그컴포넌트로 교체
twossu Jul 2, 2026
17ce9a3
fix: 카드 컴포넌트의 태그 타입을 백엔드 스키마에 맞게 수정하고 태그 크기 변경
twossu Jul 29, 2026
61e44b1
fix: 태그 컴포넌트의 색상 서버로 부터 받아서 사용하도록 수정
twossu Jul 29, 2026
a091bc9
refactor: cds-ui에서 사용하지 않는 Label/LabelList 컴포넌트 삭제
twossu Jul 29, 2026
1b081f1
refactor: 메모 작성 화면의 태그 선택을 실제 백엔드 태그로 전환
twossu Jul 29, 2026
6018fe5
refactor: 메모작성 탭의 태그 기반 색상 표시와 디테일모달의 태그 표시 제거
twossu Jul 29, 2026
c058bbd
refactor: 태그 컴포넌트 및 관련 타입을 업데이트하고 전부 태그로 네이밍 교체
twossu Jul 30, 2026
66b5d67
refactor: 태그 컴포넌트에서 제거 가능 여부에 보더 스타일 반영
twossu Jul 30, 2026
371a7f0
fix: 태그 컴포넌트의 크기 변형에서 lineHeight 속성 제거
twossu Jul 30, 2026
5e7c28d
fix: 태그 컴포넌트에서 불필요한 스타일 삭제
twossu Jul 30, 2026
09c7987
refactor: ai 결과물 태그 스타일 적용
twossu Jul 31, 2026
2ce673a
refactor: 태그 컴포넌트에서 compoundVariants 제거
twossu Aug 1, 2026
3592970
fix: 태그 컴포넌트에서 ai 변형일 때 삭제 버튼 숨김
twossu Aug 1, 2026
1c3bfa2
refactor: 태그 컴포넌트 색상 매칭로 반영
twossu Aug 7, 2026
03495f9
refactor: 태그 컴포넌트의 color/action prop 재설계 및 색상 매칭 상수 cds-ui로 이동
twossu Aug 10, 2026
15bf32f
feat: tag 컴포넌트 스토리북 작성
twossu Aug 10, 2026
154ce19
fix: 태그 컴포넌트에서 color prop을 필수로 변경
twossu Aug 10, 2026
a09e7d4
refactor: 태그 컴포넌트에서 action prop 제거
twossu Aug 10, 2026
a860209
fix: ai결과물일때 remove사용하지 못하게의 경우를 다시 원복
twossu Aug 10, 2026
be62bee
fix: 태그 컴포넌트에서 ai지식 다시 제거
twossu Aug 10, 2026
0fa24f0
refactor: 태그 컴포넌트 fill/outlined variant 기반으로 재설계
twossu Aug 11, 2026
53e99f3
refactor: 태그 타입에서 중복된 TagBaseProps 교차를 상위로 통합
twossu Aug 11, 2026
68100b4
fix: 카드 컴포넌트 그라데이션 스타일을 isAiGenerated 대신 isNew로 분리
twossu Aug 14, 2026
5f1df80
fix: 태그 컴포넌트의 불필요한 removable 상태 제거
twossu Aug 21, 2026
812a579
refactor: onRemove 핸들러를 rest.onRemove로 직접 참조하도록 변경
twossu Aug 21, 2026
5e6b71d
refactor: 트리뷰 라벨 관련 코드 정리
twossu Aug 21, 2026
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

This file was deleted.

This file was deleted.

40 changes: 10 additions & 30 deletions apps/client/src/pages/new-memo/components/memo-input/memo-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,49 @@ import { Button } from '@cds/ui';
import { htmlToMarkdown } from '@pages/new-memo/utils/html-to-markdown';

import ConfirmModal from '@shared/components/confirm-modal/confirm-modal';
import { LabelTextType } from '@shared/types/label-type';
import { components } from '@shared/types/schema';

import { useCreateMemo } from '../../apis/queries';
import type { MemoCreateRequest } from '../../apis/type';
import { useNavigationBlocker } from '../../hooks/use-navigation-blocker';
import InputContent from '../input-content/input-content';
import InputTitle from '../input-title/input-title';
import LabelSelect from '../label-select/label-select';
import TabList from '../tab-list/tab-list';
import ToolBar from '../toolbar/toolbar';

import * as styles from './memo-input.css';

type TagItem = components['schemas']['TagResponse'];

type TabItem = {
id: string;
title?: string;
label: LabelTextType;
};

type LabelItem = {
id: string;
text: LabelTextType;
};

export type MemoDraft = {
id: string;
title: string;
contents: string;
labels: LabelItem[];
tags: TagItem[];
};

export type DraftsById = Record<string, MemoDraft>;

const MAX_TABS = 4;
const DEFAULT_TITLE = '제목없음';
const DEFAULT_LABEL = '라벨없음' as LabelTextType;

const createId = () => crypto.randomUUID();
const createEmptyDraft = (id: string): MemoDraft => ({
id,
title: '',
contents: '',
labels: [],
tags: [],
});

const MemoInput = () => {
const [{ initTabs, initSelectedId, initDraftsById }] = useState(() => {
const id = createId();
const tab: TabItem = { id, title: DEFAULT_TITLE, label: DEFAULT_LABEL };
const tab: TabItem = { id, title: DEFAULT_TITLE };
return {
initTabs: [tab],
initSelectedId: id,
Expand Down Expand Up @@ -84,12 +78,10 @@ const MemoInput = () => {
return tabs.map((tab) => {
const draft = draftsById[tab.id];
const title = draft?.title?.trim();
const firstLabel = draft?.labels?.[0]?.text;

return {
...tab,
title: title && title.length > 0 ? title : DEFAULT_TITLE,
label: firstLabel ?? DEFAULT_LABEL,
};
});
}, [tabs, draftsById]);
Expand All @@ -99,10 +91,7 @@ const MemoInput = () => {

const id = createId();

setTabs((prevTabs) => [
...prevTabs,
{ id, title: DEFAULT_TITLE, label: DEFAULT_LABEL },
]);
setTabs((prevTabs) => [...prevTabs, { id, title: DEFAULT_TITLE }]);

setDraftsById((prevDrafts) => ({
...prevDrafts,
Expand All @@ -116,8 +105,8 @@ const MemoInput = () => {
if (!draft) return false;
const hasTitle = draft.title.trim().length > 0;
const hasContents = draft.contents.trim().length > 0;
const hasLabels = draft.labels.length > 0;
return hasTitle || hasContents || hasLabels;
const hasTags = draft.tags.length > 0;
return hasTitle || hasContents || hasTags;
};

const deleteTabById = (idToDelete: string) => {
Expand Down Expand Up @@ -181,15 +170,11 @@ const MemoInput = () => {
patchSelectedDraft({ contents });
};

const handleChangeLabels = (labels: LabelItem[]) => {
patchSelectedDraft({ labels });
};

const handleSubmit = () => {
const request: MemoCreateRequest = {
title: selectedDraft.title,
content: htmlToMarkdown(selectedDraft.contents),
tagNames: selectedDraft.labels.map((l) => l.text),
tagNames: selectedDraft.tags.map((l) => l.name ?? ''),
};

createMemo(request, {
Expand Down Expand Up @@ -265,11 +250,6 @@ const MemoInput = () => {
/>
<div className={styles.inputContainer}>
<div className={styles.contentsContainer}>
<LabelSelect
selectedItems={selectedDraft.labels}
onSelect={handleChangeLabels}
/>

<InputTitle
title={selectedDraft.title}
onChange={handleChangeTitle}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { Icon } from '@cds/icon';

import { LabelTextType } from '@shared/types/label-type';

import Tab from '../tab/tab';

import * as styles from './tab-list.css';

interface TabItemType {
id: string;
title?: string;
label: LabelTextType;
}

interface TabListProps {
Expand Down Expand Up @@ -37,12 +34,11 @@ const TabList = ({
<div className={styles.memoMarker}>
<Icon name="ic_memo_36" size={36} />
</div>
{items.map(({ id, title, label }) => (
{items.map(({ id, title }) => (
<Tab
key={id}
id={id}
title={title}
label={label}
isSelected={id === selectedTabId}
handleSelect={() => handleSelectTab(id)}
handleDelete={() => handleDeleteTab(id)}
Expand Down
4 changes: 1 addition & 3 deletions apps/client/src/pages/new-memo/components/tab/tab.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { recipe } from '@vanilla-extract/recipes';

import { themeVars } from '@cds/ui';

export const PRIMARY_COLOR_VAR = '--card-primary-color';

export const tabContainer = recipe({
base: {
position: 'relative',
Expand All @@ -28,7 +26,7 @@ export const tabContainer = recipe({
top: 0,
width: '4px',
height: '100%',
backgroundColor: `var(${PRIMARY_COLOR_VAR})`,
backgroundColor: themeVars.color.blue400,
pointerEvents: 'none',

transform: 'scaleX(0)',
Expand Down
18 changes: 1 addition & 17 deletions apps/client/src/pages/new-memo/components/tab/tab.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
import { CSSProperties } from 'react';

import { Icon } from '@cds/icon';

import {
LABEL_COLOR_BY_TEXT,
PRIMARY_COLOR_VALUE_BY_LABEL_COLOR,
} from '@shared/constants/label-match';
import { LabelTextType } from '@shared/types/label-type';

import * as styles from './tab.css';

interface TabProps {
id: string;
title?: string;
label: LabelTextType;
handleDelete: () => void;
handleSelect: () => void;
isSelected: boolean;
Expand All @@ -22,25 +13,18 @@ interface TabProps {

const Tab = ({
title,
label,
handleSelect,
isSelected,
handleDelete,
isDefault,
}: TabProps) => {
const labelColor = LABEL_COLOR_BY_TEXT[label];
const primaryColorValue = PRIMARY_COLOR_VALUE_BY_LABEL_COLOR[labelColor];

const handleDeleteClick = (e: React.MouseEvent) => {
e.stopPropagation();
handleDelete();
};

return (
<div
className={styles.tabContainer({ isSelected })}
style={{ [styles.PRIMARY_COLOR_VAR]: primaryColorValue } as CSSProperties}
>
<div className={styles.tabContainer({ isSelected })}>
<button
type="button"
className={styles.buttonTextContainer}
Expand Down
Loading
Loading