@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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/block-editor/README.md +254 -0
  4. package/fesm2022/mk-kit-ui-block-editor.mjs +2158 -0
  5. package/fesm2022/mk-kit-ui-block-editor.mjs.map +1 -0
  6. package/fesm2022/mk-kit-ui-button.mjs +81 -0
  7. package/fesm2022/mk-kit-ui-button.mjs.map +1 -0
  8. package/fesm2022/mk-kit-ui-checkbox.mjs +136 -0
  9. package/fesm2022/mk-kit-ui-checkbox.mjs.map +1 -0
  10. package/fesm2022/mk-kit-ui-chip.mjs +122 -0
  11. package/fesm2022/mk-kit-ui-chip.mjs.map +1 -0
  12. package/fesm2022/mk-kit-ui-context-menu.mjs +144 -0
  13. package/fesm2022/mk-kit-ui-context-menu.mjs.map +1 -0
  14. package/fesm2022/mk-kit-ui-core.mjs +1576 -0
  15. package/fesm2022/mk-kit-ui-core.mjs.map +1 -0
  16. package/fesm2022/mk-kit-ui-data.mjs +6055 -0
  17. package/fesm2022/mk-kit-ui-data.mjs.map +1 -0
  18. package/fesm2022/mk-kit-ui-datetime.mjs +3409 -0
  19. package/fesm2022/mk-kit-ui-datetime.mjs.map +1 -0
  20. package/fesm2022/mk-kit-ui-directives.mjs +1779 -0
  21. package/fesm2022/mk-kit-ui-directives.mjs.map +1 -0
  22. package/fesm2022/mk-kit-ui-dnd.mjs +1073 -0
  23. package/fesm2022/mk-kit-ui-dnd.mjs.map +1 -0
  24. package/fesm2022/mk-kit-ui-feedback.mjs +2426 -0
  25. package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -0
  26. package/fesm2022/mk-kit-ui-forms.mjs +9208 -0
  27. package/fesm2022/mk-kit-ui-forms.mjs.map +1 -0
  28. package/fesm2022/mk-kit-ui-icon.mjs +470 -0
  29. package/fesm2022/mk-kit-ui-icon.mjs.map +1 -0
  30. package/fesm2022/mk-kit-ui-media.mjs +896 -0
  31. package/fesm2022/mk-kit-ui-media.mjs.map +1 -0
  32. package/fesm2022/mk-kit-ui-navigation.mjs +2542 -0
  33. package/fesm2022/mk-kit-ui-navigation.mjs.map +1 -0
  34. package/fesm2022/mk-kit-ui-rich-text.mjs +565 -0
  35. package/fesm2022/mk-kit-ui-rich-text.mjs.map +1 -0
  36. package/fesm2022/mk-kit-ui-table.mjs +1378 -0
  37. package/fesm2022/mk-kit-ui-table.mjs.map +1 -0
  38. package/fesm2022/mk-kit-ui.mjs +32 -0
  39. package/fesm2022/mk-kit-ui.mjs.map +1 -0
  40. package/package.json +130 -0
  41. package/schematics/collection.json +10 -0
  42. package/schematics/ng-add/index.js +113 -0
  43. package/schematics/ng-add/schema.json +22 -0
  44. package/schematics/package.json +3 -0
  45. package/styles/mk-kit.css +750 -0
  46. package/types/mk-kit-ui-block-editor.d.ts +292 -0
  47. package/types/mk-kit-ui-button.d.ts +40 -0
  48. package/types/mk-kit-ui-checkbox.d.ts +62 -0
  49. package/types/mk-kit-ui-chip.d.ts +59 -0
  50. package/types/mk-kit-ui-context-menu.d.ts +57 -0
  51. package/types/mk-kit-ui-core.d.ts +1105 -0
  52. package/types/mk-kit-ui-data.d.ts +2580 -0
  53. package/types/mk-kit-ui-datetime.d.ts +1171 -0
  54. package/types/mk-kit-ui-directives.d.ts +807 -0
  55. package/types/mk-kit-ui-dnd.d.ts +423 -0
  56. package/types/mk-kit-ui-feedback.d.ts +1270 -0
  57. package/types/mk-kit-ui-forms.d.ts +3586 -0
  58. package/types/mk-kit-ui-icon.d.ts +108 -0
  59. package/types/mk-kit-ui-media.d.ts +549 -0
  60. package/types/mk-kit-ui-navigation.d.ts +1169 -0
  61. package/types/mk-kit-ui-rich-text.d.ts +187 -0
  62. package/types/mk-kit-ui-table.d.ts +739 -0
  63. 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
+ ```