@loword/loword-design-system 0.4.2 → 0.5.0
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.
- package/dist/components/LDDatePicker/LDDatePicker.js +1 -1
- package/dist/components/LDDateRangePicker/LDDateRangePicker.js +1 -1
- package/dist/components/LDEditor/LDEditor.d.ts.map +1 -1
- package/dist/components/LDEditor/LDEditor.js +48 -2
- package/dist/components/LDEditor/LDViewer.d.ts.map +1 -1
- package/dist/components/LDEditor/LDViewer.js +4 -1
- package/dist/components/LDEditor/extensions.d.ts +2 -1
- package/dist/components/LDEditor/extensions.d.ts.map +1 -1
- package/dist/components/LDEditor/extensions.js +11 -1
- package/dist/index.css +55 -0
- package/package.json +6 -1
- package/src/styles/components/editor.css +62 -0
|
@@ -16,7 +16,7 @@ const variantColorClasses = {
|
|
|
16
16
|
filled: {
|
|
17
17
|
neutral: 'bg-neutral50 text-neutral900 border-none outline-2 outline-transparent outline-solid -outline-offset-2 focus-within:bg-tintBlue focus-within:outline-lb500',
|
|
18
18
|
point: 'bg-tintBlue text-neutral900 border-none outline-2 outline-transparent outline-solid -outline-offset-2 focus-within:outline-lb500',
|
|
19
|
-
danger: 'bg-tintRed text-neutral900 border-none outline-2 outline-
|
|
19
|
+
danger: 'bg-tintRed text-neutral900 border-none outline-2 outline-danger600 outline-solid -outline-offset-2',
|
|
20
20
|
},
|
|
21
21
|
outline: {
|
|
22
22
|
neutral: 'bg-transparent text-neutral900 border-none outline-2 outline-neutral100 outline-solid -outline-offset-2 focus-within:outline-lb500',
|
|
@@ -23,7 +23,7 @@ const variantColorClasses = {
|
|
|
23
23
|
filled: {
|
|
24
24
|
neutral: 'bg-neutral50 text-neutral900 border-none outline-2 outline-transparent outline-solid -outline-offset-2 focus-within:bg-tintBlue focus-within:outline-lb500',
|
|
25
25
|
point: 'bg-tintBlue text-neutral900 border-none outline-2 outline-transparent outline-solid -outline-offset-2 focus-within:outline-lb500',
|
|
26
|
-
danger: 'bg-tintRed text-neutral900 border-none outline-2 outline-
|
|
26
|
+
danger: 'bg-tintRed text-neutral900 border-none outline-2 outline-danger600 outline-solid -outline-offset-2',
|
|
27
27
|
},
|
|
28
28
|
outline: {
|
|
29
29
|
neutral: 'bg-transparent text-neutral900 border-none outline-2 outline-neutral100 outline-solid -outline-offset-2 focus-within:outline-lb500',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LDEditor.d.ts","sourceRoot":"","sources":["../../../src/components/LDEditor/LDEditor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LDEditor.d.ts","sourceRoot":"","sources":["../../../src/components/LDEditor/LDEditor.tsx"],"names":[],"mappings":"AA8IA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE;QAC7B,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9B;AAED,QAAA,MAAM,QAAQ,GAAI,sDAKf,aAAa,4CAYf,CAAC;AAwmBF,eAAe,QAAQ,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEditorState } from '@tiptap/react';
|
|
4
4
|
import { BubbleMenu } from '@tiptap/react/menus';
|
|
5
|
-
import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Bold, ChevronDown, Image as ImageIcon, Italic, Link2, List, ListOrdered, Redo, Strikethrough, Underline, Undo, Unlink, } from 'lucide-react';
|
|
5
|
+
import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Bold, ChevronDown, Image as ImageIcon, Italic, Link2, List, ListOrdered, Redo, Strikethrough, Table as TableIcon, Underline, Undo, Unlink, } from 'lucide-react';
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
7
|
import EditorBase, { useEditorContext } from '../../components/LDEditor/EditorBase.js';
|
|
8
8
|
import { createEditorExtensions } from '../../components/LDEditor/extensions.js';
|
|
@@ -35,6 +35,40 @@ const HEADINGS = [
|
|
|
35
35
|
{ label: '제목 2', level: 2 },
|
|
36
36
|
{ label: '제목 3', level: 3 },
|
|
37
37
|
];
|
|
38
|
+
const TABLE_GRID_MAX = 5;
|
|
39
|
+
const TABLE_ACTIONS = [
|
|
40
|
+
{ label: '위에 행 추가', run: (e) => e.chain().focus().addRowBefore().run() },
|
|
41
|
+
{ label: '아래 행 추가', run: (e) => e.chain().focus().addRowAfter().run() },
|
|
42
|
+
{
|
|
43
|
+
label: '왼쪽 열 추가',
|
|
44
|
+
run: (e) => e.chain().focus().addColumnBefore().run(),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: '오른쪽 열 추가',
|
|
48
|
+
run: (e) => e.chain().focus().addColumnAfter().run(),
|
|
49
|
+
},
|
|
50
|
+
{ label: '행 삭제', run: (e) => e.chain().focus().deleteRow().run() },
|
|
51
|
+
{ label: '열 삭제', run: (e) => e.chain().focus().deleteColumn().run() },
|
|
52
|
+
{
|
|
53
|
+
label: '헤더 행 토글',
|
|
54
|
+
run: (e) => e.chain().focus().toggleHeaderRow().run(),
|
|
55
|
+
},
|
|
56
|
+
{ label: '표 삭제', run: (e) => e.chain().focus().deleteTable().run() },
|
|
57
|
+
];
|
|
58
|
+
// 삽입할 표 크기를 호버(키보드는 포커스)로 고르는 5x5 그리드.
|
|
59
|
+
// Dropdown의 children은 렌더 콜백이라 훅을 쓸 수 없어 별도 컴포넌트로 뺀다.
|
|
60
|
+
const TableGridPicker = ({ onSelect, }) => {
|
|
61
|
+
const [size, setSize] = useState({ rows: 0, cols: 0 });
|
|
62
|
+
return (_jsxs("div", { className: "p-2", children: [_jsx("div", { className: "grid grid-cols-5 gap-1", children: Array.from({ length: TABLE_GRID_MAX * TABLE_GRID_MAX }, (_, index) => {
|
|
63
|
+
const rows = Math.floor(index / TABLE_GRID_MAX) + 1;
|
|
64
|
+
const cols = (index % TABLE_GRID_MAX) + 1;
|
|
65
|
+
const filled = rows <= size.rows && cols <= size.cols;
|
|
66
|
+
return (_jsx("button", { type: "button", "aria-label": `${rows}행 ${cols}열 표 삽입`, onMouseEnter: () => setSize({ rows, cols }), onFocus: () => setSize({ rows, cols }), onClick: (event) => {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
onSelect(rows, cols);
|
|
69
|
+
}, className: cn('size-5 cursor-pointer rounded-xs border border-neutral200', filled && 'border-blue-500 bg-blue-100') }, index));
|
|
70
|
+
}) }), _jsx("p", { className: "mt-2 text-center text-sm", children: size.rows ? `${size.rows} x ${size.cols}` : '크기 선택' })] }));
|
|
71
|
+
};
|
|
38
72
|
const iconBtn = 'p-1 rounded-lg cursor-pointer transition hover:bg-mono100 disabled:opacity-30 disabled:cursor-not-allowed';
|
|
39
73
|
const activeBtn = 'bg-mono100';
|
|
40
74
|
const LDDivider = () => _jsx("div", { className: "mx-1 h-6 w-px bg-neutral200" });
|
|
@@ -77,6 +111,7 @@ const EditorToolbar = ({ handleImageUpload, }) => {
|
|
|
77
111
|
canUndo: editor.can().undo(),
|
|
78
112
|
canRedo: editor.can().redo(),
|
|
79
113
|
isLink: editor.isActive('link'),
|
|
114
|
+
isTable: editor.isActive('table'),
|
|
80
115
|
hasSelection: !editor.state.selection.empty,
|
|
81
116
|
};
|
|
82
117
|
},
|
|
@@ -175,7 +210,18 @@ const EditorToolbar = ({ handleImageUpload, }) => {
|
|
|
175
210
|
setLinkUrl('');
|
|
176
211
|
close();
|
|
177
212
|
}, className: "rounded-lg border border-neutral200 px-3 py-1.5 text-sm transition duration-300 hover:bg-mono100", children: "\uCDE8\uC18C" })] })] }));
|
|
178
|
-
} }), _jsx(EditorBase.Toolbar.LDButton, { disabled: !state.isLink, onClick: () => editor.chain().focus().unsetLink().run(), className: iconBtn, "aria-label": "\uB9C1\uD06C \uD574\uC81C", children: _jsx(Unlink, { className: "size-5" }) })] })), _jsx(LDDivider, {}),
|
|
213
|
+
} }), _jsx(EditorBase.Toolbar.LDButton, { disabled: !state.isLink, onClick: () => editor.chain().focus().unsetLink().run(), className: iconBtn, "aria-label": "\uB9C1\uD06C \uD574\uC81C", children: _jsx(Unlink, { className: "size-5" }) })] })), _jsx(LDDivider, {}), _jsx(EditorBase.Toolbar.Dropdown, { className: "min-w-40 rounded-lg border border-neutral200 bg-white shadow-lg", trigger: ({ toggle }) => (_jsx(EditorBase.Toolbar.LDButton, { "aria-label": "\uD45C", onClick: toggle, className: cn(iconBtn, state.isTable && activeBtn), children: _jsx(TableIcon, { className: "size-5" }) })), children: ({ close }) => state.isTable ? (_jsx("div", { children: TABLE_ACTIONS.map(({ label, run }) => (_jsx("button", { type: "button", onClick: (e) => {
|
|
214
|
+
e.preventDefault();
|
|
215
|
+
run(editor);
|
|
216
|
+
close();
|
|
217
|
+
}, className: "w-full cursor-pointer px-3 py-2 text-left text-sm first:rounded-t-lg last:rounded-b-lg hover:bg-mono100", children: label }, label))) })) : (_jsx(TableGridPicker, { onSelect: (rows, cols) => {
|
|
218
|
+
editor
|
|
219
|
+
.chain()
|
|
220
|
+
.focus()
|
|
221
|
+
.insertTable({ rows, cols, withHeaderRow: true })
|
|
222
|
+
.run();
|
|
223
|
+
close();
|
|
224
|
+
} })) }), _jsxs("label", { className: cn(iconBtn, 'relative'), children: [_jsx(ImageIcon, { className: "size-5" }), _jsx("input", { type: "file", accept: "image/*", "aria-label": "\uC774\uBBF8\uC9C0 \uC5C5\uB85C\uB4DC", className: "absolute inset-0 size-full cursor-pointer opacity-0", onChange: (e) => {
|
|
179
225
|
handleImageUploadAction(e.target.files);
|
|
180
226
|
e.target.value = '';
|
|
181
227
|
} })] })] }), isMobile && (_jsxs(BubbleMenu, { editor: editor, className: "flex items-center gap-1 rounded-lg border border-neutral200 bg-white p-1 shadow-lg", children: [_jsx(EditorBase.Toolbar.LDButton, { onClick: () => editor.chain().focus().toggleBold().run(), className: cn(iconBtn, state.isBold && activeBtn), "aria-label": "\uAD75\uAC8C", children: _jsx(Bold, { className: "size-5" }) }), _jsx(EditorBase.Toolbar.LDButton, { onClick: () => editor.chain().focus().toggleItalic().run(), className: cn(iconBtn, state.isItalic && activeBtn), "aria-label": "\uAE30\uC6B8\uC784", children: _jsx(Italic, { className: "size-5" }) }), _jsx(EditorBase.Toolbar.LDButton, { onClick: () => editor.chain().focus().toggleUnderline().run(), className: cn(iconBtn, state.isUnderline && activeBtn), "aria-label": "\uBC11\uC904", children: _jsx(Underline, { className: "size-5" }) }), _jsx(EditorBase.Toolbar.LDButton, { onClick: () => editor.chain().focus().toggleStrike().run(), className: cn(iconBtn, state.isStrike && activeBtn), "aria-label": "\uCDE8\uC18C\uC120", children: _jsx(Strikethrough, { className: "size-5" }) }), _jsx(EditorBase.Toolbar.Dropdown, { className: "min-w-35 rounded-lg border border-neutral200 bg-white shadow-lg", trigger: ({ toggle }) => (_jsxs(EditorBase.Toolbar.LDButton, { onClick: toggle, className: cn(iconBtn, 'flex items-center gap-1 px-2'), "aria-label": "\uAE00\uC790 \uC0C9", children: [_jsx("span", { className: "size-4 rounded-full", style: { backgroundColor: state.color } }), _jsx(ChevronDown, { className: "size-4" })] })), children: ({ close }) => COLORS.map((color) => (_jsxs("button", { type: "button", onClick: (e) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LDViewer.d.ts","sourceRoot":"","sources":["../../../src/components/LDEditor/LDViewer.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,QAAQ,GAAI,wBAAwB,aAAa,
|
|
1
|
+
{"version":3,"file":"LDViewer.d.ts","sourceRoot":"","sources":["../../../src/components/LDEditor/LDViewer.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,QAAQ,GAAI,wBAAwB,aAAa,4CAWtD,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -4,6 +4,9 @@ import EditorBase from '../../components/LDEditor/EditorBase.js';
|
|
|
4
4
|
import { createEditorExtensions } from '../../components/LDEditor/extensions.js';
|
|
5
5
|
import cn from '../../utils/cn.js';
|
|
6
6
|
const LDViewer = ({ content, className }) => {
|
|
7
|
-
return (_jsx(EditorBase.LDViewer, { extensions: createEditorExtensions({
|
|
7
|
+
return (_jsx(EditorBase.LDViewer, { extensions: createEditorExtensions({
|
|
8
|
+
resizableImage: false,
|
|
9
|
+
resizableTable: false,
|
|
10
|
+
}), content: content, className: cn('prose prose-lg w-full max-w-none', className) }));
|
|
8
11
|
};
|
|
9
12
|
export default LDViewer;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Extensions } from '@tiptap/react';
|
|
2
2
|
export interface EditorExtensionOptions {
|
|
3
3
|
resizableImage?: boolean;
|
|
4
|
+
resizableTable?: boolean;
|
|
4
5
|
}
|
|
5
|
-
export declare const createEditorExtensions: ({ resizableImage, }?: EditorExtensionOptions) => Extensions;
|
|
6
|
+
export declare const createEditorExtensions: ({ resizableImage, resizableTable, }?: EditorExtensionOptions) => Extensions;
|
|
6
7
|
//# sourceMappingURL=extensions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../../../src/components/LDEditor/extensions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../../../src/components/LDEditor/extensions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,MAAM,WAAW,sBAAsB;IACrC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,sBAAsB,GAAI,sCAGpC,sBAA2B,KAAG,UAwChC,CAAC"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import Image from '@tiptap/extension-image';
|
|
2
|
+
import { TableKit } from '@tiptap/extension-table';
|
|
2
3
|
import TextAlign from '@tiptap/extension-text-align';
|
|
3
4
|
import { Color, FontSize, TextStyle } from '@tiptap/extension-text-style';
|
|
4
5
|
import StarterKit from '@tiptap/starter-kit';
|
|
5
|
-
export const createEditorExtensions = ({ resizableImage = true, } = {}) => [
|
|
6
|
+
export const createEditorExtensions = ({ resizableImage = true, resizableTable = true, } = {}) => [
|
|
6
7
|
// link·underline은 StarterKit에 포함돼 있다. 따로 등록하면 중복 경고가 난다.
|
|
7
8
|
StarterKit.configure({
|
|
8
9
|
heading: {
|
|
@@ -33,4 +34,13 @@ export const createEditorExtensions = ({ resizableImage = true, } = {}) => [
|
|
|
33
34
|
}
|
|
34
35
|
: false,
|
|
35
36
|
}),
|
|
37
|
+
TableKit.configure({
|
|
38
|
+
table: {
|
|
39
|
+
resizable: resizableTable,
|
|
40
|
+
// 리사이즈로 표가 컨테이너보다 넓어질 수 있다. 래퍼가 있어야 가로 스크롤을
|
|
41
|
+
// 표에만 줄 수 있다. 편집 모드에서는 TableView가 항상 래퍼를 만들지만
|
|
42
|
+
// 읽기 전용(LDViewer)에서는 이 옵션이 있어야 생긴다.
|
|
43
|
+
renderWrapper: true,
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
36
46
|
];
|
package/dist/index.css
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"Courier New", monospace;
|
|
10
10
|
--color-red-500: oklch(63.7% 0.237 25.331);
|
|
11
11
|
--color-yellow-400: oklch(85.2% 0.199 91.936);
|
|
12
|
+
--color-blue-100: oklch(93.2% 0.032 255.585);
|
|
12
13
|
--color-blue-400: oklch(70.7% 0.165 254.624);
|
|
13
14
|
--color-blue-500: oklch(62.3% 0.214 259.815);
|
|
14
15
|
--color-blue-600: oklch(54.6% 0.245 262.881);
|
|
@@ -2018,6 +2019,9 @@
|
|
|
2018
2019
|
.grid-cols-3 {
|
|
2019
2020
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
2020
2021
|
}
|
|
2022
|
+
.grid-cols-5 {
|
|
2023
|
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
2024
|
+
}
|
|
2021
2025
|
.grid-cols-7 {
|
|
2022
2026
|
grid-template-columns: repeat(7, minmax(0, 1fr));
|
|
2023
2027
|
}
|
|
@@ -2361,6 +2365,9 @@
|
|
|
2361
2365
|
--tw-border-style: solid;
|
|
2362
2366
|
border-style: solid;
|
|
2363
2367
|
}
|
|
2368
|
+
.border-blue-500 {
|
|
2369
|
+
border-color: var(--color-blue-500);
|
|
2370
|
+
}
|
|
2364
2371
|
.border-blue500 {
|
|
2365
2372
|
border-color: var(--color-blue500);
|
|
2366
2373
|
}
|
|
@@ -2439,6 +2446,9 @@
|
|
|
2439
2446
|
background-color: color-mix(in oklab, var(--color-black) 40%, transparent);
|
|
2440
2447
|
}
|
|
2441
2448
|
}
|
|
2449
|
+
.bg-blue-100 {
|
|
2450
|
+
background-color: var(--color-blue-100);
|
|
2451
|
+
}
|
|
2442
2452
|
.bg-blue-500 {
|
|
2443
2453
|
background-color: var(--color-blue-500);
|
|
2444
2454
|
}
|
|
@@ -5611,6 +5621,51 @@
|
|
|
5611
5621
|
.ProseMirror [data-resize-wrapper] img {
|
|
5612
5622
|
margin: 0 !important;
|
|
5613
5623
|
}
|
|
5624
|
+
.ProseMirror .tableWrapper {
|
|
5625
|
+
overflow-x: auto;
|
|
5626
|
+
margin: 1rem 0;
|
|
5627
|
+
}
|
|
5628
|
+
.ProseMirror table {
|
|
5629
|
+
width: 100%;
|
|
5630
|
+
margin: 0;
|
|
5631
|
+
table-layout: fixed;
|
|
5632
|
+
border-collapse: collapse;
|
|
5633
|
+
}
|
|
5634
|
+
.ProseMirror table td, .ProseMirror table th {
|
|
5635
|
+
position: relative;
|
|
5636
|
+
min-width: 1em;
|
|
5637
|
+
padding: 0.5rem 0.75rem;
|
|
5638
|
+
border: 1px solid var(--color-neutral200);
|
|
5639
|
+
vertical-align: top;
|
|
5640
|
+
}
|
|
5641
|
+
.ProseMirror table th {
|
|
5642
|
+
background: var(--color-mono100);
|
|
5643
|
+
font-weight: 600;
|
|
5644
|
+
text-align: left;
|
|
5645
|
+
}
|
|
5646
|
+
.ProseMirror table p {
|
|
5647
|
+
margin: 0;
|
|
5648
|
+
}
|
|
5649
|
+
.ProseMirror .selectedCell::after {
|
|
5650
|
+
content: '';
|
|
5651
|
+
position: absolute;
|
|
5652
|
+
inset: 0;
|
|
5653
|
+
background: rgba(59, 130, 246, 0.15);
|
|
5654
|
+
pointer-events: none;
|
|
5655
|
+
z-index: 2;
|
|
5656
|
+
}
|
|
5657
|
+
.ProseMirror .column-resize-handle {
|
|
5658
|
+
position: absolute;
|
|
5659
|
+
top: 0;
|
|
5660
|
+
right: -2px;
|
|
5661
|
+
bottom: -2px;
|
|
5662
|
+
width: 4px;
|
|
5663
|
+
background: #3b82f6;
|
|
5664
|
+
pointer-events: none;
|
|
5665
|
+
}
|
|
5666
|
+
.ProseMirror.resize-cursor {
|
|
5667
|
+
cursor: col-resize;
|
|
5668
|
+
}
|
|
5614
5669
|
@layer components {
|
|
5615
5670
|
.select-2xs {
|
|
5616
5671
|
height: var(--control-height-2xs);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loword/loword-design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "로워드 디자인 시스템 컴포넌트 패키지(Tailwind v4)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"@tiptap/extension-highlight": "^3.26.0",
|
|
64
64
|
"@tiptap/extension-image": "^3.26.0",
|
|
65
65
|
"@tiptap/extension-link": "^3.26.0",
|
|
66
|
+
"@tiptap/extension-table": "^3.26.0",
|
|
66
67
|
"@tiptap/extension-text-align": "^3.26.0",
|
|
67
68
|
"@tiptap/extension-text-style": "^3.26.0",
|
|
68
69
|
"@tiptap/extension-underline": "^3.26.0",
|
|
@@ -131,6 +132,9 @@
|
|
|
131
132
|
"@tiptap/extension-link": {
|
|
132
133
|
"optional": true
|
|
133
134
|
},
|
|
135
|
+
"@tiptap/extension-table": {
|
|
136
|
+
"optional": true
|
|
137
|
+
},
|
|
134
138
|
"@tiptap/extension-text-align": {
|
|
135
139
|
"optional": true
|
|
136
140
|
},
|
|
@@ -209,6 +213,7 @@
|
|
|
209
213
|
"@tiptap/extension-highlight": "^3.26.0",
|
|
210
214
|
"@tiptap/extension-image": "^3.26.0",
|
|
211
215
|
"@tiptap/extension-link": "^3.26.0",
|
|
216
|
+
"@tiptap/extension-table": "^3.26.0",
|
|
212
217
|
"@tiptap/extension-text-align": "^3.26.0",
|
|
213
218
|
"@tiptap/extension-text-style": "^3.26.0",
|
|
214
219
|
"@tiptap/extension-underline": "^3.26.0",
|
|
@@ -165,3 +165,65 @@
|
|
|
165
165
|
.ProseMirror [data-resize-wrapper] img {
|
|
166
166
|
margin: 0 !important;
|
|
167
167
|
}
|
|
168
|
+
|
|
169
|
+
/* 표
|
|
170
|
+
prose 기본 표는 가로줄만 그어 에디터에서 셀 경계가 보이지 않는다. 격자 테두리로 덮는다.
|
|
171
|
+
@layer 밖(=무레이어)이라 utilities 레이어의 prose보다 우선한다. */
|
|
172
|
+
.ProseMirror .tableWrapper {
|
|
173
|
+
overflow-x: auto;
|
|
174
|
+
margin: 1rem 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.ProseMirror table {
|
|
178
|
+
width: 100%;
|
|
179
|
+
margin: 0;
|
|
180
|
+
/* 열 너비 리사이즈(colgroup width)가 반영되려면 fixed여야 한다. */
|
|
181
|
+
table-layout: fixed;
|
|
182
|
+
border-collapse: collapse;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.ProseMirror table td,
|
|
186
|
+
.ProseMirror table th {
|
|
187
|
+
/* 열 리사이즈 핸들의 위치 기준 */
|
|
188
|
+
position: relative;
|
|
189
|
+
min-width: 1em;
|
|
190
|
+
padding: 0.5rem 0.75rem;
|
|
191
|
+
border: 1px solid var(--color-neutral200);
|
|
192
|
+
vertical-align: top;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.ProseMirror table th {
|
|
196
|
+
background: var(--color-mono100);
|
|
197
|
+
font-weight: 600;
|
|
198
|
+
text-align: left;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* 셀 안 문단은 prose의 블록 여백을 쓰지 않는다 — 셀 높이가 과하게 늘어난다. */
|
|
202
|
+
.ProseMirror table p {
|
|
203
|
+
margin: 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* 드래그로 여러 셀을 선택했을 때 */
|
|
207
|
+
.ProseMirror .selectedCell::after {
|
|
208
|
+
content: '';
|
|
209
|
+
position: absolute;
|
|
210
|
+
inset: 0;
|
|
211
|
+
background: rgba(59, 130, 246, 0.15);
|
|
212
|
+
pointer-events: none;
|
|
213
|
+
z-index: 2;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* 열 너비 리사이즈 핸들 (셀 오른쪽 경계) */
|
|
217
|
+
.ProseMirror .column-resize-handle {
|
|
218
|
+
position: absolute;
|
|
219
|
+
top: 0;
|
|
220
|
+
right: -2px;
|
|
221
|
+
bottom: -2px;
|
|
222
|
+
width: 4px;
|
|
223
|
+
background: #3b82f6;
|
|
224
|
+
pointer-events: none;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.ProseMirror.resize-cursor {
|
|
228
|
+
cursor: col-resize;
|
|
229
|
+
}
|