@seorii/tiptap 0.4.0 → 0.4.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.
@@ -24,6 +24,8 @@ declare const _default: {
24
24
  codeBlock: string;
25
25
  mathBlock: string;
26
26
  table: string;
27
+ twoColumns: string;
28
+ threeColumns: string;
27
29
  image: string;
28
30
  iframe: string;
29
31
  youtube: string;
@@ -36,6 +38,8 @@ declare const _default: {
36
38
  codeBlockInfo: string;
37
39
  mathBlockInfo: string;
38
40
  tableInfo: string;
41
+ twoColumnsInfo: string;
42
+ threeColumnsInfo: string;
39
43
  imageInfo: string;
40
44
  iframeInfo: string;
41
45
  youtubeInfo: string;
@@ -24,6 +24,8 @@ export default {
24
24
  codeBlock: 'Code block',
25
25
  mathBlock: 'Math block',
26
26
  table: 'Table',
27
+ twoColumns: '2 Columns',
28
+ threeColumns: '3 Columns',
27
29
  image: 'Image',
28
30
  iframe: 'iframe',
29
31
  youtube: 'Youtube',
@@ -36,6 +38,8 @@ export default {
36
38
  codeBlockInfo: 'Code block with syntax highlighting',
37
39
  mathBlockInfo: 'Math block',
38
40
  tableInfo: 'Table',
41
+ twoColumnsInfo: 'Two-column layout',
42
+ threeColumnsInfo: 'Three-column layout',
39
43
  imageInfo: 'Image',
40
44
  iframeInfo: 'Embed another website',
41
45
  youtubeInfo: 'Embed Youtube video',
@@ -24,6 +24,8 @@ declare const locales: readonly [{
24
24
  codeBlock: string;
25
25
  mathBlock: string;
26
26
  table: string;
27
+ twoColumns: string;
28
+ threeColumns: string;
27
29
  image: string;
28
30
  iframe: string;
29
31
  youtube: string;
@@ -36,6 +38,8 @@ declare const locales: readonly [{
36
38
  codeBlockInfo: string;
37
39
  mathBlockInfo: string;
38
40
  tableInfo: string;
41
+ twoColumnsInfo: string;
42
+ threeColumnsInfo: string;
39
43
  imageInfo: string;
40
44
  iframeInfo: string;
41
45
  youtubeInfo: string;
@@ -69,6 +73,8 @@ declare const locales: readonly [{
69
73
  codeBlock: string;
70
74
  mathBlock: string;
71
75
  table: string;
76
+ twoColumns: string;
77
+ threeColumns: string;
72
78
  image: string;
73
79
  iframe: string;
74
80
  youtube: string;
@@ -81,6 +87,8 @@ declare const locales: readonly [{
81
87
  codeBlockInfo: string;
82
88
  mathBlockInfo: string;
83
89
  tableInfo: string;
90
+ twoColumnsInfo: string;
91
+ threeColumnsInfo: string;
84
92
  imageInfo: string;
85
93
  iframeInfo: string;
86
94
  youtubeInfo: string;
@@ -24,6 +24,8 @@ declare const _default: {
24
24
  codeBlock: string;
25
25
  mathBlock: string;
26
26
  table: string;
27
+ twoColumns: string;
28
+ threeColumns: string;
27
29
  image: string;
28
30
  iframe: string;
29
31
  youtube: string;
@@ -36,6 +38,8 @@ declare const _default: {
36
38
  codeBlockInfo: string;
37
39
  mathBlockInfo: string;
38
40
  tableInfo: string;
41
+ twoColumnsInfo: string;
42
+ threeColumnsInfo: string;
39
43
  imageInfo: string;
40
44
  iframeInfo: string;
41
45
  youtubeInfo: string;
@@ -24,6 +24,8 @@ export default {
24
24
  codeBlock: '코드 블록',
25
25
  mathBlock: '수식 블록',
26
26
  table: '테이블',
27
+ twoColumns: '2열',
28
+ threeColumns: '3열',
27
29
  image: '이미지',
28
30
  iframe: 'iframe',
29
31
  youtube: '유튜브',
@@ -36,6 +38,8 @@ export default {
36
38
  codeBlockInfo: '하이라이팅되는 코드 블록',
37
39
  mathBlockInfo: '가운데로 정렬되는 수식 블록',
38
40
  tableInfo: '표 삽입',
41
+ twoColumnsInfo: '2열 레이아웃',
42
+ threeColumnsInfo: '3열 레이아웃',
39
43
  imageInfo: '이미지',
40
44
  iframeInfo: '다른 웹사이트 삽입',
41
45
  youtubeInfo: 'Youtube 동영상 삽입',
@@ -0,0 +1,14 @@
1
+ import { Node } from '@tiptap/core';
2
+ import './style.css';
3
+ type ColumnCount = 2 | 3;
4
+ declare module '@tiptap/core' {
5
+ interface Commands<ReturnType> {
6
+ columnLayout: {
7
+ setColumns: (count: ColumnCount) => ReturnType;
8
+ setTwoColumns: () => ReturnType;
9
+ setThreeColumns: () => ReturnType;
10
+ };
11
+ }
12
+ }
13
+ declare const _default: Node<any, any>[];
14
+ export default _default;
@@ -0,0 +1,88 @@
1
+ import { Node, mergeAttributes } from '@tiptap/core';
2
+ import './style.css';
3
+ const normalizeColumnCount = (value) => (Number(value) === 3 ? 3 : 2);
4
+ const Column = Node.create({
5
+ name: 'column',
6
+ content: 'block+',
7
+ isolating: true,
8
+ defining: true,
9
+ addOptions() {
10
+ return {
11
+ HTMLAttributes: {}
12
+ };
13
+ },
14
+ parseHTML() {
15
+ return [{ tag: 'div.tiptap-column' }];
16
+ },
17
+ renderHTML({ HTMLAttributes }) {
18
+ return [
19
+ 'div',
20
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
21
+ class: 'tiptap-column'
22
+ }),
23
+ 0
24
+ ];
25
+ }
26
+ });
27
+ const Columns = Node.create({
28
+ name: 'columns',
29
+ group: 'block',
30
+ content: 'column{2,3}',
31
+ isolating: true,
32
+ defining: true,
33
+ draggable: true,
34
+ addOptions() {
35
+ return {
36
+ HTMLAttributes: {}
37
+ };
38
+ },
39
+ addAttributes() {
40
+ return {
41
+ count: {
42
+ default: 2,
43
+ parseHTML: (element) => {
44
+ if (!(element instanceof HTMLElement))
45
+ return 2;
46
+ if (element.classList.contains('columns-3'))
47
+ return 3;
48
+ if (element.classList.contains('columns-2'))
49
+ return 2;
50
+ const columnCount = Array.from(element.children).filter((child) => child instanceof HTMLElement && child.classList.contains('tiptap-column')).length;
51
+ return columnCount >= 3 ? 3 : 2;
52
+ },
53
+ renderHTML: () => ({})
54
+ }
55
+ };
56
+ },
57
+ parseHTML() {
58
+ return [{ tag: 'div.tiptap-columns' }];
59
+ },
60
+ renderHTML({ node, HTMLAttributes }) {
61
+ const count = normalizeColumnCount(node.attrs.count);
62
+ return [
63
+ 'div',
64
+ mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
65
+ class: `tiptap-columns columns-${count}`
66
+ }),
67
+ 0
68
+ ];
69
+ },
70
+ addCommands() {
71
+ return {
72
+ setColumns: (count) => ({ commands }) => {
73
+ const normalizedCount = normalizeColumnCount(count);
74
+ return commands.insertContent({
75
+ type: this.name,
76
+ attrs: { count: normalizedCount },
77
+ content: Array.from({ length: normalizedCount }, () => ({
78
+ type: 'column',
79
+ content: [{ type: 'paragraph' }]
80
+ }))
81
+ });
82
+ },
83
+ setTwoColumns: () => ({ commands }) => commands.setColumns(2),
84
+ setThreeColumns: () => ({ commands }) => commands.setColumns(3)
85
+ };
86
+ }
87
+ });
88
+ export default [Columns, Column];
@@ -0,0 +1,37 @@
1
+ .tiptap-columns {
2
+ display: grid;
3
+ gap: 12px;
4
+ margin: 12px 0;
5
+ }
6
+
7
+ .tiptap-columns.columns-2 {
8
+ grid-template-columns: repeat(2, minmax(0, 1fr));
9
+ }
10
+
11
+ .tiptap-columns.columns-3 {
12
+ grid-template-columns: repeat(3, minmax(0, 1fr));
13
+ }
14
+
15
+ .tiptap-column {
16
+ min-width: 0;
17
+ padding: 10px 12px;
18
+ border: 0;
19
+ border-radius: 10px;
20
+ background: transparent;
21
+ }
22
+
23
+ .tiptap-column > :first-child {
24
+ margin-top: 0;
25
+ }
26
+
27
+ .tiptap-column > :last-child {
28
+ margin-bottom: 0;
29
+ }
30
+
31
+ .editable .tiptap-column {
32
+ border: 1px dashed var(--primary-light3, rgba(112, 112, 112, 0.35));
33
+ }
34
+
35
+ .editable .tiptap-column:hover {
36
+ border-color: var(--primary-light7, rgba(112, 112, 112, 0.65));
37
+ }
@@ -248,6 +248,24 @@ export const suggest = {
248
248
  .run();
249
249
  }
250
250
  },
251
+ {
252
+ icon: 'view_column_2',
253
+ title: i18n('twoColumns'),
254
+ subtitle: i18n('twoColumnsInfo'),
255
+ keywords: createKeywords(['twoColumns', 'twoColumnsInfo'], ['2 columns', 'two columns', 'notion']),
256
+ command: ({ editor, range }) => {
257
+ editor.chain().focus().deleteRange(fixRange(editor, range)).setTwoColumns().run();
258
+ }
259
+ },
260
+ {
261
+ icon: 'view_week',
262
+ title: i18n('threeColumns'),
263
+ subtitle: i18n('threeColumnsInfo'),
264
+ keywords: createKeywords(['threeColumns', 'threeColumnsInfo'], ['3 columns', 'three columns', 'notion']),
265
+ command: ({ editor, range }) => {
266
+ editor.chain().focus().deleteRange(fixRange(editor, range)).setThreeColumns().run();
267
+ }
268
+ },
251
269
  {
252
270
  icon: 'format_quote',
253
271
  title: i18n('blockquote'),
@@ -158,7 +158,8 @@ function buildResizeAttrs(kind, node, height, imageRatio) {
158
158
  const roundedHeight = String(Math.round(height));
159
159
  if (kind === 'image') {
160
160
  const roundedWidth = String(Math.max(1, Math.round(height * imageRatio)));
161
- return { ...attrs, width: roundedWidth, height: roundedHeight };
161
+ // Keep image responsive by storing width only; fixed height causes ratio distortion on narrow layouts.
162
+ return { ...attrs, width: roundedWidth, height: null };
162
163
  }
163
164
  if (kind === 'iframe' || kind === 'embed') {
164
165
  return { ...attrs, width: attrs.width || '100%', height: roundedHeight };
@@ -410,6 +410,7 @@
410
410
  & :global(img) {
411
411
  transition: all 0.2s ease-in-out;
412
412
  max-width: 100%;
413
+ object-fit: contain;
413
414
  border-radius: 12px;
414
415
  position: relative;
415
416
  }
@@ -26,6 +26,7 @@ import UploadSkeleton from '../plugin/upload/skeleton';
26
26
  import { MathInline, MathBlock } from '@seorii/prosemirror-math/tiptap';
27
27
  import Youtube from '../plugin/youtube';
28
28
  import Placeholder from '@tiptap/extension-placeholder';
29
+ import columns from '../plugin/columns';
29
30
  import command from '../plugin/command/suggest';
30
31
  import emoji from '../plugin/command/emoji';
31
32
  import { countSlashItems, moveSlashSelection, runSlashItemAt, slashState } from '../plugin/command/stores.svelte';
@@ -239,6 +240,7 @@ const extensions = (placeholder, plugins, crossorigin, codeBlockLanguageLabels)
239
240
  orderedlist,
240
241
  MathInline,
241
242
  MathBlock,
243
+ ...columns,
242
244
  table,
243
245
  tableHeader,
244
246
  tableRow,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seorii/tiptap",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "svelte-kit sync && svelte-package",