@jackuait/blok 0.10.0-beta.16 → 0.10.0-beta.18
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/blok.mjs +2 -2
- package/dist/chunks/{blok-CEVrVqlx.mjs → blok-DK7jJnLH.mjs} +1393 -1381
- package/dist/chunks/{constants-BURnHRy_.mjs → constants-B0Vnu9kC.mjs} +48 -48
- package/dist/chunks/{tools-Dt2I14vP.mjs → tools-QLa-0Rbm.mjs} +816 -786
- package/dist/full.mjs +3 -3
- package/dist/react.mjs +2 -2
- package/dist/tools.mjs +2 -2
- package/package.json +1 -1
- package/src/cli/commands/convert-gdocs/index.ts +3 -1
- package/src/cli/commands/convert-html/index.ts +3 -1
- package/src/components/icons/index.ts +16 -0
- package/src/components/modules/blockEvents/composers/keyboardNavigation.ts +18 -0
- package/src/components/modules/rectangleSelection.ts +25 -5
- package/src/components/modules/toolbar/index.ts +48 -7
- package/src/styles/main.css +16 -0
- package/src/tools/callout/constants.ts +2 -1
- package/src/tools/callout/dom-builder.ts +11 -1
- package/src/tools/callout/index.ts +6 -0
- package/src/tools/code/constants.ts +9 -1
- package/src/tools/code/dom-builder.ts +90 -54
- package/src/tools/code/index.ts +33 -19
- package/src/tools/table/table-cell-blocks.ts +3 -1
|
@@ -4,15 +4,14 @@ import {
|
|
|
4
4
|
LANGUAGE_BUTTON_STYLES,
|
|
5
5
|
HEADER_BUTTON_STYLES,
|
|
6
6
|
CODE_AREA_STYLES,
|
|
7
|
-
TAB_STYLES,
|
|
8
|
-
TAB_ACTIVE_STYLES,
|
|
9
|
-
TAB_INACTIVE_STYLES,
|
|
10
7
|
PREVIEW_AREA_STYLES,
|
|
11
8
|
CODE_BODY_STYLES,
|
|
12
9
|
GUTTER_STYLES,
|
|
13
10
|
GUTTER_LINE_STYLES,
|
|
11
|
+
MORE_MENU_STYLES,
|
|
12
|
+
MORE_MENU_ITEM_STYLES,
|
|
14
13
|
} from './constants';
|
|
15
|
-
import { IconCopy, IconWrap, IconLineNumbers } from '../../components/icons';
|
|
14
|
+
import { IconCopy, IconCode, IconChevronDown, IconEllipsis, IconWrap, IconLineNumbers } from '../../components/icons';
|
|
16
15
|
|
|
17
16
|
export interface CodeDOMRefs {
|
|
18
17
|
wrapper: HTMLElement;
|
|
@@ -23,9 +22,10 @@ export interface CodeDOMRefs {
|
|
|
23
22
|
preElement: HTMLPreElement;
|
|
24
23
|
codeElement: HTMLElement;
|
|
25
24
|
gutterElement: HTMLElement;
|
|
26
|
-
|
|
27
|
-
previewTab: HTMLButtonElement | null;
|
|
25
|
+
previewToggleButton: HTMLButtonElement | null;
|
|
28
26
|
previewElement: HTMLDivElement | null;
|
|
27
|
+
moreButton: HTMLButtonElement;
|
|
28
|
+
moreMenu: HTMLElement;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface BuildCodeDOMOptions {
|
|
@@ -36,38 +36,30 @@ export interface BuildCodeDOMOptions {
|
|
|
36
36
|
wrapLabel: string;
|
|
37
37
|
lineNumbersLabel?: string;
|
|
38
38
|
previewable?: boolean;
|
|
39
|
-
|
|
40
|
-
previewTabLabel?: string;
|
|
39
|
+
previewToggleLabel?: string;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
function buildPreviewElements(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const codeTab = document.createElement('button');
|
|
43
|
+
previewToggleLabel?: string,
|
|
44
|
+
): { previewToggleButton: HTMLButtonElement; previewElement: HTMLDivElement } {
|
|
45
|
+
const previewToggleButton = document.createElement('button');
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const previewTab = document.createElement('button');
|
|
55
|
-
|
|
56
|
-
previewTab.type = 'button';
|
|
57
|
-
previewTab.className = `${TAB_STYLES} ${TAB_ACTIVE_STYLES}`;
|
|
58
|
-
previewTab.textContent = previewTabLabel ?? 'Preview';
|
|
59
|
-
previewTab.setAttribute('data-blok-testid', 'code-preview-tab');
|
|
47
|
+
previewToggleButton.type = 'button';
|
|
48
|
+
previewToggleButton.className = HEADER_BUTTON_STYLES;
|
|
49
|
+
previewToggleButton.innerHTML = IconCode;
|
|
50
|
+
previewToggleButton.setAttribute('aria-label', previewToggleLabel ?? 'Preview');
|
|
51
|
+
previewToggleButton.setAttribute('data-blok-testid', 'code-preview-toggle-btn');
|
|
60
52
|
|
|
61
53
|
const previewElement = document.createElement('div');
|
|
62
54
|
|
|
63
55
|
previewElement.className = PREVIEW_AREA_STYLES;
|
|
64
56
|
previewElement.setAttribute('data-blok-testid', 'code-preview');
|
|
65
57
|
|
|
66
|
-
return {
|
|
58
|
+
return { previewToggleButton, previewElement };
|
|
67
59
|
}
|
|
68
60
|
|
|
69
61
|
export function buildCodeDOM(options: BuildCodeDOMOptions): CodeDOMRefs {
|
|
70
|
-
const { code, languageName, readOnly, copyLabel, wrapLabel, lineNumbersLabel, previewable,
|
|
62
|
+
const { code, languageName, readOnly, copyLabel, wrapLabel, lineNumbersLabel, previewable, previewToggleLabel } = options;
|
|
71
63
|
|
|
72
64
|
// Wrapper
|
|
73
65
|
const wrapper = document.createElement('div');
|
|
@@ -77,38 +69,30 @@ export function buildCodeDOM(options: BuildCodeDOMOptions): CodeDOMRefs {
|
|
|
77
69
|
const header = document.createElement('div');
|
|
78
70
|
header.className = HEADER_STYLES;
|
|
79
71
|
|
|
80
|
-
// Language button (opens language picker)
|
|
72
|
+
// Language button (opens language picker) — includes text + chevron icon
|
|
81
73
|
const languageButton = document.createElement('button');
|
|
82
74
|
languageButton.type = 'button';
|
|
83
75
|
languageButton.className = LANGUAGE_BUTTON_STYLES;
|
|
84
|
-
languageButton.textContent = languageName;
|
|
85
76
|
languageButton.setAttribute('aria-haspopup', 'listbox');
|
|
86
77
|
languageButton.setAttribute('data-blok-testid', 'code-language-btn');
|
|
87
78
|
|
|
79
|
+
const langText = document.createElement('span');
|
|
80
|
+
langText.textContent = languageName;
|
|
81
|
+
languageButton.appendChild(langText);
|
|
82
|
+
|
|
83
|
+
const chevronSpan = document.createElement('span');
|
|
84
|
+
chevronSpan.className = 'inline-flex items-center ml-0.5 -mr-0.5';
|
|
85
|
+
chevronSpan.innerHTML = IconChevronDown;
|
|
86
|
+
languageButton.appendChild(chevronSpan);
|
|
87
|
+
|
|
88
88
|
// Spacer
|
|
89
89
|
const spacer = document.createElement('div');
|
|
90
90
|
spacer.className = 'flex-1';
|
|
91
91
|
|
|
92
|
-
//
|
|
93
|
-
const {
|
|
94
|
-
? buildPreviewElements(
|
|
95
|
-
: {
|
|
96
|
-
|
|
97
|
-
// Wrap toggle button
|
|
98
|
-
const wrapButton = document.createElement('button');
|
|
99
|
-
wrapButton.type = 'button';
|
|
100
|
-
wrapButton.className = HEADER_BUTTON_STYLES;
|
|
101
|
-
wrapButton.innerHTML = IconWrap;
|
|
102
|
-
wrapButton.setAttribute('aria-label', wrapLabel);
|
|
103
|
-
wrapButton.setAttribute('data-blok-testid', 'code-wrap-btn');
|
|
104
|
-
|
|
105
|
-
// Line numbers toggle button
|
|
106
|
-
const lineNumbersButton = document.createElement('button');
|
|
107
|
-
lineNumbersButton.type = 'button';
|
|
108
|
-
lineNumbersButton.className = HEADER_BUTTON_STYLES;
|
|
109
|
-
lineNumbersButton.innerHTML = IconLineNumbers;
|
|
110
|
-
lineNumbersButton.setAttribute('aria-label', lineNumbersLabel ?? 'Line numbers');
|
|
111
|
-
lineNumbersButton.setAttribute('data-blok-testid', 'code-line-numbers-btn');
|
|
92
|
+
// Preview toggle button (only when previewable and not read-only)
|
|
93
|
+
const { previewToggleButton, previewElement } = previewable
|
|
94
|
+
? buildPreviewElements(previewToggleLabel)
|
|
95
|
+
: { previewToggleButton: null, previewElement: null };
|
|
112
96
|
|
|
113
97
|
// Copy button
|
|
114
98
|
const copyButton = document.createElement('button');
|
|
@@ -118,6 +102,54 @@ export function buildCodeDOM(options: BuildCodeDOMOptions): CodeDOMRefs {
|
|
|
118
102
|
copyButton.setAttribute('aria-label', copyLabel);
|
|
119
103
|
copyButton.setAttribute('data-blok-testid', 'code-copy-btn');
|
|
120
104
|
|
|
105
|
+
// More button (ellipsis)
|
|
106
|
+
const moreButton = document.createElement('button');
|
|
107
|
+
moreButton.type = 'button';
|
|
108
|
+
moreButton.className = HEADER_BUTTON_STYLES;
|
|
109
|
+
moreButton.innerHTML = IconEllipsis;
|
|
110
|
+
moreButton.setAttribute('aria-label', 'More');
|
|
111
|
+
moreButton.setAttribute('aria-haspopup', 'true');
|
|
112
|
+
moreButton.setAttribute('data-blok-testid', 'code-more-btn');
|
|
113
|
+
|
|
114
|
+
// More menu dropdown
|
|
115
|
+
const moreMenu = document.createElement('div');
|
|
116
|
+
moreMenu.className = MORE_MENU_STYLES;
|
|
117
|
+
moreMenu.hidden = true;
|
|
118
|
+
moreMenu.setAttribute('data-blok-testid', 'code-more-menu');
|
|
119
|
+
|
|
120
|
+
// Line numbers toggle (inside more menu)
|
|
121
|
+
const lineNumbersButton = document.createElement('button');
|
|
122
|
+
lineNumbersButton.type = 'button';
|
|
123
|
+
lineNumbersButton.className = MORE_MENU_ITEM_STYLES;
|
|
124
|
+
lineNumbersButton.setAttribute('data-blok-testid', 'code-line-numbers-btn');
|
|
125
|
+
|
|
126
|
+
const lineNumIconSpan = document.createElement('span');
|
|
127
|
+
lineNumIconSpan.className = 'flex items-center justify-center w-5 h-5';
|
|
128
|
+
lineNumIconSpan.innerHTML = IconLineNumbers;
|
|
129
|
+
lineNumbersButton.appendChild(lineNumIconSpan);
|
|
130
|
+
|
|
131
|
+
const lineNumText = document.createElement('span');
|
|
132
|
+
lineNumText.textContent = lineNumbersLabel ?? 'Line numbers';
|
|
133
|
+
lineNumbersButton.appendChild(lineNumText);
|
|
134
|
+
|
|
135
|
+
// Wrap toggle (inside more menu)
|
|
136
|
+
const wrapButton = document.createElement('button');
|
|
137
|
+
wrapButton.type = 'button';
|
|
138
|
+
wrapButton.className = MORE_MENU_ITEM_STYLES;
|
|
139
|
+
wrapButton.setAttribute('data-blok-testid', 'code-wrap-btn');
|
|
140
|
+
|
|
141
|
+
const wrapIconSpan = document.createElement('span');
|
|
142
|
+
wrapIconSpan.className = 'flex items-center justify-center w-5 h-5';
|
|
143
|
+
wrapIconSpan.innerHTML = IconWrap;
|
|
144
|
+
wrapButton.appendChild(wrapIconSpan);
|
|
145
|
+
|
|
146
|
+
const wrapText = document.createElement('span');
|
|
147
|
+
wrapText.textContent = wrapLabel;
|
|
148
|
+
wrapButton.appendChild(wrapText);
|
|
149
|
+
|
|
150
|
+
moreMenu.appendChild(lineNumbersButton);
|
|
151
|
+
moreMenu.appendChild(wrapButton);
|
|
152
|
+
|
|
121
153
|
// Code area
|
|
122
154
|
const codeElement = document.createElement('code');
|
|
123
155
|
codeElement.className = CODE_AREA_STYLES;
|
|
@@ -146,19 +178,23 @@ export function buildCodeDOM(options: BuildCodeDOMOptions): CodeDOMRefs {
|
|
|
146
178
|
gutterElement.appendChild(lineEl);
|
|
147
179
|
});
|
|
148
180
|
|
|
149
|
-
// Assemble header
|
|
181
|
+
// Assemble header: [language] [spacer] [preview toggle?] [copy] [more ▸ menu]
|
|
150
182
|
header.appendChild(languageButton);
|
|
151
183
|
header.appendChild(spacer);
|
|
152
184
|
|
|
153
|
-
if (
|
|
154
|
-
header.appendChild(
|
|
155
|
-
header.appendChild(previewTab);
|
|
185
|
+
if (previewToggleButton) {
|
|
186
|
+
header.appendChild(previewToggleButton);
|
|
156
187
|
}
|
|
157
188
|
|
|
158
|
-
header.appendChild(lineNumbersButton);
|
|
159
|
-
header.appendChild(wrapButton);
|
|
160
189
|
header.appendChild(copyButton);
|
|
161
190
|
|
|
191
|
+
// More wrapper (relative position anchor for absolute dropdown)
|
|
192
|
+
const moreWrapper = document.createElement('div');
|
|
193
|
+
moreWrapper.className = 'relative';
|
|
194
|
+
moreWrapper.appendChild(moreButton);
|
|
195
|
+
moreWrapper.appendChild(moreMenu);
|
|
196
|
+
header.appendChild(moreWrapper);
|
|
197
|
+
|
|
162
198
|
// Pre wrapper for semantic HTML
|
|
163
199
|
const preElement = document.createElement('pre');
|
|
164
200
|
preElement.appendChild(codeElement);
|
|
@@ -177,5 +213,5 @@ export function buildCodeDOM(options: BuildCodeDOMOptions): CodeDOMRefs {
|
|
|
177
213
|
wrapper.appendChild(previewElement);
|
|
178
214
|
}
|
|
179
215
|
|
|
180
|
-
return { wrapper, languageButton, lineNumbersButton, copyButton, wrapButton, preElement, codeElement, gutterElement,
|
|
216
|
+
return { wrapper, languageButton, lineNumbersButton, copyButton, wrapButton, preElement, codeElement, gutterElement, previewToggleButton, previewElement, moreButton, moreMenu };
|
|
181
217
|
}
|
package/src/tools/code/index.ts
CHANGED
|
@@ -26,11 +26,7 @@ import {
|
|
|
26
26
|
LANGUAGE_KEY,
|
|
27
27
|
COPIED_FEEDBACK_STYLES,
|
|
28
28
|
PREVIEWABLE_LANGUAGES,
|
|
29
|
-
|
|
30
|
-
PREVIEW_TAB_KEY,
|
|
31
|
-
TAB_STYLES,
|
|
32
|
-
TAB_ACTIVE_STYLES,
|
|
33
|
-
TAB_INACTIVE_STYLES,
|
|
29
|
+
PREVIEW_TOGGLE_KEY,
|
|
34
30
|
PREVIEW_AREA_STYLES,
|
|
35
31
|
GUTTER_LINE_STYLES,
|
|
36
32
|
} from './constants';
|
|
@@ -76,8 +72,7 @@ export class CodeTool implements BlockTool {
|
|
|
76
72
|
wrapLabel: this.api.i18n.t(WRAP_LINES_KEY),
|
|
77
73
|
lineNumbersLabel: this.api.i18n.t(LINE_NUMBERS_KEY),
|
|
78
74
|
previewable: this.readOnly ? false : isPreviewable,
|
|
79
|
-
|
|
80
|
-
previewTabLabel: this.api.i18n.t(PREVIEW_TAB_KEY),
|
|
75
|
+
previewToggleLabel: this.api.i18n.t(PREVIEW_TOGGLE_KEY),
|
|
81
76
|
});
|
|
82
77
|
|
|
83
78
|
this._dom = dom;
|
|
@@ -86,7 +81,10 @@ export class CodeTool implements BlockTool {
|
|
|
86
81
|
dom.gutterElement.hidden = !this._lineNumbers;
|
|
87
82
|
dom.lineNumbersButton.addEventListener('click', () => this.toggleLineNumbers());
|
|
88
83
|
|
|
89
|
-
//
|
|
84
|
+
// More menu toggle
|
|
85
|
+
dom.moreButton.addEventListener('click', () => this.toggleMoreMenu());
|
|
86
|
+
|
|
87
|
+
// Read-only + previewable: show preview only, hide code, no toggle
|
|
90
88
|
if (this.readOnly && isPreviewable) {
|
|
91
89
|
const previewEl = document.createElement('div');
|
|
92
90
|
|
|
@@ -99,8 +97,8 @@ export class CodeTool implements BlockTool {
|
|
|
99
97
|
void this.renderPreview();
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
// Edit mode + previewable: show
|
|
103
|
-
if (!this.readOnly && isPreviewable && dom.
|
|
100
|
+
// Edit mode + previewable: show preview toggle, default to preview
|
|
101
|
+
if (!this.readOnly && isPreviewable && dom.previewToggleButton && dom.previewElement) {
|
|
104
102
|
this._previewActive = true;
|
|
105
103
|
dom.preElement.hidden = true;
|
|
106
104
|
dom.gutterElement.hidden = true;
|
|
@@ -108,8 +106,7 @@ export class CodeTool implements BlockTool {
|
|
|
108
106
|
this._previewContainer = dom.previewElement;
|
|
109
107
|
void this.renderPreview();
|
|
110
108
|
|
|
111
|
-
dom.
|
|
112
|
-
dom.previewTab.addEventListener('click', () => this.showPreview());
|
|
109
|
+
dom.previewToggleButton.addEventListener('click', () => this.togglePreview());
|
|
113
110
|
}
|
|
114
111
|
|
|
115
112
|
if (!this.readOnly) {
|
|
@@ -156,8 +153,16 @@ export class CodeTool implements BlockTool {
|
|
|
156
153
|
void this.highlightCode();
|
|
157
154
|
}
|
|
158
155
|
|
|
156
|
+
private togglePreview(): void {
|
|
157
|
+
if (this._previewActive) {
|
|
158
|
+
this.showCode();
|
|
159
|
+
} else {
|
|
160
|
+
this.showPreview();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
159
164
|
private showCode(): void {
|
|
160
|
-
if (!this._dom?.previewElement || !this._dom.
|
|
165
|
+
if (!this._dom?.previewElement || !this._dom.previewToggleButton) {
|
|
161
166
|
return;
|
|
162
167
|
}
|
|
163
168
|
|
|
@@ -165,12 +170,10 @@ export class CodeTool implements BlockTool {
|
|
|
165
170
|
this._dom.preElement.hidden = false;
|
|
166
171
|
this._dom.gutterElement.hidden = !this._lineNumbers;
|
|
167
172
|
this._dom.previewElement.hidden = true;
|
|
168
|
-
this._dom.codeTab.className = `${TAB_STYLES} ${TAB_ACTIVE_STYLES}`;
|
|
169
|
-
this._dom.previewTab.className = `${TAB_STYLES} ${TAB_INACTIVE_STYLES}`;
|
|
170
173
|
}
|
|
171
174
|
|
|
172
175
|
private showPreview(): void {
|
|
173
|
-
if (!this._dom?.previewElement || !this._dom.
|
|
176
|
+
if (!this._dom?.previewElement || !this._dom.previewToggleButton) {
|
|
174
177
|
return;
|
|
175
178
|
}
|
|
176
179
|
|
|
@@ -178,13 +181,19 @@ export class CodeTool implements BlockTool {
|
|
|
178
181
|
this._dom.preElement.hidden = true;
|
|
179
182
|
this._dom.gutterElement.hidden = true;
|
|
180
183
|
this._dom.previewElement.hidden = false;
|
|
181
|
-
this._dom.codeTab.className = `${TAB_STYLES} ${TAB_INACTIVE_STYLES}`;
|
|
182
|
-
this._dom.previewTab.className = `${TAB_STYLES} ${TAB_ACTIVE_STYLES}`;
|
|
183
184
|
|
|
184
185
|
// Re-render preview with current code content
|
|
185
186
|
void this.renderPreview();
|
|
186
187
|
}
|
|
187
188
|
|
|
189
|
+
private toggleMoreMenu(): void {
|
|
190
|
+
if (!this._dom) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this._dom.moreMenu.hidden = !this._dom.moreMenu.hidden;
|
|
195
|
+
}
|
|
196
|
+
|
|
188
197
|
private async renderPreview(): Promise<void> {
|
|
189
198
|
if (!this._previewContainer) {
|
|
190
199
|
return;
|
|
@@ -281,7 +290,12 @@ export class CodeTool implements BlockTool {
|
|
|
281
290
|
this._data.language = id;
|
|
282
291
|
|
|
283
292
|
if (this._dom) {
|
|
284
|
-
|
|
293
|
+
// Update the text span inside the language button (first child)
|
|
294
|
+
const textSpan = this._dom.languageButton.querySelector('span');
|
|
295
|
+
|
|
296
|
+
if (textSpan) {
|
|
297
|
+
textSpan.textContent = this.getLanguageName(id);
|
|
298
|
+
}
|
|
285
299
|
}
|
|
286
300
|
|
|
287
301
|
this._picker?.setActiveLanguage(id);
|
|
@@ -1067,7 +1067,9 @@ export class TableCellBlocks {
|
|
|
1067
1067
|
|
|
1068
1068
|
const savedScrollY = window.scrollY;
|
|
1069
1069
|
|
|
1070
|
-
const deletePromises = blockIndices.map(index =>
|
|
1070
|
+
const deletePromises = blockIndices.map(index => {
|
|
1071
|
+
return this.api.blocks.delete(index);
|
|
1072
|
+
});
|
|
1071
1073
|
|
|
1072
1074
|
void Promise.all(deletePromises).then(() => {
|
|
1073
1075
|
if (window.scrollY !== savedScrollY) {
|