@bloklabs/angular 1.3.0 → 1.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.
- package/README.md +65 -0
- package/fesm2022/bloklabs-angular.mjs +192 -9
- package/fesm2022/bloklabs-angular.mjs.map +1 -1
- package/package.json +23 -3
- package/types/bloklabs-angular.d.ts +81 -8
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @bloklabs/angular
|
|
2
|
+
|
|
3
|
+
The Angular block editor bindings for [Blok](https://blokeditor.com) — a headless, Notion-like rich text editor whose content is JSON blocks, not HTML.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @bloklabs/angular @bloklabs/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`@bloklabs/core` and `@angular/core` / `@angular/common` / `@angular/forms` are peer dependencies.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
`BlokEditorComponent` is a standalone component: it mounts the editor, manages its lifecycle, and takes every editor config option as an input.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { Component } from '@angular/core';
|
|
19
|
+
import { BlokEditorComponent } from '@bloklabs/angular';
|
|
20
|
+
import { Header, Paragraph, List } from '@bloklabs/core/tools';
|
|
21
|
+
|
|
22
|
+
@Component({
|
|
23
|
+
selector: 'app-editor',
|
|
24
|
+
standalone: true,
|
|
25
|
+
imports: [BlokEditorComponent],
|
|
26
|
+
template: `<blok-editor [tools]="tools" />`,
|
|
27
|
+
})
|
|
28
|
+
export class EditorComponent {
|
|
29
|
+
tools = {
|
|
30
|
+
paragraph: Paragraph,
|
|
31
|
+
header: { class: Header, placeholder: 'Enter a heading' },
|
|
32
|
+
list: List,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Saving
|
|
38
|
+
|
|
39
|
+
The `(save)` output fires with the latest content on every change, so you never have to call `save()` by hand.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import type { OutputData } from '@bloklabs/core';
|
|
43
|
+
|
|
44
|
+
// template: <blok-editor [tools]="tools" (save)="onSave($event)" />
|
|
45
|
+
onSave(data: OutputData) {
|
|
46
|
+
console.log(data.blocks);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The component also implements `ControlValueAccessor`, so it works with `[(ngModel)]` and reactive forms.
|
|
51
|
+
|
|
52
|
+
## Also exported
|
|
53
|
+
|
|
54
|
+
- `BlokContentDirective` (`[blokContent]`) — the mount point, if you drive the editor yourself.
|
|
55
|
+
- `injectBlocks()` — a reactive snapshot of the block tree plus a manipulation API.
|
|
56
|
+
- `createAngularBlock` — author block tools as Angular components.
|
|
57
|
+
- `provideBlok` / `BLOK_DEFAULT_CONFIG` — share default editor config through DI.
|
|
58
|
+
|
|
59
|
+
## Docs
|
|
60
|
+
|
|
61
|
+
- [Quick start](https://blokeditor.com/docs/quick-start?framework=angular)
|
|
62
|
+
- [`BlokEditor` reference](https://blokeditor.com/docs/blok-editor?framework=angular)
|
|
63
|
+
- [`useBlocks` reference](https://blokeditor.com/docs/use-blocks?framework=angular)
|
|
64
|
+
|
|
65
|
+
Licensed under Apache-2.0.
|
|
@@ -1754,9 +1754,15 @@ const DATA_ATTR = {
|
|
|
1754
1754
|
/** Editor or element is empty */
|
|
1755
1755
|
empty: 'data-blok-empty',
|
|
1756
1756
|
/** Present on the editor wrapper while read-only mode is active.
|
|
1757
|
-
* Public styling hook — lets hosts
|
|
1758
|
-
*
|
|
1757
|
+
* Public styling hook — lets hosts key rules off the editing state
|
|
1758
|
+
* without JS. Deliberately does NOT collapse the gutter: plain
|
|
1759
|
+
* read-only still shows the block-hover copy-link control there, and
|
|
1760
|
+
* in-place readOnly.set() flips must not shift the layout. */
|
|
1759
1761
|
readonly: 'data-blok-readonly',
|
|
1762
|
+
/** Present on the editor wrapper while read-only mode hides ALL editor
|
|
1763
|
+
* controls (readOnly: { hideControls: true }). Public styling hook —
|
|
1764
|
+
* drives the gutter auto-collapse for genuinely chromeless read-only. */
|
|
1765
|
+
controlsHidden: 'data-blok-controls-hidden',
|
|
1760
1766
|
/** Present on the editor wrapper when config.hideToolbar is true.
|
|
1761
1767
|
* Public styling hook — drives the gutter auto-collapse (the gutter
|
|
1762
1768
|
* exists solely to house the toolbar's +/⠿ controls). */
|
|
@@ -2067,8 +2073,16 @@ const mountChildBlocks = (container, children) => {
|
|
|
2067
2073
|
* `BlokEditor`). Delegates instance lifecycle to an internal `BlokContentDirective`
|
|
2068
2074
|
* and layers the typed reactive input/output API on top.
|
|
2069
2075
|
*
|
|
2070
|
-
* Reactive inputs (`readOnly`, `
|
|
2071
|
-
*
|
|
2076
|
+
* Reactive inputs (`readOnly`, `hideToolbar`, `inlineToolbar`, `theme`,
|
|
2077
|
+
* `width`, `placeholder`, `autofocus`, `styleTokens`, `i18n`, `data`) are
|
|
2078
|
+
* synced in place after mount via effects — `hideToolbar` through
|
|
2079
|
+
* `editor.toolbar.setHidden()`, `inlineToolbar` through
|
|
2080
|
+
* `editor.tools.setInlineToolbar()` (content-compared),
|
|
2081
|
+
* `styleTokens` through `editor.tokens.set()` (replace semantics),
|
|
2082
|
+
* `i18n` through `editor.i18n.update()` (deep-equal–deduped; seeded at
|
|
2083
|
+
* construction so the locale resolves during boot) and `data`
|
|
2084
|
+
* through `editor.render()` (deep-equal–deduped); everything else seeds
|
|
2085
|
+
* construction.
|
|
2072
2086
|
*
|
|
2073
2087
|
* Implementation note: classic `@Input()`/`@ViewChild()` decorators are used
|
|
2074
2088
|
* (not signal `input()`/`viewChild()`) for JIT compatibility — see
|
|
@@ -2114,14 +2128,32 @@ class BlokEditorComponent {
|
|
|
2114
2128
|
* (possibly modified) html, or null to drop the paste.
|
|
2115
2129
|
*/
|
|
2116
2130
|
onBeforePaste;
|
|
2131
|
+
/**
|
|
2132
|
+
* Error channel (core `onError`). Fires with the raised error whenever an
|
|
2133
|
+
* editor operation — currently serialization — fails instead of only logging.
|
|
2134
|
+
*/
|
|
2135
|
+
onError;
|
|
2117
2136
|
// ---- Reactive inputs (signal-backed setters; synced in place, never recreate) ----
|
|
2118
2137
|
// Default undefined (not false) so an unset input falls back to a provideBlok
|
|
2119
2138
|
// default; the readOnly effect / buildConfig coerce undefined to false.
|
|
2139
|
+
// Accepts the object form (`{ hideControls }`) too — it flows through
|
|
2140
|
+
// buildConfig unchanged and the effect expands it to
|
|
2141
|
+
// `readOnly.set(enabled, { hideControls })`.
|
|
2120
2142
|
readOnly$ = signal(undefined, /* @ts-ignore */
|
|
2121
2143
|
...(ngDevMode ? [{ debugName: "readOnly$" }] : /* istanbul ignore next */ []));
|
|
2122
2144
|
set readOnly(value) {
|
|
2123
2145
|
this.readOnly$.set(value);
|
|
2124
2146
|
}
|
|
2147
|
+
hideToolbar$ = signal(undefined, /* @ts-ignore */
|
|
2148
|
+
...(ngDevMode ? [{ debugName: "hideToolbar$" }] : /* istanbul ignore next */ []));
|
|
2149
|
+
set hideToolbar(value) {
|
|
2150
|
+
this.hideToolbar$.set(value);
|
|
2151
|
+
}
|
|
2152
|
+
inlineToolbar$ = signal(undefined, /* @ts-ignore */
|
|
2153
|
+
...(ngDevMode ? [{ debugName: "inlineToolbar$" }] : /* istanbul ignore next */ []));
|
|
2154
|
+
set inlineToolbar(value) {
|
|
2155
|
+
this.inlineToolbar$.set(value);
|
|
2156
|
+
}
|
|
2125
2157
|
theme$ = signal(undefined, /* @ts-ignore */
|
|
2126
2158
|
...(ngDevMode ? [{ debugName: "theme$" }] : /* istanbul ignore next */ []));
|
|
2127
2159
|
set theme(value) {
|
|
@@ -2147,6 +2179,18 @@ class BlokEditorComponent {
|
|
|
2147
2179
|
set styleTokens(value) {
|
|
2148
2180
|
this.styleTokens$.set(value);
|
|
2149
2181
|
}
|
|
2182
|
+
/**
|
|
2183
|
+
* Locale, host message overrides and text direction. `config.i18n` was
|
|
2184
|
+
* consumed once at boot, so a host driving a language switcher had to
|
|
2185
|
+
* recreate the editor (losing caret, focus and undo stack) to relabel the
|
|
2186
|
+
* UI; this drives the runtime `i18n.update` API instead. `defaultLocale` is
|
|
2187
|
+
* not forwarded — it only affects the INITIAL locale resolution.
|
|
2188
|
+
*/
|
|
2189
|
+
i18n$ = signal(undefined, /* @ts-ignore */
|
|
2190
|
+
...(ngDevMode ? [{ debugName: "i18n$" }] : /* istanbul ignore next */ []));
|
|
2191
|
+
set i18n(value) {
|
|
2192
|
+
this.i18n$.set(value);
|
|
2193
|
+
}
|
|
2150
2194
|
autofocus$ = signal(false, /* @ts-ignore */
|
|
2151
2195
|
...(ngDevMode ? [{ debugName: "autofocus$" }] : /* istanbul ignore next */ []));
|
|
2152
2196
|
set autofocus(value) {
|
|
@@ -2162,7 +2206,7 @@ class BlokEditorComponent {
|
|
|
2162
2206
|
tools;
|
|
2163
2207
|
/**
|
|
2164
2208
|
* Escape hatch: a full config object for keys without a dedicated input
|
|
2165
|
-
* (
|
|
2209
|
+
* (sanitizer, minHeight, …). Layered between provideBlok
|
|
2166
2210
|
* defaults and the discrete inputs.
|
|
2167
2211
|
*/
|
|
2168
2212
|
config;
|
|
@@ -2176,6 +2220,12 @@ class BlokEditorComponent {
|
|
|
2176
2220
|
lastRenderedData;
|
|
2177
2221
|
/** Last token set pushed through `tokens.set`, for deep-equal deduping. */
|
|
2178
2222
|
appliedTokens;
|
|
2223
|
+
/** Last i18n config pushed through `i18n.update`, for deep-equal deduping. */
|
|
2224
|
+
appliedI18n;
|
|
2225
|
+
/** Editor instance the i18n baseline above belongs to. */
|
|
2226
|
+
i18nAppliedFor = null;
|
|
2227
|
+
/** Last value pushed through `tools.setInlineToolbar`, for content-compare deduping. */
|
|
2228
|
+
appliedInlineToolbar;
|
|
2179
2229
|
seededEditor = null;
|
|
2180
2230
|
renderChain = Promise.resolve();
|
|
2181
2231
|
/** Registered by `ControlValueAccessor.registerOnChange` (Angular forms). */
|
|
@@ -2210,7 +2260,17 @@ class BlokEditorComponent {
|
|
|
2210
2260
|
buildConfig() {
|
|
2211
2261
|
// Precedence: provideBlok defaults < [config] escape hatch < discrete inputs.
|
|
2212
2262
|
const cfg = { ...this.defaults, ...this.config };
|
|
2263
|
+
// The object form ({ hideControls }) is preserved as-is; the readOnly
|
|
2264
|
+
// effect expands it into `readOnly.set(enabled, { hideControls })`.
|
|
2213
2265
|
cfg.readOnly = this.readOnly$() ?? this.config?.readOnly ?? this.defaults.readOnly ?? false;
|
|
2266
|
+
const hideToolbar = this.hideToolbar$();
|
|
2267
|
+
if (hideToolbar !== undefined) {
|
|
2268
|
+
cfg.hideToolbar = hideToolbar;
|
|
2269
|
+
}
|
|
2270
|
+
const inlineToolbar = this.inlineToolbar$();
|
|
2271
|
+
if (inlineToolbar !== undefined) {
|
|
2272
|
+
cfg.inlineToolbar = inlineToolbar;
|
|
2273
|
+
}
|
|
2214
2274
|
// tools registries are MERGED (not replaced) across all three layers so a
|
|
2215
2275
|
// shared registry composes with per-instance additions.
|
|
2216
2276
|
if (this.tools !== undefined ||
|
|
@@ -2234,6 +2294,12 @@ class BlokEditorComponent {
|
|
|
2234
2294
|
if (placeholder !== undefined) {
|
|
2235
2295
|
cfg.placeholder = placeholder;
|
|
2236
2296
|
}
|
|
2297
|
+
// Seeded at construction (not applied by the i18n effect) so the locale is
|
|
2298
|
+
// resolved during boot instead of flashing the default language first.
|
|
2299
|
+
const i18n = this.i18n$();
|
|
2300
|
+
if (i18n !== undefined) {
|
|
2301
|
+
cfg.i18n = i18n;
|
|
2302
|
+
}
|
|
2237
2303
|
// Opt-in: only attach each core callback when the consumer actually consumes
|
|
2238
2304
|
// it (an observed output, a registered forms hook, or a provided transform).
|
|
2239
2305
|
// Their mere presence makes the core serialize / run hooks on every change.
|
|
@@ -2259,6 +2325,10 @@ class BlokEditorComponent {
|
|
|
2259
2325
|
if (beforePaste !== undefined) {
|
|
2260
2326
|
cfg.onBeforePaste = (html) => beforePaste(html);
|
|
2261
2327
|
}
|
|
2328
|
+
const onError = this.onError;
|
|
2329
|
+
if (onError !== undefined) {
|
|
2330
|
+
cfg.onError = onError;
|
|
2331
|
+
}
|
|
2262
2332
|
return cfg;
|
|
2263
2333
|
}
|
|
2264
2334
|
ngAfterViewInit() {
|
|
@@ -2282,10 +2352,38 @@ class BlokEditorComponent {
|
|
|
2282
2352
|
// (the Angular analog of React's `editor` effect-dependency).
|
|
2283
2353
|
effect(() => {
|
|
2284
2354
|
const editor = this.instance();
|
|
2285
|
-
const readOnly = this.readOnly$();
|
|
2355
|
+
const readOnly = this.readOnly$() ?? this.config?.readOnly;
|
|
2286
2356
|
if (editor) {
|
|
2287
|
-
|
|
2357
|
+
const { enabled, hideControls } = normalizeReadOnlyConfig(readOnly);
|
|
2358
|
+
// Only the object form carries options; the boolean form keeps the
|
|
2359
|
+
// one-argument call so it never clobbers a config-seeded hideControls.
|
|
2360
|
+
if (typeof readOnly === 'object' && readOnly !== null) {
|
|
2361
|
+
void editor.readOnly.set(enabled, { hideControls });
|
|
2362
|
+
}
|
|
2363
|
+
else {
|
|
2364
|
+
void editor.readOnly.set(enabled);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2368
|
+
effect(() => {
|
|
2369
|
+
const editor = this.instance();
|
|
2370
|
+
const hideToolbar = this.hideToolbar$();
|
|
2371
|
+
if (editor && hideToolbar !== undefined) {
|
|
2372
|
+
editor.toolbar.setHidden(hideToolbar);
|
|
2373
|
+
}
|
|
2374
|
+
});
|
|
2375
|
+
// Content-compared (not identity): a new array with the same tool names is
|
|
2376
|
+
// a no-op, so template-inline array literals don't thrash the assignment.
|
|
2377
|
+
effect(() => {
|
|
2378
|
+
const editor = this.instance();
|
|
2379
|
+
const inlineToolbar = this.inlineToolbar$();
|
|
2380
|
+
if (!editor ||
|
|
2381
|
+
inlineToolbar === undefined ||
|
|
2382
|
+
deepEqual(inlineToolbar, this.appliedInlineToolbar)) {
|
|
2383
|
+
return;
|
|
2288
2384
|
}
|
|
2385
|
+
this.appliedInlineToolbar = Array.isArray(inlineToolbar) ? [...inlineToolbar] : inlineToolbar;
|
|
2386
|
+
editor.tools.setInlineToolbar(inlineToolbar);
|
|
2289
2387
|
});
|
|
2290
2388
|
effect(() => {
|
|
2291
2389
|
const editor = this.instance();
|
|
@@ -2317,6 +2415,34 @@ class BlokEditorComponent {
|
|
|
2317
2415
|
this.appliedTokens = { ...tokens };
|
|
2318
2416
|
editor.tokens.set(tokens);
|
|
2319
2417
|
});
|
|
2418
|
+
effect(() => {
|
|
2419
|
+
const editor = this.instance();
|
|
2420
|
+
const i18n = this.i18n$() ?? this.config?.i18n;
|
|
2421
|
+
if (!editor) {
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
/*
|
|
2425
|
+
* A freshly constructed editor already resolved this config during its
|
|
2426
|
+
* own boot (buildConfig seeds it), so record the baseline and push
|
|
2427
|
+
* nothing — re-applying it here would reload the locale after mount and
|
|
2428
|
+
* flash the default language.
|
|
2429
|
+
*/
|
|
2430
|
+
if (this.i18nAppliedFor !== editor) {
|
|
2431
|
+
this.i18nAppliedFor = editor;
|
|
2432
|
+
this.appliedI18n = i18n === undefined ? undefined : { ...i18n };
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
if (i18n === undefined || deepEqual(i18n, this.appliedI18n)) {
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
this.appliedI18n = { ...i18n };
|
|
2439
|
+
const { locale, messages, direction } = i18n;
|
|
2440
|
+
void editor.i18n.update({
|
|
2441
|
+
...(locale === undefined ? {} : { locale }),
|
|
2442
|
+
...(messages === undefined ? {} : { messages }),
|
|
2443
|
+
...(direction === undefined ? {} : { direction }),
|
|
2444
|
+
});
|
|
2445
|
+
});
|
|
2320
2446
|
effect(() => {
|
|
2321
2447
|
const editor = this.instance();
|
|
2322
2448
|
if (editor && this.autofocus$()) {
|
|
@@ -2397,7 +2523,7 @@ class BlokEditorComponent {
|
|
|
2397
2523
|
this.readOnly$.set(isDisabled);
|
|
2398
2524
|
}
|
|
2399
2525
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: BlokEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2400
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.7", type: BlokEditorComponent, isStandalone: true, selector: "blok-editor", inputs: { onBeforeRender: "onBeforeRender", onBeforePaste: "onBeforePaste", readOnly: "readOnly", theme: "theme", width: "width", placeholder: "placeholder", styleTokens: "styleTokens", autofocus: "autofocus", data: "data", tools: "tools", config: "config", recreateKey: "recreateKey" }, outputs: { ready: "ready", dataChange: "dataChange", save: "save", change: "change", afterRender: "afterRender", themeChange: "themeChange", blocksRendered: "blocksRendered", blockRendered: "blockRendered" }, providers: [
|
|
2526
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.7", type: BlokEditorComponent, isStandalone: true, selector: "blok-editor", inputs: { onBeforeRender: "onBeforeRender", onBeforePaste: "onBeforePaste", onError: "onError", readOnly: "readOnly", hideToolbar: "hideToolbar", inlineToolbar: "inlineToolbar", theme: "theme", width: "width", placeholder: "placeholder", styleTokens: "styleTokens", i18n: "i18n", autofocus: "autofocus", data: "data", tools: "tools", config: "config", recreateKey: "recreateKey" }, outputs: { ready: "ready", dataChange: "dataChange", save: "save", change: "change", afterRender: "afterRender", themeChange: "themeChange", blocksRendered: "blocksRendered", blockRendered: "blockRendered" }, providers: [
|
|
2401
2527
|
{
|
|
2402
2528
|
provide: NG_VALUE_ACCESSOR,
|
|
2403
2529
|
useExisting: forwardRef(() => BlokEditorComponent),
|
|
@@ -2445,8 +2571,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
2445
2571
|
type: Input
|
|
2446
2572
|
}], onBeforePaste: [{
|
|
2447
2573
|
type: Input
|
|
2574
|
+
}], onError: [{
|
|
2575
|
+
type: Input
|
|
2448
2576
|
}], readOnly: [{
|
|
2449
2577
|
type: Input
|
|
2578
|
+
}], hideToolbar: [{
|
|
2579
|
+
type: Input
|
|
2580
|
+
}], inlineToolbar: [{
|
|
2581
|
+
type: Input
|
|
2450
2582
|
}], theme: [{
|
|
2451
2583
|
type: Input
|
|
2452
2584
|
}], width: [{
|
|
@@ -2455,6 +2587,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
2455
2587
|
type: Input
|
|
2456
2588
|
}], styleTokens: [{
|
|
2457
2589
|
type: Input
|
|
2590
|
+
}], i18n: [{
|
|
2591
|
+
type: Input
|
|
2458
2592
|
}], autofocus: [{
|
|
2459
2593
|
type: Input
|
|
2460
2594
|
}], data: [{
|
|
@@ -2786,9 +2920,58 @@ function injectBlocks(editor) {
|
|
|
2786
2920
|
};
|
|
2787
2921
|
}
|
|
2788
2922
|
|
|
2923
|
+
// packages/angular/src/useBlokReady.ts
|
|
2924
|
+
/**
|
|
2925
|
+
* Resolves the caller's scope description to an element, or null when it is
|
|
2926
|
+
* not available yet.
|
|
2927
|
+
* @param within - the scope description passed in options
|
|
2928
|
+
*/
|
|
2929
|
+
function resolveScope(within) {
|
|
2930
|
+
const value = typeof within === 'function' ? within() : within;
|
|
2931
|
+
if (value === null || value === undefined) {
|
|
2932
|
+
return null;
|
|
2933
|
+
}
|
|
2934
|
+
return value instanceof ElementRef ? value.nativeElement : value;
|
|
2935
|
+
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Angular factory exposing the live readiness of the Blok editors in a DOM
|
|
2938
|
+
* scope as a boolean signal.
|
|
2939
|
+
*
|
|
2940
|
+
* The readiness logic itself is framework-agnostic and lives in core
|
|
2941
|
+
* (`Blok.readyState` / `Blok.subscribeReady`) — the SAME implementation behind
|
|
2942
|
+
* React's and Vue's `useBlokReady`, so the three adapters cannot drift. This
|
|
2943
|
+
* wrapper supplies only Angular's reactivity: a signal re-read on every
|
|
2944
|
+
* registry change, unsubscribed through the injector's `DestroyRef`.
|
|
2945
|
+
*
|
|
2946
|
+
* Call it in an injection context (component constructor / field initializer).
|
|
2947
|
+
* The signal starts `false` and takes its first real reading in
|
|
2948
|
+
* `afterNextRender` — browser-only by contract, and late enough for a
|
|
2949
|
+
* `@ViewChild` scope to exist. Over-waiting is safe; under-waiting is a bug.
|
|
2950
|
+
* @param options - scope and readiness depth
|
|
2951
|
+
*/
|
|
2952
|
+
function injectBlokReady(options = {}) {
|
|
2953
|
+
const { within, settleOn } = options;
|
|
2954
|
+
const ready = signal(false, /* @ts-ignore */
|
|
2955
|
+
...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
|
|
2956
|
+
const read = () => {
|
|
2957
|
+
const scope = within === undefined ? null : resolveScope(within);
|
|
2958
|
+
// A scope that was asked for but has not resolved yet is NOT the global
|
|
2959
|
+
// scope — reporting ready here would gate on the wrong set of editors.
|
|
2960
|
+
ready.set(within !== undefined && scope === null
|
|
2961
|
+
? false
|
|
2962
|
+
: Blok.readyState({
|
|
2963
|
+
within: scope,
|
|
2964
|
+
settleOn,
|
|
2965
|
+
}).ready);
|
|
2966
|
+
};
|
|
2967
|
+
inject(DestroyRef).onDestroy(Blok.subscribeReady(read));
|
|
2968
|
+
afterNextRender(read);
|
|
2969
|
+
return ready.asReadonly();
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2789
2972
|
/**
|
|
2790
2973
|
* Generated bundle index. Do not edit.
|
|
2791
2974
|
*/
|
|
2792
2975
|
|
|
2793
|
-
export { BLOK_BLOCK_CONTEXT, BLOK_DEFAULT_CONFIG, BlokContentDirective, BlokEditorComponent, createAngularBlock, injectBlocks, provideBlok };
|
|
2976
|
+
export { BLOK_BLOCK_CONTEXT, BLOK_DEFAULT_CONFIG, BlokContentDirective, BlokEditorComponent, createAngularBlock, injectBlocks, injectBlokReady, provideBlok };
|
|
2794
2977
|
//# sourceMappingURL=bloklabs-angular.mjs.map
|