@gusun/design-system 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.js","names":[],"sources":["../src/components/EditorToolbar.tsx","../src/components/MergeFieldNode.ts","../src/components/Editor.tsx","../src/components/NoticeWritingTemplate.tsx"],"sourcesContent":["// EditorToolbar — Editor(편집 모드)의 서식 툴바.\n// Tiptap editor 인스턴스를 받아 editor.chain() 명령으로 서식을 적용하고,\n// editor.isActive(...)로 현재 커서 위치의 활성 상태를 버튼에 표시한다.\n// toolbar prop(키 배열)으로 노출할 기능을 고를 수 있다(미지정 시 전체).\n// 키: mergefield · block · bold · italic · underline · strike · color · highlight ·\n// bulletList · orderedList · blockquote · align · link · table · image · hr · code · codeBlock\n// mergefield: Editor의 mergeFields prop이 있을 때만 노출(머지 태그 삽입 드롭다운).\n// 색·간격은 editor-*/font-icon-*/spacing-*/round-* 토큰만 사용. 재사용 컴포넌트(Popover/PopoverMenu/\n// ListGroup/List/Button)를 그대로 활용한다. 링크·이미지 입력은 PopoverMenu의 input 타입(topArea=\"input\"\n// + footer footerButtonsFill)으로 대체했다.\nimport { Fragment, useState } from 'react';\nimport type { ElementType, MouseEvent as ReactMouseEvent } from 'react';\nimport type { Editor, ChainedCommands } from '@tiptap/react';\nimport {\n Undo2, Redo2,\n Bold, Italic, Underline, Strikethrough, Type, Baseline, Highlighter,\n AlignLeft, AlignCenter, AlignRight, AlignJustify,\n Link as LinkIcon, Table as TableIcon, Image as ImageIcon, Minus,\n Braces, Plus,\n} from 'lucide-react';\nimport { Popover } from './Popover';\nimport { PopoverMenu } from './PopoverMenu';\nimport { ListGroup } from './ListGroup';\nimport { List } from './List';\nimport { Button } from './Button';\nimport { editorTextPalette, editorHighlightPalette } from '../tokens';\n\n// 머지필드 목록 항목 — 문자열 또는 {label,value}\ntype MergeFieldItem = string | { label: string; value: string };\n\n// 셀렉션 유지를 위해 mousedown 기본동작(포커스 이동)을 막는다 — 마크/색이 선택 영역에 적용되도록.\nconst keepSelection = (e: ReactMouseEvent) => e.preventDefault();\n\n// 아이콘 토글 버튼 — 공용 Button(고스트 아이콘 버튼 size 24).\n// active는 시각 표시 없이 aria-pressed(접근성)에만 반영한다(요청에 따라 색 변화 제거).\nfunction ToolbarButton({\n icon,\n label,\n active = false,\n disabled = false,\n onClick,\n}: {\n icon: ElementType;\n label: string;\n active?: boolean;\n disabled?: boolean;\n onClick?: () => void;\n}) {\n return (\n <Button\n variant=\"ghost\"\n size=\"24\"\n icon={icon}\n disabled={disabled}\n onClick={onClick}\n onMouseDown={keepSelection}\n aria-label={label}\n aria-pressed={active}\n />\n );\n}\n\n// 드롭다운 트리거 — 공용 Button(ghost, size 24). Popover의 trigger로 사용.\n// 라벨이 있으면(블록 '본문') 텍스트+아래 화살표, 없으면 아이콘 전용 버튼.\nfunction DropTrigger({ icon, label, ariaLabel }: { icon: ElementType; label?: string; ariaLabel?: string }) {\n if (label) {\n return (\n <Button\n variant=\"ghost\"\n size=\"24\"\n leftIcon={icon}\n onMouseDown={keepSelection}\n >\n {label}\n </Button>\n );\n }\n return (\n <Button\n variant=\"ghost\"\n size=\"24\"\n icon={icon}\n onMouseDown={keepSelection}\n title={ariaLabel}\n aria-label={ariaLabel}\n />\n );\n}\n\n// 그룹 구분선 — 행 높이만큼 세로선(토큰 보더색).\nfunction Divider() {\n return <span className=\"mx-spacing-2 self-stretch border-l border-editor-divider\" />;\n}\n\nconst BLOCK_OPTIONS = [\n { key: 'paragraph', label: '본문' },\n { key: 'h1', label: '제목 1' },\n { key: 'h2', label: '제목 2' },\n { key: 'h3', label: '제목 3' },\n];\n\n// 팔레트 색 항목 — value=null이면 해제(unset), swatch는 원 표시색만 별도 지정\ninterface PaletteColor {\n label: string;\n value: string | null;\n swatch?: string;\n}\n\n// 글자색/형광 색상 선택 패널 — 토큰 기반 팔레트 스와치.\nfunction ColorPanel({\n palette,\n current,\n onPick,\n}: {\n palette: PaletteColor[];\n current: string | null;\n onPick: (value: string | null) => void;\n}) {\n return (\n <PopoverMenu width={80}>\n <div className=\"bg-list-group-bg py-spacing-4\">\n {palette.map((c) => {\n const selected = current === c.value || (c.value === null && !current);\n const dot = c.swatch ?? c.value; // 원 표시색(값이 null이어도 swatch가 있으면 그 색을 보여줌)\n return (\n <button\n key={c.label}\n type=\"button\"\n onMouseDown={keepSelection}\n onClick={() => onPick(c.value)}\n className={`flex w-full cursor-pointer items-center gap-spacing-4 px-spacing-6 py-spacing-3 text-14 transition-colors hover:bg-list-hover-bg focus:outline-none focus-visible:bg-list-hover-bg ${\n selected ? 'text-list-selected-text' : 'text-list-default-text'\n }`}\n >\n <span\n className=\"inline-block h-[14px] w-[14px] shrink-0 rounded-round-00\"\n style={dot ? { backgroundColor: dot } : undefined}\n />\n {c.label}\n </button>\n );\n })}\n </div>\n </PopoverMenu>\n );\n}\n\n// URL 입력 팝오버 폼 — PopoverMenu input 타입(상단 입력 + fill 버튼) 공통 폼.\n// 링크/이미지 등에서 재사용. onRemove가 있으면 좌측 '해제' 버튼이 함께 노출된다(없으면 단일 버튼).\nfunction UrlInputForm({\n initialUrl = '',\n placeholder,\n submitLabel,\n onSubmit,\n onRemove,\n removeLabel = '해제',\n}: {\n initialUrl?: string;\n placeholder?: string;\n submitLabel: string;\n onSubmit: (url: string) => void;\n onRemove?: () => void;\n removeLabel?: string;\n}) {\n const [url, setUrl] = useState(initialUrl);\n const submit = () => onSubmit(url.trim());\n return (\n <PopoverMenu\n width={300}\n topArea=\"input\"\n inputValue={url}\n onInputChange={(e) => setUrl(e.target.value)}\n inputPlaceholder={placeholder}\n inputProps={{\n autoFocus: true,\n onKeyDown: (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n submit();\n }\n },\n }}\n footer\n footerButtonsFill\n showCancel={!!onRemove}\n cancelText={removeLabel}\n onCancel={onRemove}\n confirmText={submitLabel}\n onConfirm={submit}\n />\n );\n}\n\n// 링크 입력 폼(URL 적용/해제).\nfunction LinkForm({ editor, close }: { editor: Editor; close: () => void }) {\n const apply = (url: string) => {\n const chain = editor.chain().focus().extendMarkRange('link');\n if (url) chain.setLink({ href: url }).run();\n else chain.unsetLink().run();\n close();\n };\n const remove = () => {\n editor.chain().focus().extendMarkRange('link').unsetLink().run();\n close();\n };\n return (\n <UrlInputForm\n initialUrl={editor.getAttributes('link').href ?? ''}\n placeholder=\"https://example.com\"\n submitLabel=\"적용\"\n onSubmit={apply}\n onRemove={remove}\n />\n );\n}\n\n// 이미지 URL 입력 폼(삽입 단일 버튼).\nfunction ImageForm({ editor, close }: { editor: Editor; close: () => void }) {\n const insert = (url: string) => {\n if (url) editor.chain().focus().setImage({ src: url }).run();\n close();\n };\n return (\n <UrlInputForm placeholder=\"이미지 주소(URL)\" submitLabel=\"삽입\" onSubmit={insert} />\n );\n}\n\n// 머지필드 삽입 — search list 타입 PopoverMenu(width 180)에 머지 태그 목록(+ 아이콘).\n// 클릭 시 커서 위치에 mergeField 노드(삭제 가능한 칩)를 삽입한다. mergeFields = 문자열 | {label,value}[].\nfunction MergeFieldMenu({\n editor,\n mergeFields,\n close,\n}: {\n editor: Editor;\n mergeFields: MergeFieldItem[];\n close: () => void;\n}) {\n const [q, setQ] = useState('');\n const items = mergeFields\n .map((m) => (typeof m === 'string' ? { label: m, value: m } : m))\n .filter((m) => !q || m.label.toLowerCase().includes(q.toLowerCase()));\n const insert = (value: string) => {\n editor.chain().focus().insertContent({ type: 'mergeField', attrs: { value } }).run();\n close();\n };\n return (\n <PopoverMenu\n width={180}\n topArea=\"search\"\n searchValue={q}\n onSearchChange={(e) => setQ(e.target.value)}\n searchPlaceholder=\"필드명 검색\"\n searchInputProps={{ autoFocus: true }}\n >\n <ListGroup empty={items.length === 0} emptyMessage=\"검색 결과 없음\">\n {items.map((m) => (\n <List key={m.value} icon={Plus} title={m.label} onClick={() => insert(m.value)} />\n ))}\n </ListGroup>\n </PopoverMenu>\n );\n}\n\ninterface EditorToolbarProps {\n editor: Editor | null; // Tiptap editor 인스턴스(초기화 전 null)\n toolbar?: string[]; // 노출할 기능 키 배열(미지정 시 전체)\n mergeFields?: MergeFieldItem[]; // 머지필드 목록 — 있을 때만 mergefield 드롭다운 노출\n}\n\nexport function EditorToolbar({ editor, toolbar, mergeFields = [] }: EditorToolbarProps) {\n if (!editor) return null;\n\n const show = (key: string) => !toolbar || toolbar.includes(key);\n const run = (fn: (chain: ChainedCommands) => ChainedCommands) => fn(editor.chain().focus()).run();\n\n const currentBlock = editor.isActive('heading', { level: 1 })\n ? 'h1'\n : editor.isActive('heading', { level: 2 })\n ? 'h2'\n : editor.isActive('heading', { level: 3 })\n ? 'h3'\n : 'paragraph';\n const setBlock = (key: string, close: () => void) => {\n if (key === 'paragraph') run((c) => c.setParagraph());\n else run((c) => c.toggleHeading({ level: Number(key[1]) as 1 | 2 | 3 }));\n close();\n };\n\n const currentColor: string | null = editor.getAttributes('textStyle').color ?? null;\n const currentHighlight: string | null = editor.getAttributes('highlight').color ?? null;\n const setColor = (value: string | null) =>\n value ? run((c) => c.setColor(value)) : run((c) => c.unsetColor());\n const setHighlight = (value: string | null) =>\n value ? run((c) => c.setHighlight({ color: value })) : run((c) => c.unsetHighlight());\n\n const tableActive = editor.isActive('table');\n\n // 그룹별 노출 컨트롤 — show()로 필터한 뒤, 비어있지 않은 그룹만 구분선으로 잇는다.\n const groups = [\n // 실행 취소 / 다시 실행 (맨 앞)\n [\n show('undo') && (\n <ToolbarButton key=\"undo\" icon={Undo2} label=\"실행 취소\" disabled={!editor.can().undo()} onClick={() => run((c) => c.undo())} />\n ),\n show('redo') && (\n <ToolbarButton key=\"redo\" icon={Redo2} label=\"다시 실행\" disabled={!editor.can().redo()} onClick={() => run((c) => c.redo())} />\n ),\n ],\n // 머지필드 — mergeFields가 있을 때만\n [\n show('mergefield') && mergeFields.length > 0 && (\n <Popover\n key=\"mergefield\"\n placement=\"auto-left\"\n menuWidth={180}\n trigger={<DropTrigger icon={Braces} label=\"머지필드\" />}\n >\n {(close) => <MergeFieldMenu editor={editor} mergeFields={mergeFields} close={close} />}\n </Popover>\n ),\n ],\n // 블록 타입\n [\n show('block') && (\n <Popover\n key=\"block\"\n placement=\"auto-left\"\n menuWidth={80}\n trigger={\n <DropTrigger\n icon={Type}\n label={BLOCK_OPTIONS.find((b) => b.key === currentBlock)?.label}\n />\n }\n >\n {(close) => (\n <PopoverMenu width={80}>\n <ListGroup>\n {BLOCK_OPTIONS.map((b) => (\n <List\n key={b.key}\n title={b.label}\n selected={currentBlock === b.key}\n onClick={() => setBlock(b.key, close)}\n />\n ))}\n </ListGroup>\n </PopoverMenu>\n )}\n </Popover>\n ),\n ],\n // 강조\n [\n show('bold') && (\n <ToolbarButton key=\"bold\" icon={Bold} label=\"굵게\" active={editor.isActive('bold')} onClick={() => run((c) => c.toggleBold())} />\n ),\n show('italic') && (\n <ToolbarButton key=\"italic\" icon={Italic} label=\"기울임\" active={editor.isActive('italic')} onClick={() => run((c) => c.toggleItalic())} />\n ),\n show('underline') && (\n <ToolbarButton key=\"underline\" icon={Underline} label=\"밑줄\" active={editor.isActive('underline')} onClick={() => run((c) => c.toggleUnderline())} />\n ),\n show('strike') && (\n <ToolbarButton key=\"strike\" icon={Strikethrough} label=\"취소선\" active={editor.isActive('strike')} onClick={() => run((c) => c.toggleStrike())} />\n ),\n ],\n // 색\n [\n show('color') && (\n <Popover key=\"color\" placement=\"auto-left\" menuWidth={80} trigger={<DropTrigger icon={Baseline} ariaLabel=\"글자 색\" />}>\n {() => <ColorPanel palette={editorTextPalette} current={currentColor} onPick={setColor} />}\n </Popover>\n ),\n show('highlight') && (\n <Popover key=\"highlight\" placement=\"auto-left\" menuWidth={80} trigger={<DropTrigger icon={Highlighter} ariaLabel=\"형광펜\" />}>\n {() => <ColorPanel palette={editorHighlightPalette} current={currentHighlight} onPick={setHighlight} />}\n </Popover>\n ),\n ],\n // 정렬\n [\n show('align') && (\n <ToolbarButton key=\"al\" icon={AlignLeft} label=\"왼쪽 정렬\" active={editor.isActive({ textAlign: 'left' })} onClick={() => run((c) => c.setTextAlign('left'))} />\n ),\n show('align') && (\n <ToolbarButton key=\"ac\" icon={AlignCenter} label=\"가운데 정렬\" active={editor.isActive({ textAlign: 'center' })} onClick={() => run((c) => c.setTextAlign('center'))} />\n ),\n show('align') && (\n <ToolbarButton key=\"ar\" icon={AlignRight} label=\"오른쪽 정렬\" active={editor.isActive({ textAlign: 'right' })} onClick={() => run((c) => c.setTextAlign('right'))} />\n ),\n show('align') && (\n <ToolbarButton key=\"aj\" icon={AlignJustify} label=\"양쪽 정렬\" active={editor.isActive({ textAlign: 'justify' })} onClick={() => run((c) => c.setTextAlign('justify'))} />\n ),\n ],\n // 삽입\n [\n show('link') && (\n <Popover key=\"link\" placement=\"auto-left\" menuWidth={300} trigger={<DropTrigger icon={LinkIcon} ariaLabel=\"링크\" />}>\n {(close) => <LinkForm editor={editor} close={close} />}\n </Popover>\n ),\n show('table') && (\n <Popover key=\"table\" placement=\"auto-left\" menuWidth={140} trigger={<DropTrigger icon={TableIcon} ariaLabel=\"표\" />}>\n {(close) => (\n <PopoverMenu width={140}>\n <ListGroup>\n <List title=\"표 삽입 (3×3)\" onClick={() => { run((c) => c.insertTable({ rows: 3, cols: 3, withHeaderRow: true })); close(); }} />\n <List title=\"위에 행 추가\" disabled={!tableActive} onClick={() => { run((c) => c.addRowBefore()); close(); }} />\n <List title=\"아래에 행 추가\" disabled={!tableActive} onClick={() => { run((c) => c.addRowAfter()); close(); }} />\n <List title=\"행 삭제\" disabled={!tableActive} onClick={() => { run((c) => c.deleteRow()); close(); }} />\n <List title=\"왼쪽에 열 추가\" disabled={!tableActive} onClick={() => { run((c) => c.addColumnBefore()); close(); }} />\n <List title=\"오른쪽에 열 추가\" disabled={!tableActive} onClick={() => { run((c) => c.addColumnAfter()); close(); }} />\n <List title=\"열 삭제\" disabled={!tableActive} onClick={() => { run((c) => c.deleteColumn()); close(); }} />\n <List title=\"표 삭제\" disabled={!tableActive} onClick={() => { run((c) => c.deleteTable()); close(); }} />\n </ListGroup>\n </PopoverMenu>\n )}\n </Popover>\n ),\n show('image') && (\n <Popover key=\"image\" placement=\"auto-left\" menuWidth={300} trigger={<DropTrigger icon={ImageIcon} ariaLabel=\"이미지\" />}>\n {(close) => <ImageForm editor={editor} close={close} />}\n </Popover>\n ),\n show('hr') && (\n <ToolbarButton key=\"hr\" icon={Minus} label=\"구분선\" onClick={() => run((c) => c.setHorizontalRule())} />\n ),\n ],\n ]\n .map((g) => g.filter(Boolean))\n .filter((g) => g.length);\n\n return (\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-spacing-2\">\n {groups.map((g, i) => (\n <Fragment key={i}>\n {i > 0 && <Divider />}\n {g}\n </Fragment>\n ))}\n </div>\n );\n}\n","// MergeField — Tiptap 인라인 atom 노드. 머지필드({지원자명} 등)를 편집기 안에서\n// 삭제 가능한 '칩'으로 렌더한다.\n//\n// NodeView는 plain DOM으로 구성한다(React 인라인-atom NodeView는 빈 렌더가 잦아 회피).\n// 시각은 chip-* 시멘틱 토큰(DS Chip과 동일 클래스)으로 맞춘다 — 규칙 1(토큰 경유) 유지.\n//\n// 데이터 단위(HTML)에는 <span data-merge-field=\"{값}\">{값}</span> 로 직렬화된다(renderHTML):\n// - 재오픈(setContent) 시 parseHTML이 이 span을 다시 mergeField 노드(칩)로 복원\n// - 안에 원문 {값} 텍스트가 남아 백엔드 머지 치환 로직도 그대로 동작\n// - preview 모드(raw span 렌더)는 index.css의 [data-merge-field] 규칙으로 칩 모양을 받는다\n// ※ NodeView의 DOM에는 data-merge-field를 두지 않는다(edit 모드에서 CSS 칩 스타일 이중 적용 방지 —\n// 직렬화용 data 속성은 renderHTML이 담당).\n// atom 이라 편집기 안에서 한 덩어리로 선택·백스페이스 삭제되고, 칩의 X 버튼으로도 삭제된다.\nimport { Node, mergeAttributes } from '@tiptap/core';\n\n// 머지필드 칩은 blue color 고정.\nconst CHIP_CLASS =\n 'inline-flex items-center gap-spacing-3 rounded-round-4 border border-chip-blue-default-line ' +\n 'bg-chip-blue-default-bg pl-spacing-4 pr-spacing-3 py-spacing-1 align-middle font-pretendard ' +\n 'text-12 text-chip-blue-default-text transition-colors hover:border-chip-blue-hover-line hover:bg-chip-blue-hover-bg';\n\n// lucide X (size 12) — Chip의 삭제 아이콘과 동일 모양.\nconst X_SVG =\n '<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" ' +\n 'stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">' +\n '<path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>';\n\nexport const MergeFieldNode = Node.create({\n name: 'mergeField',\n group: 'inline',\n inline: true,\n atom: true,\n selectable: true,\n\n addAttributes() {\n return {\n value: {\n default: '',\n parseHTML: (el) => el.getAttribute('data-merge-field') || el.textContent || '',\n renderHTML: (attrs) => ({ 'data-merge-field': attrs.value }),\n },\n };\n },\n\n parseHTML() {\n return [{ tag: 'span[data-merge-field]' }];\n },\n\n // 직렬화(getHTML): <span data-merge-field=\"{값}\">{값}</span> (원문 텍스트 포함)\n renderHTML({ HTMLAttributes, node }) {\n return ['span', mergeAttributes(HTMLAttributes), node.attrs.value];\n },\n\n addNodeView() {\n return ({ node, editor, getPos }) => {\n const dom = document.createElement('span');\n dom.className = CHIP_CLASS;\n dom.contentEditable = 'false';\n\n const label = document.createElement('span');\n label.className = 'whitespace-nowrap';\n label.textContent = node.attrs.value;\n dom.appendChild(label);\n\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'flex shrink-0 items-center justify-center'; // 색은 부모(text-chip-gray-default-text) 상속\n btn.setAttribute('aria-label', '머지필드 삭제');\n btn.innerHTML = X_SVG;\n // mousedown 기본동작(포커스 이동/셀렉션 변경) 차단 후 click으로 노드 삭제.\n btn.addEventListener('mousedown', (e) => e.preventDefault());\n btn.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (typeof getPos !== 'function') return;\n const pos = getPos();\n if (pos == null) return;\n editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).run();\n });\n dom.appendChild(btn);\n\n return { dom };\n };\n },\n});\n","// Editor — Tiptap(ProseMirror) 기반 리치 텍스트 에디터 (완전 옵션형)\n// 이메일 템플릿·공고 상세·약관·문자/메일 본문 작성과, 개발자·운영자의 HTML 구조 확인을 함께 대응한다.\n// 엔진(Tiptap)은 이 파일 내부에만 격리하고, 외부에는 value/onChange·mode·toolbar 등 엔진 독립 props만 노출한다.\n//\n// - value / defaultValue / onChange(html): 데이터 단위는 HTML 문자열 하나. controlled(value) 또는 uncontrolled(defaultValue).\n// - mode / onModeChange: 'edit'(WYSIWYG) | 'source'(HTML 코드) | 'preview'(렌더 결과). controlled/내부 상태.\n// - toolbar: 노출할 서식 기능 키 배열(미지정 시 전체). EditorToolbar의 TOOLBAR_ITEMS 키 참고.\n// - readOnly: 편집 비활성(툴바 숨김, 본문 read-only). 소스/미리보기 모드 전환은 유지(HTML 확인용).\n// - allowSourceEdit: 소스 모드에서 HTML 직접 편집 허용(false면 읽기 전용 보기).\n// - placeholder / minHeight / maxHeight: 빈 본문 안내 · 본문 높이(maxHeight 시 ScrollArea 세로 스크롤).\n// - width: 'fill'(부모 전체 폭, 기본) | 숫자(px) | CSS 길이 문자열.\n// 색·간격·보더는 editor-*/table-*/spacing-*/round-* 토큰만 사용(하드코딩 금지).\nimport { useEffect, useLayoutEffect, useReducer, useRef, useState } from 'react';\nimport type { ChangeEvent, ComponentPropsWithoutRef, ReactNode } from 'react';\nimport { useEditor, EditorContent } from '@tiptap/react';\nimport { StarterKit } from '@tiptap/starter-kit';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport { Color } from '@tiptap/extension-color';\nimport { Highlight } from '@tiptap/extension-highlight';\nimport { TextAlign } from '@tiptap/extension-text-align';\nimport { Image } from '@tiptap/extension-image';\nimport { Table, TableRow, TableHeader, TableCell } from '@tiptap/extension-table';\nimport { ScrollArea } from './ScrollArea';\nimport { EditorToolbar } from './EditorToolbar';\nimport { SegmentControlGroup } from './SegmentControl';\nimport { MergeFieldNode } from './MergeFieldNode';\n\n// 모드 값 — 'edit'(WYSIWYG) | 'source'(HTML 코드) | 'preview'(렌더 결과)\ntype EditorMode = 'edit' | 'source' | 'preview';\n\nconst MODE_TABS: { value: EditorMode; label: string }[] = [\n { value: 'edit', label: '편집' },\n { value: 'source', label: 'HTML' },\n { value: 'preview', label: '미리보기' },\n];\n\n// Tiptap 확장 구성 — StarterKit에 link/underline가 이미 포함되어 있어 별도 등록하지 않는다(중복 방지).\n// Color는 TextStyle에 의존하므로 TextStyle을 먼저 둔다.\nfunction buildExtensions() {\n return [\n StarterKit.configure({\n link: { openOnClick: false, HTMLAttributes: { rel: 'noopener noreferrer nofollow' } },\n }),\n TextStyle,\n Color,\n Highlight.configure({ multicolor: true }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image,\n Table.configure({ resizable: false }),\n TableRow,\n TableHeader,\n TableCell,\n MergeFieldNode, // 머지필드를 삭제 가능한 인라인 칩으로 렌더(<span data-merge-field>로 직렬화)\n ];\n}\n\n// 모드 탭(편집/HTML/미리보기) — 공용 세그먼트 컨트롤(size 24).\n// 읽기 전용이면 편집 탭을 빼고 HTML·미리보기만 노출한다.\n// showSource=false면 HTML(소스) 탭을 숨긴다(예: SMS 평문 안내문).\nfunction ModeTabs({\n mode,\n onChange,\n readOnly,\n showSource,\n}: {\n mode: EditorMode;\n onChange: (mode: EditorMode) => void;\n readOnly: boolean;\n showSource: boolean;\n}) {\n let items = readOnly ? MODE_TABS.filter((t) => t.value !== 'edit') : MODE_TABS;\n if (!showSource) items = items.filter((t) => t.value !== 'source');\n return (\n <SegmentControlGroup\n size=\"24\"\n gap=\"4\"\n value={mode}\n onChange={(v) => onChange?.(v as EditorMode)} /* items가 EditorMode 값만 담으므로 안전한 좁힘 */\n items={items}\n />\n );\n}\n\ninterface EditorProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onChange' | 'placeholder'> {\n value?: string; // controlled HTML 값\n defaultValue?: string; // uncontrolled 초기 HTML\n onChange?: (html: string) => void;\n mode?: EditorMode; // controlled 모드\n onModeChange?: (mode: EditorMode) => void;\n toolbar?: string[]; // 노출할 서식 기능 키 배열(미지정 시 전체) — EditorToolbar TOOLBAR_ITEMS 키\n mergeFields?: (string | { label: string; value: string })[]; // 머지필드 목록: 문자열 또는 {label,value}[] — 툴바 '머지필드' 드롭다운(search list)\n readOnly?: boolean;\n showSource?: boolean; // HTML(소스) 모드 탭 노출 여부(false면 편집/미리보기만)\n placeholder?: ReactNode;\n minHeight?: number | string;\n maxHeight?: number | string;\n width?: number | string; // 'fill'(부모 전체 폭) | 숫자(px) | CSS 길이 문자열('480px','60%' 등)\n allowSourceEdit?: boolean; // 소스 모드에서 HTML 직접 편집 허용(false면 읽기 전용 보기)\n}\n\nexport function Editor({\n value,\n defaultValue = '',\n onChange,\n mode: modeProp,\n onModeChange,\n toolbar,\n mergeFields = [],\n readOnly = false,\n showSource = true,\n placeholder = '내용을 입력하세요.',\n minHeight = 240,\n maxHeight,\n width = 'fill',\n allowSourceEdit = true,\n className = '',\n ...props\n}: EditorProps) {\n const valueControlled = value !== undefined;\n const modeControlled = modeProp !== undefined;\n // 읽기 전용이면 편집 모드가 없으므로 미리보기를 기본으로 한다.\n const [internalMode, setInternalMode] = useState<EditorMode>(() => (readOnly ? 'preview' : 'edit'));\n const mode = modeControlled ? modeProp : internalMode;\n // controlled value 교체(setContent) 후 placeholder(isEmpty) 조건을 다시 평가하기 위한 강제 재렌더 트리거.\n const [, bumpRender] = useReducer((x) => x + 1, 0);\n\n // 소스 모드에서 편집 중인 HTML 초안(편집 모드와 분리해 커서 튐 방지, 모드 이탈 시 에디터에 반영)\n const [sourceDraft, setSourceDraft] = useState('');\n const sourceRef = useRef<HTMLTextAreaElement | null>(null);\n\n // onChange 최신값을 ref로 유지(editor 'update' 핸들러의 stale 클로저 방지)\n const onChangeRef = useRef(onChange);\n useEffect(() => {\n onChangeRef.current = onChange;\n }, [onChange]);\n\n const editor = useEditor({\n editable: !readOnly,\n content: valueControlled ? value : defaultValue,\n extensions: buildExtensions(),\n // 첫 렌더에서 즉시 뷰를 만들지 않는다(effect에서 생성). React StrictMode가 dev에서\n // 마운트를 2번 돌릴 때 뷰가 파괴/재생성되며 throw하는 것을 막는다(Tiptap React 권장값).\n immediatelyRender: false,\n // Tiptap v3는 기본이 false(트랜잭션이 React 재렌더를 안 일으킴) — 이 컴포넌트는 툴바\n // isActive/undo 상태·placeholder(isEmpty)를 렌더에서 직접 읽으므로 v2 방식으로 되돌린다.\n // 끄면 uncontrolled에서 placeholder가 입력 텍스트에 겹치고, 커서만 옮기면 툴바가 이전\n // 상태로 굳는다(2026-07-07 감사).\n shouldRerenderOnTransaction: true,\n });\n\n // 본문 변경 → onChange(html) 방출\n useEffect(() => {\n if (!editor) return;\n const handler = () => onChangeRef.current?.(editor.getHTML());\n editor.on('update', handler);\n return () => {\n editor.off('update', handler); // off는 체이닝용 Editor를 반환 — cleanup은 void여야 해 블록으로\n };\n }, [editor]);\n\n // readOnly 토글 반영\n useEffect(() => {\n editor?.setEditable(!readOnly);\n }, [editor, readOnly]);\n\n // 읽기 전용으로 바뀌면 편집 모드(탭이 사라짐)에 머물지 않고 미리보기로 이동(uncontrolled).\n useEffect(() => {\n if (readOnly && !modeControlled && internalMode === 'edit') {\n // readOnly 전환 시 편집 모드 → 미리보기로 1회 이동 — 의도된 effect 내 setState\n setInternalMode('preview'); // eslint-disable-line react-hooks/set-state-in-effect\n }\n }, [readOnly, modeControlled, internalMode]);\n\n // HTML 탭이 숨겨졌는데(showSource=false) 소스 모드에 머물면 편집으로 복귀(탭 전환 등으로 갇힘 방지).\n useEffect(() => {\n if (!showSource && !modeControlled && internalMode === 'source') {\n setInternalMode('edit'); // eslint-disable-line react-hooks/set-state-in-effect\n }\n }, [showSource, modeControlled, internalMode]);\n\n // controlled value 외부 변경 동기화(편집 결과로 같은 값이 돌아오면 no-op)\n // useLayoutEffect + bumpRender: 콘텐츠 교체 직후(페인트 전) 재렌더해 placeholder(isEmpty)를 다시 평가한다.\n // (안 하면 채널 전환 등 value가 바뀔 때 stale한 isEmpty로 그려진 placeholder가 새 본문과 겹쳐 보인다.)\n useLayoutEffect(() => {\n if (!editor || !valueControlled) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n // 소스 모드 열람 중 외부에서 value가 바뀌면(템플릿 불러오기 등) 초안도 동기화 —\n // 안 하면 모드 이탈 시 stale 초안이 외부 변경분을 덮어쓴다(2026-07-07 감사)\n if (mode === 'source') {\n setSourceDraft((d) => (d === value ? d : value)); // eslint-disable-line react-hooks/set-state-in-effect\n }\n bumpRender();\n }\n }, [editor, value, valueControlled, mode]);\n\n const changeMode = (next: EditorMode) => {\n if (next === mode) return;\n if (editor) {\n // 소스 모드를 떠날 때: 초안 HTML을 에디터에 반영하고 onChange 방출\n if (mode === 'source') {\n editor.commands.setContent(sourceDraft, { emitUpdate: false });\n onChangeRef.current?.(editor.getHTML());\n }\n // 소스 모드로 들어갈 때: 현재 에디터 HTML로 초안 시드\n if (next === 'source') {\n setSourceDraft(editor.getHTML());\n }\n }\n if (!modeControlled) setInternalMode(next);\n onModeChange?.(next);\n };\n\n const handleSourceChange = (e: ChangeEvent<HTMLTextAreaElement>) => {\n const v = e.target.value;\n setSourceDraft(v);\n onChangeRef.current?.(v);\n };\n\n // 소스 textarea를 내용 높이에 맞춰 늘린다 — 자체(네이티브) 스크롤 대신 ScrollArea 오버레이 스크롤로 위임(규칙 6).\n useLayoutEffect(() => {\n if (mode !== 'source') return;\n const el = sourceRef.current;\n if (!el) return;\n el.style.height = 'auto';\n el.style.height = `${el.scrollHeight}px`;\n }, [mode, sourceDraft]);\n\n const sourceEditable = allowSourceEdit && !readOnly;\n // 전체 문서 직렬화(getHTML)는 미리보기 모드에서만 — edit 모드 타이핑마다 직렬화하지 않는다(2026-07-07 감사)\n const previewHtml = mode === 'preview' && editor ? editor.getHTML() : '';\n const bodyMinHeight = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n const widthStyle = width === 'fill' ? '100%' : typeof width === 'number' ? `${width}px` : width;\n\n // 본문 영역 — maxHeight가 있으면 ScrollArea로 감싼다(세로 오버레이 스크롤바, 규칙 6).\n const wrapScroll = (node: ReactNode) =>\n maxHeight != null ? <ScrollArea maxHeight={maxHeight}>{node}</ScrollArea> : node;\n\n let body: ReactNode;\n if (mode === 'source') {\n body = (\n <textarea\n ref={sourceRef}\n value={sourceDraft}\n onChange={handleSourceChange}\n readOnly={!sourceEditable}\n spellCheck={false}\n className=\"block w-full resize-none overflow-hidden bg-editor-source-bg px-spacing-7 py-spacing-6 font-mono text-13 text-editor-source-text outline-none\"\n style={{ minHeight: bodyMinHeight }}\n />\n );\n // 소스 영역은 어두운 배경이라 흰색(light) 스크롤바 변형을 쓴다.\n body = maxHeight != null ? <ScrollArea maxHeight={maxHeight} variant=\"light\">{body}</ScrollArea> : body;\n } else if (mode === 'preview') {\n body = wrapScroll(\n <div\n className=\"tiptap-prose px-spacing-7 py-spacing-6\"\n style={{ minHeight: bodyMinHeight }}\n dangerouslySetInnerHTML={{ __html: previewHtml }}\n />,\n );\n } else {\n // edit\n body = wrapScroll(\n <div\n className=\"relative tiptap-prose cursor-text px-spacing-7 py-spacing-6\"\n style={{ minHeight: bodyMinHeight }}\n onMouseDown={(e) => {\n // 콘텐츠(.tiptap) 밖(패딩·첫 줄 아래 빈 영역)을 클릭하면 에디터 끝에 포커스한다 —\n // 첫 줄까지 가지 않아도 박스 전체가 클릭 영역이 되도록.\n if (editor && !(e.target as HTMLElement).closest('.tiptap')) {\n e.preventDefault();\n editor.commands.focus('end');\n }\n }}\n >\n {editor?.isEmpty && (\n <p className=\"pointer-events-none absolute left-spacing-7 top-spacing-6 text-14 leading-30 text-editor-placeholder\">\n {placeholder}\n </p>\n )}\n <EditorContent editor={editor} />\n </div>,\n );\n }\n\n return (\n <div className={`overflow-hidden rounded-round-4 border border-editor-outline bg-white ${className}`} style={{ width: widthStyle }} {...props}>\n {/* 헤더: (편집 모드·편집 가능 시) 서식 툴바 + 모드 탭 */}\n <div className=\"border-b border-editor-divider bg-editor-toolbar-bg\">\n <div className=\"flex items-center justify-between gap-spacing-5 px-spacing-5 py-spacing-3\">\n {mode === 'edit' && !readOnly ? (\n <EditorToolbar editor={editor} toolbar={toolbar} mergeFields={mergeFields} />\n ) : (\n <span className=\"text-12 text-font-icon-5\">\n {mode === 'source' ? 'HTML 소스' : mode === 'preview' ? '미리보기' : ''}\n </span>\n )}\n <div className=\"shrink-0\">\n <ModeTabs mode={mode} onChange={changeMode} readOnly={readOnly} showSource={showSource} />\n </div>\n </div>\n </div>\n\n {body}\n </div>\n );\n}\n","// NoticeWritingTemplate — 안내문 작성 템플릿 (Figma \"Notice Writing Template\", state=site/email/sms)\n// 채널 탭(채용사이트/이메일/SMS 안내문) + 제목 입력 + 첨부파일 + 본문 에디터를 한 덩어리로 묶은\n// 완전 옵션형 템플릿(규칙 4 — Tabs·Input·FileUploadButton·Editor 조립).\n// - site/email : 제목 Input + [첨부파일] FileUploadButton(업로드 메뉴 팝오버) + 풀 툴바 Editor(머지필드 포함)\n// - sms : 바이트 안내 문구 + 축약 툴바 Editor(undo/redo·머지필드만, SMS 평문)\n// 채널·제목·본문·첨부는 내부 상태로 동작하며(채널만 controlled 지원), 색·간격은 하위 컴포넌트 토큰을 따른다.\n// 작성 내용은 onChange 스냅샷으로 반출한다(2026-07-07 감사 — 값 반출 계약 없이는 저장/발송이 불가능했음).\nimport { useState } from 'react';\nimport type { ComponentPropsWithoutRef } from 'react';\nimport { Paperclip } from 'lucide-react';\nimport { Tabs } from './Tabs';\nimport { Input } from './Input';\nimport { FileUploadButton } from './FileUploadButton';\nimport { Editor } from './Editor';\n\n// 채널 값 — 'site' | 'email' | 'sms'\ntype NoticeChannel = 'site' | 'email' | 'sms';\n\n// 채널 정의 — 탭 라벨과 제목 placeholder. sms는 제목 없이 안내 문구를 노출한다.\nconst CHANNELS: { value: NoticeChannel; label: string; titlePlaceholder?: string }[] = [\n { value: 'site', label: '채용사이트 안내문', titlePlaceholder: '채용 사이트안내문 제목을 입력하세요' },\n { value: 'email', label: '이메일 안내문', titlePlaceholder: '이메일 안내문 제목을 입력하세요' },\n { value: 'sms', label: 'SMS 안내문' },\n];\n\nconst SMS_INFO = '90byte를 초과(한글 45자)하면 멀티 문자로 전환되며, 발송 비용이 변경될 수 있습니다.';\n// SMS는 평문이라 서식 없이 실행취소/다시실행 + 머지필드만 노출한다.\nconst SMS_TOOLBAR = ['undo', 'redo', 'mergefield'];\n\n// 첨부 항목 — 표시용 name/size + 원본 File(.file — 소비자가 실제 업로드에 사용)\ninterface NoticeAttachment {\n name: string;\n size: number; // MB(소수 1자리)\n id: string;\n file: File;\n}\n\n// onChange 반출 스냅샷 — { channel, titles: {site,email,sms}, bodies: {site,email,sms},\n// attachments: {site,email,sms} } (attachments 항목에 원본 File은 .file)\ninterface NoticeWritingValues {\n channel: NoticeChannel;\n titles: Record<NoticeChannel, string>;\n bodies: Record<NoticeChannel, string>;\n attachments: Record<NoticeChannel, NoticeAttachment[]>;\n}\n\ninterface NoticeWritingTemplateProps extends Omit<ComponentPropsWithoutRef<'div'>, 'onChange'> {\n channel?: NoticeChannel; // controlled 채널값('site'|'email'|'sms')\n defaultChannel?: NoticeChannel; // uncontrolled 초기 채널\n onChannelChange?: (channel: NoticeChannel) => void;\n onChange?: (values: NoticeWritingValues) => void; // 작성 내용이 바뀔 때마다 전체 스냅샷 반출\n showTabs?: boolean; // 채널 탭 노출 여부(false면 단일 채널 모드)\n tabVariant?: 'fill' | 'hug'; // 탭 너비 타입: 'fill'(균등 분할, 기본) | 'hug'(내용 폭)\n enabledChannels?: NoticeChannel[]; // 활성 채널 목록(['site','email','sms']) — 미지정 시 전체 활성. 밖의 탭은 disabled\n mergeFields?: (string | { label: string; value: string })[]; // Editor 머지필드 목록\n defaultBodies?: Partial<Record<NoticeChannel, string>>; // 채널별 초기 본문 HTML — { site?, email?, sms? }\n showAttach?: boolean; // 첨부파일 버튼 노출 여부(false면 제목 입력만 전체 폭)\n maxAttachments?: number; // 첨부 최대 개수\n attachGuide?: string; // 업로드 메뉴 안내 문구\n attachAccept?: string; // <input accept> 필터(예: '.pdf,.doc') — guide와 직접 일치시킬 것\n editorMinHeight?: number | string; // 본문 최소 높이(px)\n editorMaxHeight?: number | string; // 지정 시 본문 세로 스크롤(ScrollArea)\n editorReadOnly?: boolean; // 본문 편집 비활성(툴바 숨김·읽기 전용)\n editorPlaceholder?: string; // 본문 빈 안내 문구(Editor 기본값 사용 시 생략)\n}\n\nconst emptyByChannel = (): Record<NoticeChannel, NoticeAttachment[]> => ({ site: [], email: [], sms: [] });\n\nexport function NoticeWritingTemplate({\n channel: channelProp,\n defaultChannel = 'site',\n onChannelChange,\n onChange,\n showTabs = true,\n tabVariant = 'fill',\n enabledChannels,\n mergeFields = [],\n defaultBodies,\n showAttach = true,\n maxAttachments = 5,\n attachGuide = '파일을 첨부하세요 (파일당 최대 10MB).',\n attachAccept,\n editorMinHeight = 360,\n editorMaxHeight,\n editorReadOnly = false,\n editorPlaceholder,\n className = '',\n ...props\n}: NoticeWritingTemplateProps) {\n const controlled = channelProp !== undefined;\n const [internal, setInternal] = useState<NoticeChannel>(defaultChannel);\n const isEnabled = (v: NoticeChannel) => !enabledChannels || enabledChannels.includes(v);\n const rawChannel = controlled ? channelProp : internal;\n // 현재 채널이 비활성이면 첫 활성 채널로 표시 폴백(파생값 — 다시 활성화되면 원래 채널로 복귀)\n const channel = isEnabled(rawChannel)\n ? rawChannel\n : (CHANNELS.find((c) => isEnabled(c.value))?.value ?? rawChannel);\n const setChannel = (v: NoticeChannel) => {\n if (!controlled) setInternal(v);\n onChannelChange?.(v);\n };\n\n // 채널별 제목·본문·첨부는 내부 상태로 유지(채널 전환 시 각자 보존).\n // 변경 시마다 onChange로 전체 스냅샷을 반출한다 — 소비자는 이 값으로 저장/발송한다.\n const [titles, setTitles] = useState<Record<NoticeChannel, string>>({ site: '', email: '', sms: '' });\n const [bodies, setBodies] = useState<Record<NoticeChannel, string>>(() => ({ site: '', email: '', sms: '', ...(defaultBodies || {}) }));\n const [files, setFiles] = useState<Record<NoticeChannel, NoticeAttachment[]>>(emptyByChannel);\n\n const emitChange = (patch: Partial<NoticeWritingValues>) =>\n onChange?.({ channel, titles, bodies, attachments: files, ...patch });\n\n const current = CHANNELS.find((c) => c.value === channel) ?? CHANNELS[0];\n const isSms = channel === 'sms';\n\n const setTitle = (value: string) => {\n const next = { ...titles, [channel]: value };\n setTitles(next);\n emitChange({ titles: next });\n };\n const setBody = (html: string) => {\n const next = { ...bodies, [channel]: html };\n setBodies(next);\n emitChange({ bodies: next });\n };\n const addFiles = (fileList: FileList | File[]) => {\n const next = {\n ...files,\n [channel]: [\n ...files[channel],\n ...Array.from(fileList).map((f) => ({\n name: f.name,\n size: Math.max(0.1, Math.round((f.size / 1048576) * 10) / 10), // MB(소수 1자리)\n id: `${f.name}-${f.size}-${Date.now()}`,\n file: f, // 원본 File — 소비자가 실제 업로드에 사용(표시용 name/size만 남기면 발송 불가)\n })),\n ].slice(0, maxAttachments),\n };\n setFiles(next);\n emitChange({ attachments: next });\n };\n const deleteFile = (_file: unknown, index: number) => {\n const next = { ...files, [channel]: files[channel].filter((_, i) => i !== index) };\n setFiles(next);\n emitChange({ attachments: next });\n };\n\n return (\n <div className={`flex w-full flex-col gap-spacing-6 ${className}`} {...props}>\n {showTabs && (\n <Tabs\n items={CHANNELS.map(({ value, label }) => ({ value, label, disabled: !isEnabled(value) }))}\n value={channel}\n onChange={setChannel}\n variant={tabVariant}\n />\n )}\n\n {isSms ? (\n <p className=\"text-12 text-font-icon-4\">{SMS_INFO}</p>\n ) : (\n <div className=\"flex items-start gap-spacing-6\">\n <div className=\"min-w-0 flex-1\">\n <Input\n width=\"100%\"\n placeholder={current.titlePlaceholder}\n value={titles[channel]}\n onChange={(e) => setTitle(e.target.value)}\n />\n </div>\n {showAttach && (\n <FileUploadButton\n triggerText=\"첨부파일\"\n buttonProps={{ leftIcon: Paperclip }}\n placement=\"auto-right\"\n menuWidth={320}\n files={files[channel]}\n guide={attachGuide}\n accept={attachAccept}\n maxCount={maxAttachments}\n onAdd={addFiles}\n onDelete={deleteFile}\n />\n )}\n </div>\n )}\n\n <Editor\n value={bodies[channel]}\n onChange={setBody}\n mergeFields={mergeFields}\n toolbar={isSms ? SMS_TOOLBAR : undefined}\n showSource={!isSms}\n minHeight={editorMinHeight}\n maxHeight={editorMaxHeight}\n readOnly={editorReadOnly}\n {...(editorPlaceholder ? { placeholder: editorPlaceholder } : {})}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -24,7 +24,6 @@ export * from './components/List';
24
24
  export * from './components/ListEmpty';
25
25
  export * from './components/ListGroup';
26
26
  export * from './components/Modal';
27
- export * from './components/NoticeWritingTemplate';
28
27
  export * from './components/Pagination';
29
28
  export * from './components/Popover';
30
29
  export * from './components/PopoverMenu';