@mk-kit/ui 0.34.0 → 0.35.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 +118 -34
- package/fesm2022/mk-kit-ui-core.mjs +25 -5
- package/fesm2022/mk-kit-ui-core.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-datetime.mjs +566 -21
- package/fesm2022/mk-kit-ui-datetime.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-feedback.mjs +4 -4
- package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-forms.mjs +7 -7
- package/fesm2022/mk-kit-ui-forms.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-navigation.mjs +286 -17
- package/fesm2022/mk-kit-ui-navigation.mjs.map +1 -1
- package/package.json +1 -1
- package/types/mk-kit-ui-core.d.ts +18 -2
- package/types/mk-kit-ui-datetime.d.ts +163 -6
- package/types/mk-kit-ui-navigation.d.ts +172 -40
package/README.md
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# @mk-kit/ui
|
|
2
2
|
|
|
3
|
-
**Themable, accessible Angular
|
|
3
|
+
**Themable, accessible Angular component library for admin dashboards, back-offices and internal tools.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
165+ standalone components, directives and services — data tables, charts,
|
|
6
|
+
date & time pickers, editors, kanban, overlays, an app shell — written for
|
|
7
|
+
Angular 22 with signals and `OnPush` from day one. Every visual value is a
|
|
8
|
+
`--mk-*` CSS custom property; light and dark ship out of the box; WCAG 2.1 AA
|
|
9
|
+
is the target, not the marketing. MIT licensed. Zero runtime dependencies
|
|
10
|
+
beyond Angular.
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
- **Docs & live demos:** <https://mk-kit.dev>
|
|
13
|
+
- **Changelog:** [CHANGELOG.md](https://github.com/mk-kit/mk-kit/blob/main/CHANGELOG.md)
|
|
14
|
+
- **Issues:** <https://github.com/mk-kit/mk-kit/issues>
|
|
10
15
|
|
|
11
16
|
## Install
|
|
12
17
|
|
|
@@ -14,32 +19,33 @@ dark mode out of the box. Zero runtime dependencies beyond Angular.
|
|
|
14
19
|
ng add @mk-kit/ui
|
|
15
20
|
```
|
|
16
21
|
|
|
17
|
-
`ng add` installs the package
|
|
18
|
-
|
|
22
|
+
`ng add` installs the package and wires the theme stylesheet into
|
|
23
|
+
`angular.json`. By hand instead:
|
|
19
24
|
|
|
20
25
|
```bash
|
|
21
26
|
npm install @mk-kit/ui
|
|
22
27
|
```
|
|
23
28
|
|
|
24
|
-
Peer dependencies: `@angular/core`, `@angular/common`, `@angular/forms
|
|
29
|
+
Peer dependencies: `@angular/core`, `@angular/common`, `@angular/forms`,
|
|
30
|
+
`@angular/platform-browser` (^22) and `rxjs` (^7.8).
|
|
25
31
|
|
|
26
32
|
## Setup
|
|
27
33
|
|
|
28
|
-
**1. Import the theme stylesheet** once (
|
|
29
|
-
global
|
|
34
|
+
**1. Import the theme stylesheet** once (in `angular.json` `styles` or your
|
|
35
|
+
global stylesheet):
|
|
30
36
|
|
|
31
37
|
```css
|
|
32
38
|
@import '@mk-kit/ui/styles.css';
|
|
33
39
|
```
|
|
34
40
|
|
|
35
|
-
**2. Add the `mk-app` class** to
|
|
36
|
-
background, text
|
|
41
|
+
**2. Add the `mk-app` class** to `<body>` (or a top-level wrapper) so
|
|
42
|
+
background, text colour, fonts and themed scrollbars apply:
|
|
37
43
|
|
|
38
44
|
```html
|
|
39
45
|
<body class="mk-app">
|
|
40
46
|
```
|
|
41
47
|
|
|
42
|
-
**3. Use components** — everything is standalone
|
|
48
|
+
**3. Use components** — everything is standalone; import what you need:
|
|
43
49
|
|
|
44
50
|
```ts
|
|
45
51
|
import { Component, inject } from '@angular/core';
|
|
@@ -61,25 +67,64 @@ export class AppRoot {
|
|
|
61
67
|
}
|
|
62
68
|
```
|
|
63
69
|
|
|
70
|
+
## What's inside
|
|
71
|
+
|
|
72
|
+
| Group | Highlights |
|
|
73
|
+
|---|---|
|
|
74
|
+
| **Forms & inputs** (49) | text, number, password (strength meter), OTP, phone (country prefix), postal code, currency, card number, IBAN, tax id, masked input, tag input, select, autocomplete, multi-select, tree-select, transfer list, checkbox, radio, switch, slider, range slider, rating, colour picker, file upload (dropzone), signature pad, numeric keypad & on-screen keyboard, repeater (`FormArray`), form field with automatic validation messages, form error summary |
|
|
75
|
+
| **Date & time** | calendar, date picker, date-range picker, time picker, month/year picker, week picker, inline mini date, event calendar with editable week/day grid (drag to move / resize) |
|
|
76
|
+
| **Tables & grids** (6) | data table — sort, multi-select, expandable rows, grouping, sticky header, column resize / reorder / pin, inline cell edit, responsive stacking — plus `MkTableDataSource` for server-side sort/page/filter |
|
|
77
|
+
| **Charts** (12) | line/area, bar (stacked, horizontal, label fitting), donut, gauge, progress ring, scatter/bubble, radar, funnel, treemap, heatmap, calendar heatmap, sparkline — SVG, themed, accessible |
|
|
78
|
+
| **Data display** (25) | cards, lists, stat cards, badges, tags, chips, avatars & groups, timeline, description list, tree, empty state, countdown, QR code, diff view, JSON viewer, code block, virtual scroll, carousel, kanban |
|
|
79
|
+
| **Navigation & layout** (18) | app shell (responsive sidebar), nav list & groups, tabs, stepper, breadcrumb, pagination, menu, context menu, command palette (⌘K), page header, toolbar, splitter, drawer, scroll area, FAB, back-to-top |
|
|
80
|
+
| **Feedback & overlays** (20) | dialogs (+ `confirm()` / `alert()` / `prompt()`), bottom sheet, drawer, popover, popconfirm, hovercard, tooltip, toast, snackbar, alert, banner, result page, notification center, product tour, progress bar, loading bar, spinner, skeletons |
|
|
81
|
+
| **Editors & interactions** (22) | block editor (Notion-style, HTML round-trip), rich text, markdown renderer, code editor, log viewer, drag & drop, sortable list, @mentions, hotkeys, undo/redo history, permissions (`*mkCan`), clipboard, intersect, infinite scroll, ripple, scrollspy |
|
|
82
|
+
| **Media** (6) | image with states, gallery, lightbox, cropper, media manager |
|
|
83
|
+
| **Core services** | theme (light/dark/system + density), overlay & anchored panels, focus trap, live announcer, icon registry, i18n |
|
|
84
|
+
|
|
85
|
+
The full, searchable index lives in the docs (`/components-index`).
|
|
86
|
+
|
|
87
|
+
## Entry points
|
|
88
|
+
|
|
89
|
+
Import from the umbrella `@mk-kit/ui`, or from a group to keep bundles lean:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { MkTable } from '@mk-kit/ui/table';
|
|
93
|
+
import { MkLineChart } from '@mk-kit/ui/data';
|
|
94
|
+
import { MkDatePicker } from '@mk-kit/ui/datetime';
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Available: `core`, `forms`, `datetime`, `table`, `data`, `navigation`,
|
|
98
|
+
`feedback`, `directives`, `dnd`, `media`, `icon`, `button`, `checkbox`, `chip`,
|
|
99
|
+
`context-menu`, `rich-text`, `block-editor`. `sideEffects: false` throughout.
|
|
100
|
+
|
|
64
101
|
## Theming
|
|
65
102
|
|
|
66
|
-
The entire look is driven by `--mk-*` custom properties
|
|
67
|
-
|
|
103
|
+
The entire look is driven by `--mk-*` custom properties on `:root`. Override
|
|
104
|
+
any of them — globally, or scoped to a subtree — to re-brand at runtime, no
|
|
105
|
+
rebuild:
|
|
68
106
|
|
|
69
107
|
```css
|
|
70
108
|
:root {
|
|
71
|
-
--mk-primary: #
|
|
72
|
-
--mk-primary-
|
|
109
|
+
--mk-primary: #0f766e;
|
|
110
|
+
--mk-primary-contrast: #ffffff;
|
|
73
111
|
--mk-radius-md: 4px;
|
|
74
112
|
--mk-font-sans: 'Inter', system-ui, sans-serif;
|
|
75
113
|
}
|
|
114
|
+
|
|
115
|
+
[data-mk-theme='dark'] {
|
|
116
|
+
--mk-primary: #2dd4bf;
|
|
117
|
+
--mk-primary-contrast: #042f2e;
|
|
118
|
+
}
|
|
76
119
|
```
|
|
77
120
|
|
|
121
|
+
The docs include a theme builder that exports a ready-to-paste `:root` block.
|
|
122
|
+
|
|
78
123
|
### Dark mode
|
|
79
124
|
|
|
80
|
-
Dark mode
|
|
81
|
-
|
|
82
|
-
`
|
|
125
|
+
Dark mode follows the OS `prefers-color-scheme` with no JavaScript. To let
|
|
126
|
+
users choose, set `data-mk-theme` on `<html>` (`"light"` | `"dark"`) or use
|
|
127
|
+
`MkThemeService`:
|
|
83
128
|
|
|
84
129
|
```ts
|
|
85
130
|
const theme = inject(MkThemeService);
|
|
@@ -89,26 +134,65 @@ theme.toggle(); // flip light/dark
|
|
|
89
134
|
theme.resolvedTheme(); // signal: 'light' | 'dark'
|
|
90
135
|
```
|
|
91
136
|
|
|
92
|
-
The
|
|
137
|
+
The choice is persisted to `localStorage`; the service is SSR-safe.
|
|
93
138
|
|
|
94
|
-
|
|
139
|
+
### Density & touch
|
|
95
140
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
- **Navigation & layout** — Tabs, Accordion, Breadcrumb, Pagination, Menu,
|
|
101
|
-
AppShell, NavList
|
|
141
|
+
Three densities — `comfortable`, `compact`, `touch` — via `data-mk-density`
|
|
142
|
+
or `MkThemeService.setDensity()`. Touch density enlarges every hit target;
|
|
143
|
+
inputs use 16px text on coarse pointers, drag starts on long-press, overlays
|
|
144
|
+
respect safe-area insets.
|
|
102
145
|
|
|
103
|
-
|
|
104
|
-
|
|
146
|
+
## Internationalisation
|
|
147
|
+
|
|
148
|
+
Every string the library renders comes from one provider:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { provideMkI18n } from '@mk-kit/ui/core';
|
|
152
|
+
|
|
153
|
+
bootstrapApplication(AppRoot, {
|
|
154
|
+
providers: [provideMkI18n({ close: 'Zamknij', noData: 'Brak danych' })],
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Overrides can be scoped to a component subtree for mixed-locale screens.
|
|
159
|
+
Layout and arrow keys flip under `dir="rtl"`.
|
|
105
160
|
|
|
106
161
|
## Accessibility
|
|
107
162
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
163
|
+
Target: **WCAG 2.1 AA**. Semantic roles and complete `aria-*` wiring, roving
|
|
164
|
+
tabindex for composite widgets, focus trapping and background `inert` for
|
|
165
|
+
modals, an Escape that closes only the topmost overlay, live-region
|
|
166
|
+
announcements for async state, contrast-checked tokens in both themes,
|
|
167
|
+
`prefers-reduced-motion` and `forced-colors` support. Axe runs over rendered
|
|
168
|
+
fixtures in the test suite; information is never conveyed by colour alone.
|
|
169
|
+
|
|
170
|
+
## SSR & zoneless
|
|
171
|
+
|
|
172
|
+
Every component guards non-browser platforms (an SSR smoke suite renders the
|
|
173
|
+
whole library on the server), and nothing depends on Zone.js.
|
|
174
|
+
|
|
175
|
+
## Coming from PrimeNG?
|
|
176
|
+
|
|
177
|
+
The admin surface maps closely — `p-table` → `mk-table`, `p-select` →
|
|
178
|
+
`mk-select`, `p-datePicker` → `mk-date-picker`, `DialogService` →
|
|
179
|
+
`MkDialogService`, `MessageService` → `MkToastService` — and the parts
|
|
180
|
+
PrimeUI sells separately (charts, text editor, scheduler, task board) are
|
|
181
|
+
here under MIT. The docs landing page carries a mapping table.
|
|
182
|
+
|
|
183
|
+
## Versioning & support
|
|
184
|
+
|
|
185
|
+
mk-kit tracks the current Angular major. A matching release follows each new
|
|
186
|
+
Angular major within weeks; the previous major keeps receiving fixes for six
|
|
187
|
+
months. Releases are published from CI with npm provenance
|
|
188
|
+
(`npm audit signatures`).
|
|
189
|
+
|
|
190
|
+
## Contributing
|
|
191
|
+
|
|
192
|
+
See [CONTRIBUTING.md](https://github.com/mk-kit/mk-kit/blob/main/CONTRIBUTING.md)
|
|
193
|
+
(DCO sign-off), [SECURITY.md](https://github.com/mk-kit/mk-kit/blob/main/SECURITY.md)
|
|
194
|
+
for vulnerability reports, and [TRADEMARK.md](https://github.com/mk-kit/mk-kit/blob/main/TRADEMARK.md)
|
|
195
|
+
for use of the name and logo.
|
|
112
196
|
|
|
113
197
|
## License
|
|
114
198
|
|
|
@@ -678,7 +678,7 @@ function sideOf(placement) {
|
|
|
678
678
|
return 'top';
|
|
679
679
|
if (placement.startsWith('bottom'))
|
|
680
680
|
return 'bottom';
|
|
681
|
-
return placement;
|
|
681
|
+
return placement.startsWith('left') ? 'left' : 'right';
|
|
682
682
|
}
|
|
683
683
|
function alignOf(placement) {
|
|
684
684
|
if (placement.endsWith('-start'))
|
|
@@ -740,8 +740,14 @@ function mkComputeAnchoredPosition(anchor, panel, viewport, opts) {
|
|
|
740
740
|
left = anchor.left + anchor.width / 2 - w / 2;
|
|
741
741
|
}
|
|
742
742
|
else {
|
|
743
|
-
// Left/right:
|
|
744
|
-
|
|
743
|
+
// Left/right: the cross axis is vertical. `-start` tops the panel with the
|
|
744
|
+
// anchor (a submenu beside its item), `-end` bottoms it, else centre.
|
|
745
|
+
if (align === 'start')
|
|
746
|
+
top = anchor.top;
|
|
747
|
+
else if (align === 'end')
|
|
748
|
+
top = anchor.bottom - h;
|
|
749
|
+
else
|
|
750
|
+
top = anchor.top + anchor.height / 2 - h / 2;
|
|
745
751
|
}
|
|
746
752
|
if (clamp) {
|
|
747
753
|
left = Math.max(gap, Math.min(left, vw - w - gap));
|
|
@@ -793,6 +799,13 @@ class MkAnchoredPanel {
|
|
|
793
799
|
/** Clamp the panel inside the viewport. */
|
|
794
800
|
clamp = input(true, /* @ts-ignore */
|
|
795
801
|
...(ngDevMode ? [{ debugName: "clamp" }] : /* istanbul ignore next */ []));
|
|
802
|
+
/**
|
|
803
|
+
* Predicate consulted before an outside pointerdown dismisses the panel.
|
|
804
|
+
* Return `true` for targets that must keep it open — e.g. a nested panel
|
|
805
|
+
* (a submenu) that lives in the top layer outside this element.
|
|
806
|
+
*/
|
|
807
|
+
keepOpenWhen = input(null, /* @ts-ignore */
|
|
808
|
+
...(ngDevMode ? [{ debugName: "keepOpenWhen" }] : /* istanbul ignore next */ []));
|
|
796
809
|
/** Emitted on an outside pointerdown or when the window loses focus. */
|
|
797
810
|
dismiss = output();
|
|
798
811
|
popover = false;
|
|
@@ -828,6 +841,8 @@ class MkAnchoredPanel {
|
|
|
828
841
|
const anchorEl = this.resolveAnchorEl();
|
|
829
842
|
if (anchorEl?.contains(target))
|
|
830
843
|
return;
|
|
844
|
+
if (this.keepOpenWhen()?.(target))
|
|
845
|
+
return;
|
|
831
846
|
this.dismiss.emit();
|
|
832
847
|
};
|
|
833
848
|
onWindowBlur = () => this.dismiss.emit();
|
|
@@ -1022,7 +1037,7 @@ class MkAnchoredPanel {
|
|
|
1022
1037
|
el.remove();
|
|
1023
1038
|
}
|
|
1024
1039
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkAnchoredPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1025
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: MkAnchoredPanel, isStandalone: true, selector: "[mkAnchoredPanel]", inputs: { anchor: { classPropertyName: "anchor", publicName: "mkAnchoredPanelFor", isSignal: true, isRequired: false, transformFunction: null }, anchorRect: { classPropertyName: "anchorRect", publicName: "anchorRect", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, matchWidth: { classPropertyName: "matchWidth", publicName: "matchWidth", isSignal: true, isRequired: false, transformFunction: null }, flip: { classPropertyName: "flip", publicName: "flip", isSignal: true, isRequired: false, transformFunction: null }, clamp: { classPropertyName: "clamp", publicName: "clamp", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss" }, exportAs: ["mkAnchoredPanel"], ngImport: i0 });
|
|
1040
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: MkAnchoredPanel, isStandalone: true, selector: "[mkAnchoredPanel]", inputs: { anchor: { classPropertyName: "anchor", publicName: "mkAnchoredPanelFor", isSignal: true, isRequired: false, transformFunction: null }, anchorRect: { classPropertyName: "anchorRect", publicName: "anchorRect", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, matchWidth: { classPropertyName: "matchWidth", publicName: "matchWidth", isSignal: true, isRequired: false, transformFunction: null }, flip: { classPropertyName: "flip", publicName: "flip", isSignal: true, isRequired: false, transformFunction: null }, clamp: { classPropertyName: "clamp", publicName: "clamp", isSignal: true, isRequired: false, transformFunction: null }, keepOpenWhen: { classPropertyName: "keepOpenWhen", publicName: "keepOpenWhen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss" }, exportAs: ["mkAnchoredPanel"], ngImport: i0 });
|
|
1026
1041
|
}
|
|
1027
1042
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkAnchoredPanel, decorators: [{
|
|
1028
1043
|
type: Directive,
|
|
@@ -1030,7 +1045,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
1030
1045
|
selector: '[mkAnchoredPanel]',
|
|
1031
1046
|
exportAs: 'mkAnchoredPanel',
|
|
1032
1047
|
}]
|
|
1033
|
-
}], propDecorators: { anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkAnchoredPanelFor", required: false }] }], anchorRect: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorRect", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], matchWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "matchWidth", required: false }] }], flip: [{ type: i0.Input, args: [{ isSignal: true, alias: "flip", required: false }] }], clamp: [{ type: i0.Input, args: [{ isSignal: true, alias: "clamp", required: false }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }] } });
|
|
1048
|
+
}], propDecorators: { anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkAnchoredPanelFor", required: false }] }], anchorRect: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorRect", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], matchWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "matchWidth", required: false }] }], flip: [{ type: i0.Input, args: [{ isSignal: true, alias: "flip", required: false }] }], clamp: [{ type: i0.Input, args: [{ isSignal: true, alias: "clamp", required: false }] }], keepOpenWhen: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepOpenWhen", required: false }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }] } });
|
|
1034
1049
|
|
|
1035
1050
|
/** The built-in English validation messages. */
|
|
1036
1051
|
const MK_DEFAULT_VALIDATION = {
|
|
@@ -1132,6 +1147,11 @@ const MK_DEFAULT_I18N = {
|
|
|
1132
1147
|
chooseWeek: 'Choose week',
|
|
1133
1148
|
openCalendar: 'Open calendar',
|
|
1134
1149
|
openTimeList: 'Open time list',
|
|
1150
|
+
moreActions: 'More actions',
|
|
1151
|
+
selectDateTime: 'Select date and time…',
|
|
1152
|
+
chooseDateTime: 'Choose date and time',
|
|
1153
|
+
chooseTime: 'Choose time',
|
|
1154
|
+
openDateTimePicker: 'Open date and time picker',
|
|
1135
1155
|
daySegment: 'Day',
|
|
1136
1156
|
monthSegment: 'Month',
|
|
1137
1157
|
yearSegment: 'Year',
|