@mk-kit/ui 0.34.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/LICENSE +21 -0
- package/README.md +115 -0
- package/block-editor/README.md +254 -0
- package/fesm2022/mk-kit-ui-block-editor.mjs +2158 -0
- package/fesm2022/mk-kit-ui-block-editor.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-button.mjs +81 -0
- package/fesm2022/mk-kit-ui-button.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-checkbox.mjs +136 -0
- package/fesm2022/mk-kit-ui-checkbox.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-chip.mjs +122 -0
- package/fesm2022/mk-kit-ui-chip.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-context-menu.mjs +144 -0
- package/fesm2022/mk-kit-ui-context-menu.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-core.mjs +1576 -0
- package/fesm2022/mk-kit-ui-core.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-data.mjs +6055 -0
- package/fesm2022/mk-kit-ui-data.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-datetime.mjs +3409 -0
- package/fesm2022/mk-kit-ui-datetime.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-directives.mjs +1779 -0
- package/fesm2022/mk-kit-ui-directives.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-dnd.mjs +1073 -0
- package/fesm2022/mk-kit-ui-dnd.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-feedback.mjs +2426 -0
- package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-forms.mjs +9208 -0
- package/fesm2022/mk-kit-ui-forms.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-icon.mjs +470 -0
- package/fesm2022/mk-kit-ui-icon.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-media.mjs +896 -0
- package/fesm2022/mk-kit-ui-media.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-navigation.mjs +2542 -0
- package/fesm2022/mk-kit-ui-navigation.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-rich-text.mjs +565 -0
- package/fesm2022/mk-kit-ui-rich-text.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-table.mjs +1378 -0
- package/fesm2022/mk-kit-ui-table.mjs.map +1 -0
- package/fesm2022/mk-kit-ui.mjs +32 -0
- package/fesm2022/mk-kit-ui.mjs.map +1 -0
- package/package.json +130 -0
- package/schematics/collection.json +10 -0
- package/schematics/ng-add/index.js +113 -0
- package/schematics/ng-add/schema.json +22 -0
- package/schematics/package.json +3 -0
- package/styles/mk-kit.css +750 -0
- package/types/mk-kit-ui-block-editor.d.ts +292 -0
- package/types/mk-kit-ui-button.d.ts +40 -0
- package/types/mk-kit-ui-checkbox.d.ts +62 -0
- package/types/mk-kit-ui-chip.d.ts +59 -0
- package/types/mk-kit-ui-context-menu.d.ts +57 -0
- package/types/mk-kit-ui-core.d.ts +1105 -0
- package/types/mk-kit-ui-data.d.ts +2580 -0
- package/types/mk-kit-ui-datetime.d.ts +1171 -0
- package/types/mk-kit-ui-directives.d.ts +807 -0
- package/types/mk-kit-ui-dnd.d.ts +423 -0
- package/types/mk-kit-ui-feedback.d.ts +1270 -0
- package/types/mk-kit-ui-forms.d.ts +3586 -0
- package/types/mk-kit-ui-icon.d.ts +108 -0
- package/types/mk-kit-ui-media.d.ts +549 -0
- package/types/mk-kit-ui-navigation.d.ts +1169 -0
- package/types/mk-kit-ui-rich-text.d.ts +187 -0
- package/types/mk-kit-ui-table.d.ts +739 -0
- package/types/mk-kit-ui.d.ts +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mateusz Kornaś
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @mk-kit/ui
|
|
2
|
+
|
|
3
|
+
**Themable, accessible Angular 22 component library for admin dashboards & UIs.**
|
|
4
|
+
|
|
5
|
+
Signals-first. WCAG 2.1 AA. Every pixel controlled by CSS variables. Light &
|
|
6
|
+
dark mode out of the box. Zero runtime dependencies beyond Angular.
|
|
7
|
+
|
|
8
|
+
> Like Angular Material — but leaner, admin-oriented, and re-themable by editing
|
|
9
|
+
> a handful of CSS custom properties.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ng add @mk-kit/ui
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`ng add` installs the package from npm and wires the theme stylesheet into
|
|
18
|
+
your `angular.json`. To do it by hand instead:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @mk-kit/ui
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Peer dependencies: `@angular/core`, `@angular/common`, `@angular/forms` (v22+).
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
**1. Import the theme stylesheet** once (e.g. in `angular.json` `styles` or your
|
|
29
|
+
global `styles.css`):
|
|
30
|
+
|
|
31
|
+
```css
|
|
32
|
+
@import '@mk-kit/ui/styles.css';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**2. Add the `mk-app` class** to your `<body>` (or a top-level wrapper) so the
|
|
36
|
+
background, text color, fonts and themed scrollbars apply:
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<body class="mk-app">
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**3. Use components** — everything is standalone, just import what you need:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { Component, inject } from '@angular/core';
|
|
46
|
+
import { MkButton, MkCard, MkThemeService } from '@mk-kit/ui';
|
|
47
|
+
|
|
48
|
+
@Component({
|
|
49
|
+
selector: 'app-root',
|
|
50
|
+
imports: [MkButton, MkCard],
|
|
51
|
+
template: `
|
|
52
|
+
<mk-card variant="elevated">
|
|
53
|
+
<button mkButton tone="primary" (click)="theme.toggle()">
|
|
54
|
+
Toggle theme
|
|
55
|
+
</button>
|
|
56
|
+
</mk-card>
|
|
57
|
+
`,
|
|
58
|
+
})
|
|
59
|
+
export class AppRoot {
|
|
60
|
+
protected readonly theme = inject(MkThemeService);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Theming
|
|
65
|
+
|
|
66
|
+
The entire look is driven by `--mk-*` custom properties defined on `:root`.
|
|
67
|
+
Override any of them — globally or scoped to a subtree — to re-brand instantly:
|
|
68
|
+
|
|
69
|
+
```css
|
|
70
|
+
:root {
|
|
71
|
+
--mk-primary: #7c3aed;
|
|
72
|
+
--mk-primary-hover: #6d28d9;
|
|
73
|
+
--mk-radius-md: 4px;
|
|
74
|
+
--mk-font-sans: 'Inter', system-ui, sans-serif;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Dark mode
|
|
79
|
+
|
|
80
|
+
Dark mode works with no JavaScript — it follows the OS `prefers-color-scheme`.
|
|
81
|
+
To let users choose explicitly, set `data-mk-theme` on `<html>` (`"light"` |
|
|
82
|
+
`"dark"`), or use the built-in `MkThemeService`:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const theme = inject(MkThemeService);
|
|
86
|
+
theme.setTheme('dark'); // force dark
|
|
87
|
+
theme.setTheme('system'); // follow the OS
|
|
88
|
+
theme.toggle(); // flip light/dark
|
|
89
|
+
theme.resolvedTheme(); // signal: 'light' | 'dark'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The service persists the choice to `localStorage` and is fully SSR-safe.
|
|
93
|
+
|
|
94
|
+
## What's inside
|
|
95
|
+
|
|
96
|
+
- **Forms** — Button, FormField, Input, Select, Checkbox, Radio, Switch, Slider
|
|
97
|
+
- **Data** — Table (sortable/sticky), Card, Badge, Tag, Chip, Avatar, List,
|
|
98
|
+
StatCard, ProgressBar, Spinner, Skeleton, Divider
|
|
99
|
+
- **Feedback** — Alert, Toast, Dialog (+confirm), Tooltip
|
|
100
|
+
- **Navigation & layout** — Tabs, Accordion, Breadcrumb, Pagination, Menu,
|
|
101
|
+
AppShell, NavList
|
|
102
|
+
|
|
103
|
+
All components: `OnPush`, signal inputs/outputs, keyboard-operable, screen-reader
|
|
104
|
+
labelled, `:focus-visible` rings, and `prefers-reduced-motion` aware.
|
|
105
|
+
|
|
106
|
+
## Accessibility
|
|
107
|
+
|
|
108
|
+
mk-kit targets **WCAG 2.1 AA**: semantic roles, complete `aria-*` wiring, focus
|
|
109
|
+
trapping for overlays, roving tabindex for composite widgets, live-region status
|
|
110
|
+
announcements, and color contrast that holds in both themes. Information is never
|
|
111
|
+
conveyed by color alone.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT © Mateusz Kornaś
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Block Editor (`@mk-kit/ui`)
|
|
2
|
+
|
|
3
|
+
A configurable, Gutenberg-style **block content editor** for authoring blog
|
|
4
|
+
posts and content pages, plus a read-only **renderer** for displaying the saved
|
|
5
|
+
document. Dependency-free: built on native `contenteditable` + the Selection API
|
|
6
|
+
(`document.execCommand` is used for inline formatting — deprecated but
|
|
7
|
+
universally supported and the only practical way to do inline formatting without
|
|
8
|
+
a heavyweight editor engine).
|
|
9
|
+
|
|
10
|
+
Everything is themed through `--mk-*` tokens (light/dark aware), keyboard
|
|
11
|
+
operable, and WCAG 2.1 AA minded.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {
|
|
15
|
+
MkBlockEditor,
|
|
16
|
+
MkBlockRenderer,
|
|
17
|
+
mkBlocksToHtml,
|
|
18
|
+
type MkBlockDocument,
|
|
19
|
+
} from '@mk-kit/ui'; // (barrel: block-editor.barrel.ts)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. The document model
|
|
25
|
+
|
|
26
|
+
The value is a small, 100% JSON-serialisable document — persist it as-is.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
interface MkBlock {
|
|
30
|
+
id: string; // stable unique id
|
|
31
|
+
type: string; // registry key: 'paragraph', 'columns', …
|
|
32
|
+
data: Record<string, any>; // per-block state, e.g. { html } or { src, alt }
|
|
33
|
+
children?: MkBlock[]; // nested blocks (layout/columns)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface MkBlockDocument {
|
|
37
|
+
version: number; // schema version (MK_BLOCK_DOCUMENT_VERSION)
|
|
38
|
+
blocks: MkBlock[]; // top-level blocks, in order
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Built-in block `data` shapes:
|
|
43
|
+
|
|
44
|
+
| type | `data` | `children` |
|
|
45
|
+
| ----------- | ---------------------------------------------------------------- | ------------------ |
|
|
46
|
+
| `paragraph` | `{ html }` | — |
|
|
47
|
+
| `heading` | `{ html, level: 1..4 }` | — |
|
|
48
|
+
| `list` | `{ ordered: boolean, items: string[] }` | — |
|
|
49
|
+
| `quote` | `{ html, citation }` | — |
|
|
50
|
+
| `code` | `{ code, language }` | — |
|
|
51
|
+
| `image` | `{ src, alt, caption, align, width }` | — |
|
|
52
|
+
| `embed` | `{ url, embedUrl, provider, aspectRatio }` | — |
|
|
53
|
+
| `button` | `{ label, href, tone, variant, align }` | — |
|
|
54
|
+
| `divider` | `{}` | — |
|
|
55
|
+
| `columns` | `{ count, ratio, gap, align, justify }` | `column[]` |
|
|
56
|
+
| `column` | `{}` | any block[] |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. Wiring the editor with `[(value)]`
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { Component, signal } from '@angular/core';
|
|
64
|
+
import { MkBlockEditor, mkEmptyDocument, type MkBlockDocument } from '@mk-kit/ui';
|
|
65
|
+
|
|
66
|
+
@Component({
|
|
67
|
+
selector: 'app-post-editor',
|
|
68
|
+
imports: [MkBlockEditor],
|
|
69
|
+
template: `<mk-block-editor [(value)]="doc" placeholder="Write your post…" />`,
|
|
70
|
+
})
|
|
71
|
+
export class PostEditor {
|
|
72
|
+
readonly doc = signal<MkBlockDocument>(mkEmptyDocument());
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Because the editor implements `ControlValueAccessor`, it also works with forms:
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<mk-block-editor [(ngModel)]="doc" />
|
|
80
|
+
<mk-block-editor [formControl]="control" />
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Editor inputs / outputs
|
|
84
|
+
|
|
85
|
+
| input | type | notes |
|
|
86
|
+
| ---------------- | ------------------------------- | ------------------------------------------------ |
|
|
87
|
+
| `value` | `model<MkBlockDocument>` | two-way document binding |
|
|
88
|
+
| `blocks` | `MkBlockDefinition[] \| null` | custom/extended palette (merged over defaults) |
|
|
89
|
+
| `placeholder` | `string` | empty text-block prompt |
|
|
90
|
+
| `readonly` | `boolean` | hides editing chrome |
|
|
91
|
+
| `disabled` | `boolean` | form-level disable |
|
|
92
|
+
| `uploadHandler` | `(f: File) => Promise<string>` | image upload (see §4) |
|
|
93
|
+
| `embedProviders` | `MkEmbedProvider[] \| null` | extra embed providers (see §5) |
|
|
94
|
+
| `ariaLabel` | `string` | region label |
|
|
95
|
+
| **output** | | |
|
|
96
|
+
| `change` | `MkBlockDocument` | fires on every edit (alongside the `value` model)|
|
|
97
|
+
|
|
98
|
+
### Keyboard
|
|
99
|
+
|
|
100
|
+
- **Enter** in a text block splits at the caret and starts a new paragraph.
|
|
101
|
+
- **Backspace** at the start of an empty block deletes it and focuses the previous.
|
|
102
|
+
- **Arrow Up/Down** at a block edge move the caret to the adjacent block.
|
|
103
|
+
- **Select text** to reveal the floating toolbar (Bold, Italic, Underline,
|
|
104
|
+
Strikethrough, Inline code, Link, Clear).
|
|
105
|
+
- The inserter is a combobox → listbox: type to filter, Arrow keys to move,
|
|
106
|
+
Enter to insert, Esc to close.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 3. Providing a custom block (registerBlockType-style)
|
|
111
|
+
|
|
112
|
+
A block is described by an `MkBlockDefinition`. Supply extras via the `blocks`
|
|
113
|
+
input or, app-wide, via the `MK_BLOCK_DEFINITIONS` multi-token.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { type MkBlockDefinition, MK_DEFAULT_BLOCKS, mkBlockId } from '@mk-kit/ui';
|
|
117
|
+
|
|
118
|
+
const calloutBlock: MkBlockDefinition = {
|
|
119
|
+
type: 'callout',
|
|
120
|
+
label: 'Callout',
|
|
121
|
+
icon: '💡',
|
|
122
|
+
group: 'Text',
|
|
123
|
+
description: 'A highlighted note.',
|
|
124
|
+
keywords: ['note', 'tip', 'info'],
|
|
125
|
+
create: () => ({ id: mkBlockId('callout'), type: 'callout', data: { html: '', tone: 'info' } }),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Per-editor:
|
|
129
|
+
@Component({
|
|
130
|
+
template: `<mk-block-editor [(value)]="doc" [blocks]="palette" />`,
|
|
131
|
+
})
|
|
132
|
+
class Editor {
|
|
133
|
+
readonly palette = [...MK_DEFAULT_BLOCKS, calloutBlock];
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
App-wide via the token (merged over `MK_DEFAULT_BLOCKS`, then the input wins):
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { MK_BLOCK_DEFINITIONS } from '@mk-kit/ui';
|
|
141
|
+
|
|
142
|
+
providers: [
|
|
143
|
+
{ provide: MK_BLOCK_DEFINITIONS, multi: true, useValue: [calloutBlock] },
|
|
144
|
+
];
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
> The editor renders a built-in edit UI per known `type`. A brand-new `type`
|
|
148
|
+
> appears in the inserter and serialises through your `create()` data, but the
|
|
149
|
+
> editor shows an "Unknown block" placeholder unless the type matches a built-in
|
|
150
|
+
> (extend the switch by contributing to the library, or model your custom block
|
|
151
|
+
> on an existing `data` shape such as `{ html }`).
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 4. Image upload handler
|
|
156
|
+
|
|
157
|
+
If an `uploadHandler` is provided (editor input **or** the
|
|
158
|
+
`MK_BLOCK_UPLOAD_HANDLER` token), it is called with the chosen `File` and must
|
|
159
|
+
resolve to a URL. Otherwise the image falls back to an inline `data:` URL via
|
|
160
|
+
`FileReader`.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { MK_BLOCK_UPLOAD_HANDLER } from '@mk-kit/ui';
|
|
164
|
+
|
|
165
|
+
async function uploadToCdn(file: File): Promise<string> {
|
|
166
|
+
const body = new FormData();
|
|
167
|
+
body.append('file', file);
|
|
168
|
+
const res = await fetch('/api/upload', { method: 'POST', body });
|
|
169
|
+
return (await res.json()).url;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Per editor:
|
|
173
|
+
// <mk-block-editor [(value)]="doc" [uploadHandler]="uploadToCdn" />
|
|
174
|
+
|
|
175
|
+
// Or app-wide:
|
|
176
|
+
providers: [{ provide: MK_BLOCK_UPLOAD_HANDLER, useValue: uploadToCdn }];
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The image block also accepts a pasted URL and drag-and-drop, and exposes alt
|
|
180
|
+
text, caption, alignment and width settings.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 5. Adding an embed provider
|
|
185
|
+
|
|
186
|
+
Embeds render a **sandboxed** iframe for allow-listed providers (YouTube and
|
|
187
|
+
Vimeo out of the box). Add your own:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { type MkEmbedProvider } from '@mk-kit/ui';
|
|
191
|
+
|
|
192
|
+
const codepen: MkEmbedProvider = {
|
|
193
|
+
name: 'CodePen',
|
|
194
|
+
test: /codepen\.io\/([\w-]+)\/pen\/([\w-]+)/i,
|
|
195
|
+
aspectRatio: 16 / 9,
|
|
196
|
+
toEmbedUrl(url) {
|
|
197
|
+
const m = url.match(/codepen\.io\/([\w-]+)\/pen\/([\w-]+)/i);
|
|
198
|
+
return m ? `https://codepen.io/${m[1]}/embed/${m[2]}` : null;
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// <mk-block-editor [(value)]="doc" [embedProviders]="[codepen]" />
|
|
203
|
+
// or app-wide: { provide: MK_BLOCK_EMBED_PROVIDERS, multi: true, useValue: [codepen] }
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Non-matching URLs render a safe link card instead of an iframe.
|
|
207
|
+
|
|
208
|
+
**Security model:** rich-text is stored as HTML but always sanitised — through
|
|
209
|
+
`sanitizeInlineHtml` (an allow-list cleaner) on serialise, and Angular's
|
|
210
|
+
`DomSanitizer` via `[innerHTML]` on render. The **only** value ever passed to
|
|
211
|
+
`bypassSecurityTrustResourceUrl` is a provider-transformed `embedUrl`, never a
|
|
212
|
+
raw pasted string.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 6. Rendering saved content
|
|
217
|
+
|
|
218
|
+
### Component (themed, interactive iframes)
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import { MkBlockRenderer } from '@mk-kit/ui';
|
|
222
|
+
|
|
223
|
+
@Component({
|
|
224
|
+
imports: [MkBlockRenderer],
|
|
225
|
+
template: `<mk-block-renderer [value]="doc()" />`,
|
|
226
|
+
})
|
|
227
|
+
class PublishedPost { /* doc() is a stored MkBlockDocument */ }
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`mk-block-renderer` re-uses the editor's layout CSS, so authored columns look
|
|
231
|
+
identical when published, and renders embeds as sandboxed iframes.
|
|
232
|
+
|
|
233
|
+
### Static HTML string (SSG/SSR, emails, feeds)
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
import { mkBlocksToHtml, mkBlocksToText } from '@mk-kit/ui';
|
|
237
|
+
|
|
238
|
+
const html = mkBlocksToHtml(doc); // clean, self-contained semantic HTML
|
|
239
|
+
const text = mkBlocksToText(doc); // plain text for excerpts / meta descriptions
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Serializer signatures:
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
function mkBlocksToHtml(doc: MkBlockDocument | null | undefined): string;
|
|
246
|
+
function mkBlocksToText(doc: MkBlockDocument | null | undefined): string;
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`mkBlocksToHtml` emits layout blocks as responsive grid wrappers with inline
|
|
250
|
+
styles, so the output is portable to any page. It is dependency-free and safe to
|
|
251
|
+
run on a server. Because it returns a string, always let a sanitiser (or the
|
|
252
|
+
`mk-block-renderer`) handle final display — never feed it back into `innerHTML`
|
|
253
|
+
without sanitising again.
|
|
254
|
+
```
|