@bloklabs/angular 1.2.6 → 1.4.0
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 +235 -27
- package/fesm2022/bloklabs-angular.mjs.map +1 -1
- package/index.d.ts +1 -675
- package/package.json +26 -5
- package/types/bloklabs-angular.d.ts +752 -0
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.
|
|
@@ -182,7 +182,8 @@ class BlokContentDirective {
|
|
|
182
182
|
/** Emits the live Blok instance once it is ready, after `instance` is populated. */
|
|
183
183
|
ready = new EventEmitter();
|
|
184
184
|
/** The live Blok instance, or null before `isReady` resolves / after destroy. */
|
|
185
|
-
instance = signal(null,
|
|
185
|
+
instance = signal(null, /* @ts-ignore */
|
|
186
|
+
...(ngDevMode ? [{ debugName: "instance" }] : /* istanbul ignore next */ []));
|
|
186
187
|
/** The editor created by the most recent construction; used as the staleness key. */
|
|
187
188
|
current = null;
|
|
188
189
|
destroyed = false;
|
|
@@ -250,10 +251,10 @@ class BlokContentDirective {
|
|
|
250
251
|
this.registry = undefined;
|
|
251
252
|
this.instance.set(null);
|
|
252
253
|
}
|
|
253
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
254
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "
|
|
254
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: BlokContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
255
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: BlokContentDirective, isStandalone: true, selector: "[blokContent]", inputs: { config: "config", recreateKey: "recreateKey" }, outputs: { ready: "ready" }, ngImport: i0 });
|
|
255
256
|
}
|
|
256
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
257
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: BlokContentDirective, decorators: [{
|
|
257
258
|
type: Directive,
|
|
258
259
|
args: [{
|
|
259
260
|
selector: '[blokContent]',
|
|
@@ -1753,9 +1754,15 @@ const DATA_ATTR = {
|
|
|
1753
1754
|
/** Editor or element is empty */
|
|
1754
1755
|
empty: 'data-blok-empty',
|
|
1755
1756
|
/** Present on the editor wrapper while read-only mode is active.
|
|
1756
|
-
* Public styling hook — lets hosts
|
|
1757
|
-
*
|
|
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. */
|
|
1758
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',
|
|
1759
1766
|
/** Present on the editor wrapper when config.hideToolbar is true.
|
|
1760
1767
|
* Public styling hook — drives the gutter auto-collapse (the gutter
|
|
1761
1768
|
* exists solely to house the toolbar's +/⠿ controls). */
|
|
@@ -2066,8 +2073,16 @@ const mountChildBlocks = (container, children) => {
|
|
|
2066
2073
|
* `BlokEditor`). Delegates instance lifecycle to an internal `BlokContentDirective`
|
|
2067
2074
|
* and layers the typed reactive input/output API on top.
|
|
2068
2075
|
*
|
|
2069
|
-
* Reactive inputs (`readOnly`, `
|
|
2070
|
-
*
|
|
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.
|
|
2071
2086
|
*
|
|
2072
2087
|
* Implementation note: classic `@Input()`/`@ViewChild()` decorators are used
|
|
2073
2088
|
* (not signal `input()`/`viewChild()`) for JIT compatibility — see
|
|
@@ -2080,7 +2095,8 @@ class BlokEditorComponent {
|
|
|
2080
2095
|
defaults = inject(BLOK_DEFAULT_CONFIG, { optional: true }) ?? {};
|
|
2081
2096
|
contentQuery;
|
|
2082
2097
|
/** Signal bridge for the @ViewChild directive so effects react to its arrival. */
|
|
2083
|
-
content = signal(null,
|
|
2098
|
+
content = signal(null, /* @ts-ignore */
|
|
2099
|
+
...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
|
|
2084
2100
|
/**
|
|
2085
2101
|
* Output half of two-way `[(data)]`. Emits the editor's serialized content on
|
|
2086
2102
|
* every change. Wiring the core `onSave` callback is gated on this being
|
|
@@ -2115,28 +2131,69 @@ class BlokEditorComponent {
|
|
|
2115
2131
|
// ---- Reactive inputs (signal-backed setters; synced in place, never recreate) ----
|
|
2116
2132
|
// Default undefined (not false) so an unset input falls back to a provideBlok
|
|
2117
2133
|
// default; the readOnly effect / buildConfig coerce undefined to false.
|
|
2118
|
-
|
|
2134
|
+
// Accepts the object form (`{ hideControls }`) too — it flows through
|
|
2135
|
+
// buildConfig unchanged and the effect expands it to
|
|
2136
|
+
// `readOnly.set(enabled, { hideControls })`.
|
|
2137
|
+
readOnly$ = signal(undefined, /* @ts-ignore */
|
|
2138
|
+
...(ngDevMode ? [{ debugName: "readOnly$" }] : /* istanbul ignore next */ []));
|
|
2119
2139
|
set readOnly(value) {
|
|
2120
2140
|
this.readOnly$.set(value);
|
|
2121
2141
|
}
|
|
2122
|
-
|
|
2142
|
+
hideToolbar$ = signal(undefined, /* @ts-ignore */
|
|
2143
|
+
...(ngDevMode ? [{ debugName: "hideToolbar$" }] : /* istanbul ignore next */ []));
|
|
2144
|
+
set hideToolbar(value) {
|
|
2145
|
+
this.hideToolbar$.set(value);
|
|
2146
|
+
}
|
|
2147
|
+
inlineToolbar$ = signal(undefined, /* @ts-ignore */
|
|
2148
|
+
...(ngDevMode ? [{ debugName: "inlineToolbar$" }] : /* istanbul ignore next */ []));
|
|
2149
|
+
set inlineToolbar(value) {
|
|
2150
|
+
this.inlineToolbar$.set(value);
|
|
2151
|
+
}
|
|
2152
|
+
theme$ = signal(undefined, /* @ts-ignore */
|
|
2153
|
+
...(ngDevMode ? [{ debugName: "theme$" }] : /* istanbul ignore next */ []));
|
|
2123
2154
|
set theme(value) {
|
|
2124
2155
|
this.theme$.set(value);
|
|
2125
2156
|
}
|
|
2126
|
-
width$ = signal(undefined,
|
|
2157
|
+
width$ = signal(undefined, /* @ts-ignore */
|
|
2158
|
+
...(ngDevMode ? [{ debugName: "width$" }] : /* istanbul ignore next */ []));
|
|
2127
2159
|
set width(value) {
|
|
2128
2160
|
this.width$.set(value);
|
|
2129
2161
|
}
|
|
2130
|
-
placeholder$ = signal(undefined,
|
|
2162
|
+
placeholder$ = signal(undefined, /* @ts-ignore */
|
|
2163
|
+
...(ngDevMode ? [{ debugName: "placeholder$" }] : /* istanbul ignore next */ []));
|
|
2131
2164
|
set placeholder(value) {
|
|
2132
2165
|
this.placeholder$.set(value);
|
|
2133
2166
|
}
|
|
2134
|
-
|
|
2167
|
+
/**
|
|
2168
|
+
* Theme tokens. Construction-only config forced hosts with a live light/dark
|
|
2169
|
+
* toggle to recreate the editor or hand-write the global stylesheet Blok
|
|
2170
|
+
* already injects; this drives the runtime `tokens` API instead.
|
|
2171
|
+
*/
|
|
2172
|
+
styleTokens$ = signal(undefined, /* @ts-ignore */
|
|
2173
|
+
...(ngDevMode ? [{ debugName: "styleTokens$" }] : /* istanbul ignore next */ []));
|
|
2174
|
+
set styleTokens(value) {
|
|
2175
|
+
this.styleTokens$.set(value);
|
|
2176
|
+
}
|
|
2177
|
+
/**
|
|
2178
|
+
* Locale, host message overrides and text direction. `config.i18n` was
|
|
2179
|
+
* consumed once at boot, so a host driving a language switcher had to
|
|
2180
|
+
* recreate the editor (losing caret, focus and undo stack) to relabel the
|
|
2181
|
+
* UI; this drives the runtime `i18n.update` API instead. `defaultLocale` is
|
|
2182
|
+
* not forwarded — it only affects the INITIAL locale resolution.
|
|
2183
|
+
*/
|
|
2184
|
+
i18n$ = signal(undefined, /* @ts-ignore */
|
|
2185
|
+
...(ngDevMode ? [{ debugName: "i18n$" }] : /* istanbul ignore next */ []));
|
|
2186
|
+
set i18n(value) {
|
|
2187
|
+
this.i18n$.set(value);
|
|
2188
|
+
}
|
|
2189
|
+
autofocus$ = signal(false, /* @ts-ignore */
|
|
2190
|
+
...(ngDevMode ? [{ debugName: "autofocus$" }] : /* istanbul ignore next */ []));
|
|
2135
2191
|
set autofocus(value) {
|
|
2136
2192
|
this.autofocus$.set(value);
|
|
2137
2193
|
}
|
|
2138
2194
|
// ---- Reactive content (seeds construction, then re-renders on change) ----
|
|
2139
|
-
data$ = signal(undefined,
|
|
2195
|
+
data$ = signal(undefined, /* @ts-ignore */
|
|
2196
|
+
...(ngDevMode ? [{ debugName: "data$" }] : /* istanbul ignore next */ []));
|
|
2140
2197
|
set data(value) {
|
|
2141
2198
|
this.data$.set(value);
|
|
2142
2199
|
}
|
|
@@ -2144,7 +2201,7 @@ class BlokEditorComponent {
|
|
|
2144
2201
|
tools;
|
|
2145
2202
|
/**
|
|
2146
2203
|
* Escape hatch: a full config object for keys without a dedicated input
|
|
2147
|
-
* (
|
|
2204
|
+
* (sanitizer, minHeight, …). Layered between provideBlok
|
|
2148
2205
|
* defaults and the discrete inputs.
|
|
2149
2206
|
*/
|
|
2150
2207
|
config;
|
|
@@ -2156,6 +2213,14 @@ class BlokEditorComponent {
|
|
|
2156
2213
|
* no-op, so it won't clobber the caret. Renders are serialized via `renderChain`.
|
|
2157
2214
|
*/
|
|
2158
2215
|
lastRenderedData;
|
|
2216
|
+
/** Last token set pushed through `tokens.set`, for deep-equal deduping. */
|
|
2217
|
+
appliedTokens;
|
|
2218
|
+
/** Last i18n config pushed through `i18n.update`, for deep-equal deduping. */
|
|
2219
|
+
appliedI18n;
|
|
2220
|
+
/** Editor instance the i18n baseline above belongs to. */
|
|
2221
|
+
i18nAppliedFor = null;
|
|
2222
|
+
/** Last value pushed through `tools.setInlineToolbar`, for content-compare deduping. */
|
|
2223
|
+
appliedInlineToolbar;
|
|
2159
2224
|
seededEditor = null;
|
|
2160
2225
|
renderChain = Promise.resolve();
|
|
2161
2226
|
/** Registered by `ControlValueAccessor.registerOnChange` (Angular forms). */
|
|
@@ -2178,7 +2243,8 @@ class BlokEditorComponent {
|
|
|
2178
2243
|
});
|
|
2179
2244
|
};
|
|
2180
2245
|
/** Live Blok instance, or null until `isReady` resolves / after destroy. */
|
|
2181
|
-
instance = computed(() => this.content()?.instance() ?? null,
|
|
2246
|
+
instance = computed(() => this.content()?.instance() ?? null, /* @ts-ignore */
|
|
2247
|
+
...(ngDevMode ? [{ debugName: "instance" }] : /* istanbul ignore next */ []));
|
|
2182
2248
|
/** Guards `ready` to one emission per editor instance. */
|
|
2183
2249
|
lastReadyInstance = null;
|
|
2184
2250
|
/**
|
|
@@ -2189,7 +2255,17 @@ class BlokEditorComponent {
|
|
|
2189
2255
|
buildConfig() {
|
|
2190
2256
|
// Precedence: provideBlok defaults < [config] escape hatch < discrete inputs.
|
|
2191
2257
|
const cfg = { ...this.defaults, ...this.config };
|
|
2258
|
+
// The object form ({ hideControls }) is preserved as-is; the readOnly
|
|
2259
|
+
// effect expands it into `readOnly.set(enabled, { hideControls })`.
|
|
2192
2260
|
cfg.readOnly = this.readOnly$() ?? this.config?.readOnly ?? this.defaults.readOnly ?? false;
|
|
2261
|
+
const hideToolbar = this.hideToolbar$();
|
|
2262
|
+
if (hideToolbar !== undefined) {
|
|
2263
|
+
cfg.hideToolbar = hideToolbar;
|
|
2264
|
+
}
|
|
2265
|
+
const inlineToolbar = this.inlineToolbar$();
|
|
2266
|
+
if (inlineToolbar !== undefined) {
|
|
2267
|
+
cfg.inlineToolbar = inlineToolbar;
|
|
2268
|
+
}
|
|
2193
2269
|
// tools registries are MERGED (not replaced) across all three layers so a
|
|
2194
2270
|
// shared registry composes with per-instance additions.
|
|
2195
2271
|
if (this.tools !== undefined ||
|
|
@@ -2213,6 +2289,12 @@ class BlokEditorComponent {
|
|
|
2213
2289
|
if (placeholder !== undefined) {
|
|
2214
2290
|
cfg.placeholder = placeholder;
|
|
2215
2291
|
}
|
|
2292
|
+
// Seeded at construction (not applied by the i18n effect) so the locale is
|
|
2293
|
+
// resolved during boot instead of flashing the default language first.
|
|
2294
|
+
const i18n = this.i18n$();
|
|
2295
|
+
if (i18n !== undefined) {
|
|
2296
|
+
cfg.i18n = i18n;
|
|
2297
|
+
}
|
|
2216
2298
|
// Opt-in: only attach each core callback when the consumer actually consumes
|
|
2217
2299
|
// it (an observed output, a registered forms hook, or a provided transform).
|
|
2218
2300
|
// Their mere presence makes the core serialize / run hooks on every change.
|
|
@@ -2261,11 +2343,39 @@ class BlokEditorComponent {
|
|
|
2261
2343
|
// (the Angular analog of React's `editor` effect-dependency).
|
|
2262
2344
|
effect(() => {
|
|
2263
2345
|
const editor = this.instance();
|
|
2264
|
-
const readOnly = this.readOnly$();
|
|
2346
|
+
const readOnly = this.readOnly$() ?? this.config?.readOnly;
|
|
2265
2347
|
if (editor) {
|
|
2266
|
-
|
|
2348
|
+
const { enabled, hideControls } = normalizeReadOnlyConfig(readOnly);
|
|
2349
|
+
// Only the object form carries options; the boolean form keeps the
|
|
2350
|
+
// one-argument call so it never clobbers a config-seeded hideControls.
|
|
2351
|
+
if (typeof readOnly === 'object' && readOnly !== null) {
|
|
2352
|
+
void editor.readOnly.set(enabled, { hideControls });
|
|
2353
|
+
}
|
|
2354
|
+
else {
|
|
2355
|
+
void editor.readOnly.set(enabled);
|
|
2356
|
+
}
|
|
2267
2357
|
}
|
|
2268
2358
|
});
|
|
2359
|
+
effect(() => {
|
|
2360
|
+
const editor = this.instance();
|
|
2361
|
+
const hideToolbar = this.hideToolbar$();
|
|
2362
|
+
if (editor && hideToolbar !== undefined) {
|
|
2363
|
+
editor.toolbar.setHidden(hideToolbar);
|
|
2364
|
+
}
|
|
2365
|
+
});
|
|
2366
|
+
// Content-compared (not identity): a new array with the same tool names is
|
|
2367
|
+
// a no-op, so template-inline array literals don't thrash the assignment.
|
|
2368
|
+
effect(() => {
|
|
2369
|
+
const editor = this.instance();
|
|
2370
|
+
const inlineToolbar = this.inlineToolbar$();
|
|
2371
|
+
if (!editor ||
|
|
2372
|
+
inlineToolbar === undefined ||
|
|
2373
|
+
deepEqual(inlineToolbar, this.appliedInlineToolbar)) {
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
this.appliedInlineToolbar = Array.isArray(inlineToolbar) ? [...inlineToolbar] : inlineToolbar;
|
|
2377
|
+
editor.tools.setInlineToolbar(inlineToolbar);
|
|
2378
|
+
});
|
|
2269
2379
|
effect(() => {
|
|
2270
2380
|
const editor = this.instance();
|
|
2271
2381
|
const theme = this.theme$();
|
|
@@ -2287,6 +2397,43 @@ class BlokEditorComponent {
|
|
|
2287
2397
|
editor.placeholder.set(placeholder);
|
|
2288
2398
|
}
|
|
2289
2399
|
});
|
|
2400
|
+
effect(() => {
|
|
2401
|
+
const editor = this.instance();
|
|
2402
|
+
const tokens = this.styleTokens$() ?? this.config?.style?.tokens;
|
|
2403
|
+
if (!editor || tokens === undefined || deepEqual(tokens, this.appliedTokens)) {
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
this.appliedTokens = { ...tokens };
|
|
2407
|
+
editor.tokens.set(tokens);
|
|
2408
|
+
});
|
|
2409
|
+
effect(() => {
|
|
2410
|
+
const editor = this.instance();
|
|
2411
|
+
const i18n = this.i18n$() ?? this.config?.i18n;
|
|
2412
|
+
if (!editor) {
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
/*
|
|
2416
|
+
* A freshly constructed editor already resolved this config during its
|
|
2417
|
+
* own boot (buildConfig seeds it), so record the baseline and push
|
|
2418
|
+
* nothing — re-applying it here would reload the locale after mount and
|
|
2419
|
+
* flash the default language.
|
|
2420
|
+
*/
|
|
2421
|
+
if (this.i18nAppliedFor !== editor) {
|
|
2422
|
+
this.i18nAppliedFor = editor;
|
|
2423
|
+
this.appliedI18n = i18n === undefined ? undefined : { ...i18n };
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
if (i18n === undefined || deepEqual(i18n, this.appliedI18n)) {
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
this.appliedI18n = { ...i18n };
|
|
2430
|
+
const { locale, messages, direction } = i18n;
|
|
2431
|
+
void editor.i18n.update({
|
|
2432
|
+
...(locale === undefined ? {} : { locale }),
|
|
2433
|
+
...(messages === undefined ? {} : { messages }),
|
|
2434
|
+
...(direction === undefined ? {} : { direction }),
|
|
2435
|
+
});
|
|
2436
|
+
});
|
|
2290
2437
|
effect(() => {
|
|
2291
2438
|
const editor = this.instance();
|
|
2292
2439
|
if (editor && this.autofocus$()) {
|
|
@@ -2366,8 +2513,8 @@ class BlokEditorComponent {
|
|
|
2366
2513
|
setDisabledState(isDisabled) {
|
|
2367
2514
|
this.readOnly$.set(isDisabled);
|
|
2368
2515
|
}
|
|
2369
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2370
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
|
|
2516
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: BlokEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2517
|
+
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", 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: [
|
|
2371
2518
|
{
|
|
2372
2519
|
provide: NG_VALUE_ACCESSOR,
|
|
2373
2520
|
useExisting: forwardRef(() => BlokEditorComponent),
|
|
@@ -2375,7 +2522,7 @@ class BlokEditorComponent {
|
|
|
2375
2522
|
},
|
|
2376
2523
|
], viewQueries: [{ propertyName: "contentQuery", first: true, predicate: BlokContentDirective, descendants: true }], exportAs: ["blok"], ngImport: i0, template: `<div blokContent [config]="buildConfig()" [recreateKey]="recreateKey"></div>`, isInline: true, dependencies: [{ kind: "directive", type: BlokContentDirective, selector: "[blokContent]", inputs: ["config", "recreateKey"], outputs: ["ready"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2377
2524
|
}
|
|
2378
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2525
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: BlokEditorComponent, decorators: [{
|
|
2379
2526
|
type: Component,
|
|
2380
2527
|
args: [{
|
|
2381
2528
|
selector: 'blok-editor',
|
|
@@ -2417,12 +2564,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
2417
2564
|
type: Input
|
|
2418
2565
|
}], readOnly: [{
|
|
2419
2566
|
type: Input
|
|
2567
|
+
}], hideToolbar: [{
|
|
2568
|
+
type: Input
|
|
2569
|
+
}], inlineToolbar: [{
|
|
2570
|
+
type: Input
|
|
2420
2571
|
}], theme: [{
|
|
2421
2572
|
type: Input
|
|
2422
2573
|
}], width: [{
|
|
2423
2574
|
type: Input
|
|
2424
2575
|
}], placeholder: [{
|
|
2425
2576
|
type: Input
|
|
2577
|
+
}], styleTokens: [{
|
|
2578
|
+
type: Input
|
|
2579
|
+
}], i18n: [{
|
|
2580
|
+
type: Input
|
|
2426
2581
|
}], autofocus: [{
|
|
2427
2582
|
type: Input
|
|
2428
2583
|
}], data: [{
|
|
@@ -2492,8 +2647,10 @@ function createAngularBlock(spec) {
|
|
|
2492
2647
|
this.pointerDrag = () => api?.blocks?.isPointerDragActive === true;
|
|
2493
2648
|
this.mirror = fillDefaults(spec.propSchema, (options.data ?? {}));
|
|
2494
2649
|
this.lastRendered = this.mirror;
|
|
2495
|
-
this.dataSig = signal(this.mirror,
|
|
2496
|
-
|
|
2650
|
+
this.dataSig = signal(this.mirror, /* @ts-ignore */
|
|
2651
|
+
...(ngDevMode ? [{ debugName: "dataSig" }] : /* istanbul ignore next */ []));
|
|
2652
|
+
this.readOnlySig = signal(options.readOnly, /* @ts-ignore */
|
|
2653
|
+
...(ngDevMode ? [{ debugName: "readOnlySig" }] : /* istanbul ignore next */ []));
|
|
2497
2654
|
this.ctx = {
|
|
2498
2655
|
data: this.dataSig.asReadonly(),
|
|
2499
2656
|
commit: this.commit,
|
|
@@ -2662,13 +2819,15 @@ const BLOCK_CHANGED_EVENT = 'block changed';
|
|
|
2662
2819
|
function injectBlocks(editor) {
|
|
2663
2820
|
// Bumped on every `block changed`; the shared API's read methods touch it (via
|
|
2664
2821
|
// the onRead seam) so template/computed reads stay reactive.
|
|
2665
|
-
const version = signal(0,
|
|
2822
|
+
const version = signal(0, /* @ts-ignore */
|
|
2823
|
+
...(ngDevMode ? [{ debugName: "version" }] : /* istanbul ignore next */ []));
|
|
2666
2824
|
const touch = () => {
|
|
2667
2825
|
version();
|
|
2668
2826
|
};
|
|
2669
2827
|
// The API bound to the CURRENT editor; EMPTY_API while null. A signal so the
|
|
2670
2828
|
// facade's reads track the editor-identity swap.
|
|
2671
|
-
const bound = signal(EMPTY_API,
|
|
2829
|
+
const bound = signal(EMPTY_API, /* @ts-ignore */
|
|
2830
|
+
...(ngDevMode ? [{ debugName: "bound" }] : /* istanbul ignore next */ []));
|
|
2672
2831
|
// One subscription record (no `let` reassignment), re-bound on editor change.
|
|
2673
2832
|
const sub = { editor: null, handler: null };
|
|
2674
2833
|
const unsubscribe = () => {
|
|
@@ -2750,9 +2909,58 @@ function injectBlocks(editor) {
|
|
|
2750
2909
|
};
|
|
2751
2910
|
}
|
|
2752
2911
|
|
|
2912
|
+
// packages/angular/src/useBlokReady.ts
|
|
2913
|
+
/**
|
|
2914
|
+
* Resolves the caller's scope description to an element, or null when it is
|
|
2915
|
+
* not available yet.
|
|
2916
|
+
* @param within - the scope description passed in options
|
|
2917
|
+
*/
|
|
2918
|
+
function resolveScope(within) {
|
|
2919
|
+
const value = typeof within === 'function' ? within() : within;
|
|
2920
|
+
if (value === null || value === undefined) {
|
|
2921
|
+
return null;
|
|
2922
|
+
}
|
|
2923
|
+
return value instanceof ElementRef ? value.nativeElement : value;
|
|
2924
|
+
}
|
|
2925
|
+
/**
|
|
2926
|
+
* Angular factory exposing the live readiness of the Blok editors in a DOM
|
|
2927
|
+
* scope as a boolean signal.
|
|
2928
|
+
*
|
|
2929
|
+
* The readiness logic itself is framework-agnostic and lives in core
|
|
2930
|
+
* (`Blok.readyState` / `Blok.subscribeReady`) — the SAME implementation behind
|
|
2931
|
+
* React's and Vue's `useBlokReady`, so the three adapters cannot drift. This
|
|
2932
|
+
* wrapper supplies only Angular's reactivity: a signal re-read on every
|
|
2933
|
+
* registry change, unsubscribed through the injector's `DestroyRef`.
|
|
2934
|
+
*
|
|
2935
|
+
* Call it in an injection context (component constructor / field initializer).
|
|
2936
|
+
* The signal starts `false` and takes its first real reading in
|
|
2937
|
+
* `afterNextRender` — browser-only by contract, and late enough for a
|
|
2938
|
+
* `@ViewChild` scope to exist. Over-waiting is safe; under-waiting is a bug.
|
|
2939
|
+
* @param options - scope and readiness depth
|
|
2940
|
+
*/
|
|
2941
|
+
function injectBlokReady(options = {}) {
|
|
2942
|
+
const { within, settleOn } = options;
|
|
2943
|
+
const ready = signal(false, /* @ts-ignore */
|
|
2944
|
+
...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
|
|
2945
|
+
const read = () => {
|
|
2946
|
+
const scope = within === undefined ? null : resolveScope(within);
|
|
2947
|
+
// A scope that was asked for but has not resolved yet is NOT the global
|
|
2948
|
+
// scope — reporting ready here would gate on the wrong set of editors.
|
|
2949
|
+
ready.set(within !== undefined && scope === null
|
|
2950
|
+
? false
|
|
2951
|
+
: Blok.readyState({
|
|
2952
|
+
within: scope,
|
|
2953
|
+
settleOn,
|
|
2954
|
+
}).ready);
|
|
2955
|
+
};
|
|
2956
|
+
inject(DestroyRef).onDestroy(Blok.subscribeReady(read));
|
|
2957
|
+
afterNextRender(read);
|
|
2958
|
+
return ready.asReadonly();
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2753
2961
|
/**
|
|
2754
2962
|
* Generated bundle index. Do not edit.
|
|
2755
2963
|
*/
|
|
2756
2964
|
|
|
2757
|
-
export { BLOK_BLOCK_CONTEXT, BLOK_DEFAULT_CONFIG, BlokContentDirective, BlokEditorComponent, createAngularBlock, injectBlocks, provideBlok };
|
|
2965
|
+
export { BLOK_BLOCK_CONTEXT, BLOK_DEFAULT_CONFIG, BlokContentDirective, BlokEditorComponent, createAngularBlock, injectBlocks, injectBlokReady, provideBlok };
|
|
2758
2966
|
//# sourceMappingURL=bloklabs-angular.mjs.map
|