|
1 |
| -import { |
2 |
| - BlockFromConfig, |
3 |
| - BlockNoteEditor, |
4 |
| - createBlockSpec, |
5 |
| - Props, |
6 |
| - PropSchema, |
7 |
| -} from '@blocknote/core'; |
8 |
| -import { |
9 |
| - lucideAlertTriangle, |
10 |
| - lucideCheckCircle, |
11 |
| - lucideCircleOff, |
12 |
| - lucideInfo, |
13 |
| -} from '@ng-icons/lucide'; |
| 1 | +import { createBlockSpec, defaultProps } from '@blocknote/core'; |
14 | 2 |
|
15 |
| -// The types of alerts that users can choose from. |
16 |
| -export const alertTypes = [ |
17 |
| - { |
18 |
| - title: 'Warning', |
19 |
| - value: 'warning', |
20 |
| - icon: lucideAlertTriangle, |
| 3 | +// The types of alerts that users can choose from |
| 4 | +const alertTypes = { |
| 5 | + warning: { |
| 6 | + icon: '⚠️', |
21 | 7 | color: '#e69819',
|
22 |
| - backgroundColor: { |
23 |
| - light: '#fff6e6', |
24 |
| - dark: '#805d20', |
25 |
| - }, |
| 8 | + backgroundColor: '#fff6e6', |
26 | 9 | },
|
27 |
| - { |
28 |
| - title: 'Error', |
29 |
| - value: 'error', |
30 |
| - icon: lucideCircleOff, |
| 10 | + error: { |
| 11 | + icon: '⛔', |
31 | 12 | color: '#d80d0d',
|
32 |
| - backgroundColor: { |
33 |
| - light: '#ffe6e6', |
34 |
| - dark: '#802020', |
35 |
| - }, |
| 13 | + backgroundColor: '#ffe6e6', |
36 | 14 | },
|
37 |
| - { |
38 |
| - title: 'Info', |
39 |
| - value: 'info', |
40 |
| - icon: lucideInfo, |
| 15 | + info: { |
| 16 | + icon: 'ℹ️', |
41 | 17 | color: '#507aff',
|
42 |
| - backgroundColor: { |
43 |
| - light: '#e6ebff', |
44 |
| - dark: '#203380', |
45 |
| - }, |
| 18 | + backgroundColor: '#e6ebff', |
46 | 19 | },
|
47 |
| - { |
48 |
| - title: 'Success', |
49 |
| - value: 'success', |
50 |
| - icon: lucideCheckCircle, |
| 20 | + success: { |
| 21 | + icon: '✅', |
51 | 22 | color: '#0bc10b',
|
52 |
| - backgroundColor: { |
53 |
| - light: '#e6ffe6', |
54 |
| - dark: '#208020', |
55 |
| - }, |
| 23 | + backgroundColor: '#e6ffe6', |
56 | 24 | },
|
57 |
| -] as const; |
| 25 | +}; |
58 | 26 |
|
59 |
| -export const alertPropSchema = { |
60 |
| - type: { |
61 |
| - default: 'warning', |
62 |
| - values: alertTypes.map((type) => type.value), |
| 27 | +export const alertBlock = createBlockSpec( |
| 28 | + { |
| 29 | + type: 'alert', |
| 30 | + propSchema: { |
| 31 | + textAlignment: defaultProps.textAlignment, |
| 32 | + textColor: defaultProps.textColor, |
| 33 | + type: { |
| 34 | + default: 'warning', |
| 35 | + values: ['warning', 'error', 'info', 'success'] as const, |
| 36 | + }, |
| 37 | + }, |
| 38 | + content: 'inline', |
63 | 39 | },
|
64 |
| -} satisfies PropSchema; |
65 |
| - |
66 |
| -export const alertBlockConfig = { |
67 |
| - type: 'alert' as const, |
68 |
| - propSchema: alertPropSchema, |
69 |
| - content: 'inline', |
70 |
| -} as const; |
71 |
| - |
72 |
| -const alertRender = ( |
73 |
| - block: BlockFromConfig<typeof alertBlockConfig, any, any>, |
74 |
| - editor: BlockNoteEditor<any, any, any> |
75 |
| -) => { |
76 |
| - const div = document.createElement('div'); |
77 |
| - div.style.width = '100%'; |
78 |
| - div.style.padding = '4px 8px'; |
79 |
| - div.style.display = 'flex'; |
80 |
| - div.style.alignItems = 'center'; |
81 |
| - const alertType = alertTypes.find((type) => type.value === block.props.type); |
82 |
| - if (!alertType) { |
83 |
| - return { |
84 |
| - dom: div, |
85 |
| - }; |
| 40 | + { |
| 41 | + render: (block, editor) => { |
| 42 | + const alert = document.createElement('div'); |
| 43 | + alert.className = 'alert'; |
| 44 | + alert.style.width = '100%'; |
| 45 | + alert.style.display = 'flex'; |
| 46 | + alert.style.backgroundColor = |
| 47 | + alertTypes[block.props.type].backgroundColor; |
| 48 | + |
| 49 | + const dropdown = document.createElement('select'); |
| 50 | + dropdown.contentEditable = 'false'; |
| 51 | + dropdown.addEventListener('change', () => { |
| 52 | + editor.updateBlock(block, { |
| 53 | + type: 'alert', |
| 54 | + props: { type: dropdown.value as keyof typeof alertTypes }, |
| 55 | + }); |
| 56 | + }); |
| 57 | + dropdown.options.add( |
| 58 | + new Option( |
| 59 | + alertTypes['warning'].icon, |
| 60 | + 'warning', |
| 61 | + block.props.type === 'warning', |
| 62 | + block.props.type === 'warning' |
| 63 | + ) |
| 64 | + ); |
| 65 | + dropdown.options.add( |
| 66 | + new Option( |
| 67 | + alertTypes['error'].icon, |
| 68 | + 'error', |
| 69 | + block.props.type === 'error', |
| 70 | + block.props.type === 'error' |
| 71 | + ) |
| 72 | + ); |
| 73 | + dropdown.options.add( |
| 74 | + new Option( |
| 75 | + alertTypes['info'].icon, |
| 76 | + 'info', |
| 77 | + block.props.type === 'info', |
| 78 | + block.props.type === 'info' |
| 79 | + ) |
| 80 | + ); |
| 81 | + dropdown.options.add( |
| 82 | + new Option( |
| 83 | + alertTypes['success'].icon, |
| 84 | + 'success', |
| 85 | + block.props.type === 'success', |
| 86 | + block.props.type === 'success' |
| 87 | + ) |
| 88 | + ); |
| 89 | + alert.appendChild(dropdown); |
| 90 | + |
| 91 | + const inlineContent = document.createElement('div'); |
| 92 | + inlineContent.style.flexGrow = '1'; |
| 93 | + |
| 94 | + alert.appendChild(inlineContent); |
| 95 | + |
| 96 | + return { |
| 97 | + dom: alert, |
| 98 | + contentDOM: inlineContent, |
| 99 | + }; |
| 100 | + }, |
86 | 101 | }
|
87 |
| - div.style.color = alertType.color; |
88 |
| - div.style.backgroundColor = alertType.backgroundColor.light; |
89 |
| - |
90 |
| - // Create a select element. |
91 |
| - const selectElement = document.createElement('select'); |
92 |
| - |
93 |
| - // Populate the select element with options. |
94 |
| - alertTypes.forEach((alertType) => { |
95 |
| - const option = document.createElement('option'); |
96 |
| - |
97 |
| - option.value = alertType.value; |
98 |
| - option.text = alertType.title; |
99 |
| - |
100 |
| - // Mark the current alertType as selected in the dropdown. |
101 |
| - if (alertType.value === block.props.type) { |
102 |
| - option.selected = true; |
103 |
| - } |
104 |
| - |
105 |
| - selectElement.appendChild(option); |
106 |
| - }); |
107 |
| - |
108 |
| - // Handle the change event. |
109 |
| - selectElement.addEventListener('change', (event: Event) => { |
110 |
| - const target = event.target as HTMLSelectElement; |
111 |
| - editor.updateBlock(block, { |
112 |
| - type: 'alert', |
113 |
| - props: { type: target.value }, |
114 |
| - }); |
115 |
| - }); |
116 |
| - |
117 |
| - // Style the dropdown |
118 |
| - selectElement.style.position = 'absolute'; |
119 |
| - selectElement.style.right = '100px'; |
120 |
| - |
121 |
| - // Add the select element to the div. |
122 |
| - div.appendChild(selectElement); |
123 |
| - |
124 |
| - const iconElement = document.createElement('span'); |
125 |
| - iconElement.innerHTML = alertType.icon; |
126 |
| - iconElement.style.marginRight = '8px'; |
127 |
| - div.appendChild(iconElement); |
128 |
| - |
129 |
| - const textElement = document.createElement('span'); |
130 |
| - textElement.classList.add('inline-content'); |
131 |
| - textElement.textContent = 'Hello world'; |
132 |
| - div.appendChild(textElement); |
133 |
| - |
134 |
| - return { |
135 |
| - dom: div, |
136 |
| - }; |
137 |
| -}; |
138 |
| - |
139 |
| -export const alertParse = ( |
140 |
| - element: HTMLElement |
141 |
| -): Partial<Props<typeof alertBlockConfig.propSchema>> | undefined => { |
142 |
| - return undefined; |
143 |
| -}; |
144 |
| - |
145 |
| -export const alertBlock = createBlockSpec(alertBlockConfig, { |
146 |
| - render: alertRender, |
147 |
| - parse: alertParse, |
148 |
| -}); |
| 102 | +); |
0 commit comments