@jackuait/blok 0.19.0 → 0.19.2
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.cjs +1 -1
- package/dist/blok.iife.js +2 -2
- package/dist/blok.mjs +2 -2
- package/dist/blok.umd.js +2 -2
- package/dist/chunks/{blok-BosfCfPX.cjs → blok-C8_pY9Bg.cjs} +3 -3
- package/dist/chunks/{blok-CXB-exXU.mjs → blok-DCc23rzA.mjs} +4 -5
- package/dist/chunks/{constants-BmCvHbkL.mjs → constants-C3-Lh6Fs.mjs} +1 -1
- package/dist/chunks/{constants-ChsAFZ8F.cjs → constants-R70ypRAK.cjs} +1 -1
- package/dist/chunks/{tools-DmEzyrn3.cjs → tools-DcbePIHF.cjs} +2 -2
- package/dist/chunks/{tools-BdoYHyV0.mjs → tools-fEI-EVng.mjs} +65 -34
- package/dist/full.cjs +1 -1
- package/dist/full.mjs +3 -3
- package/dist/react.cjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/tools.cjs +1 -1
- package/dist/tools.mjs +2 -2
- package/package.json +3 -3
- package/src/blok.ts +7 -3
- package/src/components/modules/api/blocks.ts +1 -1
- package/src/components/modules/blockManager/blockManager.ts +2 -2
- package/src/components/modules/yjs/serializer.ts +1 -1
- package/src/components/utils/data-model-transform.ts +15 -15
- package/src/components/utils/popover/popover-desktop.ts +6 -3
- package/src/components/utils/tooltip.ts +5 -38
- package/src/markdown/markdown-handler.ts +1 -2
- package/src/styles/audio.css +23 -3
- package/src/tools/audio/index.ts +76 -21
- package/src/tools/audio/ui.ts +6 -1
- package/types/api/tooltip.d.ts +40 -1
- package/types/data-formats/output-data.d.ts +5 -2
- package/types/index.d.ts +28 -5
- package/types/tools/database.d.ts +1 -1
- package/types/tools/header.d.ts +2 -1
- package/types/tools/list.d.ts +2 -1
- package/types/utils/popover/popover.d.ts +42 -1
package/src/tools/audio/index.ts
CHANGED
|
@@ -52,6 +52,8 @@ export class AudioTool implements BlockTool {
|
|
|
52
52
|
private controlsHandle: ControlsHandle | null = null;
|
|
53
53
|
private waveformHandle: WaveformHandle | null = null;
|
|
54
54
|
private destroyed = false;
|
|
55
|
+
/** Pending fallback timer for the caption collapse-out animation. */
|
|
56
|
+
private captionExitTimer: ReturnType<typeof setTimeout> | null = null;
|
|
55
57
|
|
|
56
58
|
constructor(options: BlockToolConstructorOptions<AudioData, AudioConfig>) {
|
|
57
59
|
this.api = options.api;
|
|
@@ -199,6 +201,7 @@ export class AudioTool implements BlockTool {
|
|
|
199
201
|
|
|
200
202
|
public removed(): void {
|
|
201
203
|
this.destroyed = true;
|
|
204
|
+
if (this.captionExitTimer) { clearTimeout(this.captionExitTimer); this.captionExitTimer = null; }
|
|
202
205
|
this.controlsHandle?.destroy();
|
|
203
206
|
this.controlsHandle = null;
|
|
204
207
|
this.waveformHandle?.destroy();
|
|
@@ -299,6 +302,7 @@ export class AudioTool implements BlockTool {
|
|
|
299
302
|
|
|
300
303
|
private renderState(): void {
|
|
301
304
|
if (!this.root) return;
|
|
305
|
+
if (this.captionExitTimer) { clearTimeout(this.captionExitTimer); this.captionExitTimer = null; }
|
|
302
306
|
this.controlsHandle?.destroy();
|
|
303
307
|
this.controlsHandle = null;
|
|
304
308
|
this.waveformHandle?.destroy();
|
|
@@ -393,14 +397,9 @@ export class AudioTool implements BlockTool {
|
|
|
393
397
|
body.appendChild(this.controlsHandle.element);
|
|
394
398
|
|
|
395
399
|
// Caption row — outside body, below the card
|
|
396
|
-
const placeholder = this.config.captionPlaceholder ?? DEFAULT_CAPTION_PLACEHOLDER;
|
|
397
400
|
const captionVisible = this.data.captionVisible !== false;
|
|
398
|
-
if (captionVisible
|
|
399
|
-
figure.appendChild(
|
|
400
|
-
value: this.data.caption ?? '',
|
|
401
|
-
placeholder,
|
|
402
|
-
editable: !this.readOnly,
|
|
403
|
-
}));
|
|
401
|
+
if (captionVisible) {
|
|
402
|
+
figure.appendChild(this.createCaptionRow());
|
|
404
403
|
}
|
|
405
404
|
|
|
406
405
|
// Wire title/artist blur handlers
|
|
@@ -419,21 +418,32 @@ export class AudioTool implements BlockTool {
|
|
|
419
418
|
}
|
|
420
419
|
});
|
|
421
420
|
|
|
422
|
-
// Wire caption blur handler
|
|
423
|
-
const captionEl = figure.querySelector<HTMLElement>('[data-role="audio-caption"]');
|
|
424
|
-
if (captionEl) {
|
|
425
|
-
captionEl.addEventListener('blur', () => {
|
|
426
|
-
const next = captionEl.textContent ?? '';
|
|
427
|
-
if (next !== this.data.caption) {
|
|
428
|
-
this.data.caption = next || undefined;
|
|
429
|
-
this.block.dispatchChange();
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
|
|
434
421
|
this.root.appendChild(figure);
|
|
435
422
|
}
|
|
436
423
|
|
|
424
|
+
/** Build a caption row (with its blur handler wired) ready to mount. */
|
|
425
|
+
private createCaptionRow(): HTMLElement {
|
|
426
|
+
const placeholder = this.config.captionPlaceholder ?? DEFAULT_CAPTION_PLACEHOLDER;
|
|
427
|
+
const row = renderCaptionRow({
|
|
428
|
+
value: this.data.caption ?? '',
|
|
429
|
+
placeholder,
|
|
430
|
+
editable: !this.readOnly,
|
|
431
|
+
});
|
|
432
|
+
const captionEl = row.querySelector<HTMLElement>('[data-role="audio-caption"]');
|
|
433
|
+
captionEl?.addEventListener('blur', () => {
|
|
434
|
+
const next = captionEl.textContent ?? '';
|
|
435
|
+
if (next !== this.data.caption) {
|
|
436
|
+
this.data.caption = next || undefined;
|
|
437
|
+
this.block.dispatchChange();
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
return row;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
private prefersReducedMotion(): boolean {
|
|
444
|
+
return globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
|
|
445
|
+
}
|
|
446
|
+
|
|
437
447
|
private setAlignment(next: AudioAlignment): void {
|
|
438
448
|
if (this.data.alignment === next) return;
|
|
439
449
|
this.data.alignment = next;
|
|
@@ -442,9 +452,54 @@ export class AudioTool implements BlockTool {
|
|
|
442
452
|
}
|
|
443
453
|
|
|
444
454
|
private toggleCaption(): void {
|
|
445
|
-
|
|
455
|
+
const enabling = this.data.captionVisible === false;
|
|
456
|
+
this.data.captionVisible = enabling;
|
|
457
|
+
// Removing the caption clears its text too, so re-enabling starts fresh
|
|
458
|
+
// rather than resurrecting the old text.
|
|
459
|
+
if (!enabling) this.data.caption = undefined;
|
|
446
460
|
this.block.dispatchChange();
|
|
447
|
-
|
|
461
|
+
|
|
462
|
+
const figure = this.root?.querySelector<HTMLElement>('[data-role="audio-figure"]');
|
|
463
|
+
// No live card to animate (or motion is off) — fall back to an instant
|
|
464
|
+
// re-render so the caption snaps in/out.
|
|
465
|
+
if (!figure || this.prefersReducedMotion()) {
|
|
466
|
+
this.renderState();
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
this.syncRootAttributes();
|
|
470
|
+
if (enabling) this.animateCaptionIn(figure);
|
|
471
|
+
else this.animateCaptionOut(figure);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private animateCaptionIn(figure: HTMLElement): void {
|
|
475
|
+
const existing = figure.querySelector<HTMLElement>('[data-role="audio-caption-row"]');
|
|
476
|
+
if (existing) {
|
|
477
|
+
// A collapse-out was mid-flight — cancel it and re-open in place.
|
|
478
|
+
if (this.captionExitTimer) { clearTimeout(this.captionExitTimer); this.captionExitTimer = null; }
|
|
479
|
+
existing.classList.remove('is-collapsed');
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const row = this.createCaptionRow();
|
|
483
|
+
row.classList.add('is-collapsed');
|
|
484
|
+
figure.appendChild(row);
|
|
485
|
+
// Force a reflow so the collapsed state is committed, then expand into view.
|
|
486
|
+
void row.offsetHeight;
|
|
487
|
+
row.classList.remove('is-collapsed');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
private animateCaptionOut(figure: HTMLElement): void {
|
|
491
|
+
const row = figure.querySelector<HTMLElement>('[data-role="audio-caption-row"]');
|
|
492
|
+
if (!row) return;
|
|
493
|
+
row.classList.add('is-collapsed');
|
|
494
|
+
const finish = (): void => {
|
|
495
|
+
if (this.captionExitTimer) { clearTimeout(this.captionExitTimer); this.captionExitTimer = null; }
|
|
496
|
+
row.remove();
|
|
497
|
+
};
|
|
498
|
+
row.addEventListener('transitionend', (e) => {
|
|
499
|
+
if (e.propertyName === 'grid-template-rows') finish();
|
|
500
|
+
}, { once: true });
|
|
501
|
+
// Fallback in case transitionend never fires (display:none, interrupted, etc.).
|
|
502
|
+
this.captionExitTimer = setTimeout(finish, 360);
|
|
448
503
|
}
|
|
449
504
|
|
|
450
505
|
private toggleLoop(): void {
|
package/src/tools/audio/ui.ts
CHANGED
|
@@ -97,8 +97,13 @@ export function renderCaptionRow(opts: CaptionRowOptions): HTMLElement {
|
|
|
97
97
|
const row = document.createElement('div');
|
|
98
98
|
row.className = 'blok-audio-caption-row';
|
|
99
99
|
row.setAttribute('data-role', 'audio-caption-row');
|
|
100
|
+
// Inner wrapper carries the visible layout (padding/border) so the row itself
|
|
101
|
+
// can collapse cleanly via grid-template-rows when toggled.
|
|
102
|
+
const inner = document.createElement('div');
|
|
103
|
+
inner.className = 'blok-audio-caption-row__inner';
|
|
100
104
|
const cap = editableLine('audio-caption', opts.value, opts.editable, opts.placeholder);
|
|
101
105
|
cap.className = 'blok-audio-caption';
|
|
102
|
-
|
|
106
|
+
inner.appendChild(cap);
|
|
107
|
+
row.appendChild(inner);
|
|
103
108
|
return row;
|
|
104
109
|
}
|
package/types/api/tooltip.d.ts
CHANGED
|
@@ -1,7 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tooltip API
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Tooltip supported content
|
|
7
|
+
*/
|
|
8
|
+
export type TooltipContent = HTMLElement | DocumentFragment | Node | string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Base options interface for tooltips
|
|
12
|
+
*/
|
|
13
|
+
export interface TooltipOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Tooltip placement: top|bottom|left|right
|
|
16
|
+
*/
|
|
17
|
+
placement?: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Tooltip top margin
|
|
21
|
+
*/
|
|
22
|
+
marginTop?: number;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Tooltip left margin
|
|
26
|
+
*/
|
|
27
|
+
marginLeft?: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Tooltip right margin
|
|
31
|
+
*/
|
|
32
|
+
marginRight?: number;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Tooltip bottom margin
|
|
36
|
+
*/
|
|
37
|
+
marginBottom?: number;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Timeout before showing
|
|
41
|
+
*/
|
|
42
|
+
delay?: number;
|
|
43
|
+
}
|
|
5
44
|
|
|
6
45
|
export interface Tooltip {
|
|
7
46
|
/**
|
|
@@ -6,9 +6,12 @@ import { BlockId } from './block-id';
|
|
|
6
6
|
* Output of one Tool
|
|
7
7
|
*
|
|
8
8
|
* @template Type - the string literal describing a tool type
|
|
9
|
-
* @template Data - the structure describing a data object supported by the tool
|
|
9
|
+
* @template Data - the structure describing a data object supported by the tool.
|
|
10
|
+
* Defaults to `Record<string, unknown>` (matching {@link BlockToolData}) so
|
|
11
|
+
* reading saved block data is type-guarded: property access yields `unknown`,
|
|
12
|
+
* not `any`. Pass a concrete shape for fully-typed access.
|
|
10
13
|
*/
|
|
11
|
-
export interface OutputBlockData<Type extends string = string, Data extends object =
|
|
14
|
+
export interface OutputBlockData<Type extends string = string, Data extends object = Record<string, unknown>> {
|
|
12
15
|
/**
|
|
13
16
|
* Unique Id of the block
|
|
14
17
|
*/
|
package/types/index.d.ts
CHANGED
|
@@ -5,12 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
Dictionary,
|
|
9
|
-
DictValue,
|
|
10
8
|
BlokConfig,
|
|
11
9
|
I18nConfig,
|
|
12
10
|
I18nDictionary,
|
|
13
11
|
} from './configs';
|
|
12
|
+
import { InlineToolConstructable, InlineToolConstructorOptions } from './tools';
|
|
14
13
|
|
|
15
14
|
import {
|
|
16
15
|
Blocks,
|
|
@@ -81,8 +80,6 @@ export {
|
|
|
81
80
|
LogLevels,
|
|
82
81
|
ConversionConfig,
|
|
83
82
|
I18nDictionary,
|
|
84
|
-
Dictionary,
|
|
85
|
-
DictValue,
|
|
86
83
|
I18nConfig,
|
|
87
84
|
UserInfo,
|
|
88
85
|
} from './configs';
|
|
@@ -207,11 +204,37 @@ export function wrapLegacyInlineTool(
|
|
|
207
204
|
}
|
|
208
205
|
): InlineToolConstructable;
|
|
209
206
|
|
|
207
|
+
/**
|
|
208
|
+
* The surface of a Blok instance that is guaranteed to exist synchronously,
|
|
209
|
+
* immediately after `new Blok()` and before `isReady` has resolved.
|
|
210
|
+
*
|
|
211
|
+
* Blok constructs its module APIs (`blocks`, `caret`, `history`, `readOnly`, …)
|
|
212
|
+
* asynchronously: they only become available once `isReady` resolves. Accessing
|
|
213
|
+
* them earlier returns `undefined` at runtime. Type a reference you hold during
|
|
214
|
+
* that window as `PendingBlok` so the not-yet-available members are unreachable,
|
|
215
|
+
* then await `isReady` to obtain the fully-initialized {@link Blok}:
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* const pending: PendingBlok = new Blok(config);
|
|
219
|
+
* const editor = await pending.isReady; // editor is a ready Blok
|
|
220
|
+
* editor.blocks.render(data);
|
|
221
|
+
*/
|
|
222
|
+
export interface PendingBlok {
|
|
223
|
+
/** Resolves with the fully-initialized Blok instance once core modules are ready. */
|
|
224
|
+
isReady: Promise<Blok>;
|
|
225
|
+
/** Destroy the instance. Safe to call before `isReady` resolves. */
|
|
226
|
+
destroy(): void;
|
|
227
|
+
/** Theme API, exposed immediately after construction. */
|
|
228
|
+
theme: Theme;
|
|
229
|
+
/** Width API, exposed immediately after construction. */
|
|
230
|
+
width: Width;
|
|
231
|
+
}
|
|
232
|
+
|
|
210
233
|
/**
|
|
211
234
|
* Main Blok class
|
|
212
235
|
*/
|
|
213
236
|
export class Blok {
|
|
214
|
-
public isReady: Promise<
|
|
237
|
+
public isReady: Promise<Blok>;
|
|
215
238
|
|
|
216
239
|
public blocks: Blocks;
|
|
217
240
|
public caret: Caret;
|
package/types/tools/header.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { BlockTool, BlockToolConstructable, BlockToolConstructorOptions
|
|
1
|
+
import { BlockTool, BlockToolConstructable, BlockToolConstructorOptions } from './block-tool';
|
|
2
|
+
import { BlockToolData } from './block-tool-data';
|
|
2
3
|
import { MenuConfig } from './menu-config';
|
|
3
4
|
|
|
4
5
|
/**
|
package/types/tools/list.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { BlockTool, BlockToolConstructable, BlockToolConstructorOptions
|
|
1
|
+
import { BlockTool, BlockToolConstructable, BlockToolConstructorOptions } from './block-tool';
|
|
2
|
+
import { BlockToolData } from './block-tool-data';
|
|
2
3
|
import { MenuConfig } from './menu-config';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -1,7 +1,48 @@
|
|
|
1
1
|
import { PopoverItemParams } from './popover-item';
|
|
2
|
-
import type { Flipper } from '../../../src/components/flipper';
|
|
3
2
|
import { PopoverEvent } from './popover-event';
|
|
4
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Public surface of the internal keyboard-navigation Flipper that a popover
|
|
6
|
+
* exposes for reuse via {@link PopoverParams.flipper}.
|
|
7
|
+
*
|
|
8
|
+
* Declared structurally here (rather than imported from raw src) so the
|
|
9
|
+
* published declarations stay self-contained: a bare `import` of the package
|
|
10
|
+
* must never drag Blok's `.ts` source into a consumer's type program. The
|
|
11
|
+
* concrete Flipper is internal — consumers never construct it; it is only
|
|
12
|
+
* passed between Blok's own popovers.
|
|
13
|
+
*/
|
|
14
|
+
export interface Flipper {
|
|
15
|
+
/** True while the flipper is handling keyboard navigation. */
|
|
16
|
+
readonly isActivated: boolean;
|
|
17
|
+
|
|
18
|
+
/** Begin handling keyboard navigation over the given items. */
|
|
19
|
+
activate(items?: HTMLElement[], cursorPosition?: number): void;
|
|
20
|
+
|
|
21
|
+
/** Stop handling keyboard navigation. */
|
|
22
|
+
deactivate(): void;
|
|
23
|
+
|
|
24
|
+
/** Focus the first navigable item. */
|
|
25
|
+
focusFirst(): void;
|
|
26
|
+
|
|
27
|
+
/** Focus the item at the given position. */
|
|
28
|
+
focusItem(position: number, options?: { skipNextTab?: boolean }): void;
|
|
29
|
+
|
|
30
|
+
/** True if a navigable item currently has focus. */
|
|
31
|
+
hasFocus(): boolean;
|
|
32
|
+
|
|
33
|
+
/** Register a callback fired on each flip (navigation step). */
|
|
34
|
+
onFlip(cb: () => void): void;
|
|
35
|
+
|
|
36
|
+
/** Remove a previously registered flip callback. */
|
|
37
|
+
removeOnFlip(cb: () => void): void;
|
|
38
|
+
|
|
39
|
+
/** Toggle handling of keydown events originating from contenteditable targets. */
|
|
40
|
+
setHandleContentEditableTargets(value: boolean): void;
|
|
41
|
+
|
|
42
|
+
/** Whether keydown events from contenteditable targets are handled. */
|
|
43
|
+
getHandleContentEditableTargets(): boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
5
46
|
/**
|
|
6
47
|
* Params required to render popover
|
|
7
48
|
*/
|