@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
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { Signal, TemplateRef } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Emits when a pointer press lands outside the host element — the building block
|
|
6
|
+
* for dismissing menus, popovers and custom dropdowns. Listens on the document
|
|
7
|
+
* in the capture phase so it fires before the target handles the event.
|
|
8
|
+
*
|
|
9
|
+
* ```html
|
|
10
|
+
* <div class="panel" (mkClickOutside)="close()">…</div>
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
declare class MkClickOutside {
|
|
14
|
+
private readonly host;
|
|
15
|
+
private readonly document;
|
|
16
|
+
private readonly isBrowser;
|
|
17
|
+
/** Disable the listener without removing the directive. */
|
|
18
|
+
readonly enabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
19
|
+
/** Emits the originating event when a pointerdown occurs outside the host. */
|
|
20
|
+
readonly mkClickOutside: _angular_core.OutputEmitterRef<PointerEvent>;
|
|
21
|
+
private readonly onPointerdown;
|
|
22
|
+
constructor();
|
|
23
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkClickOutside, never>;
|
|
24
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkClickOutside, "[mkClickOutside]", ["mkClickOutside"], { "enabled": { "alias": "mkClickOutsideEnabled"; "required": false; "isSignal": true; }; }, { "mkClickOutside": "mkClickOutside"; }, never, never, true, never>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Copies text to the clipboard when the host is clicked. Emits `(copied)` on
|
|
29
|
+
* success and `(copyFailed)` on error, and exposes a transient `copied` signal
|
|
30
|
+
* (via `exportAs`) so a template can show a "Copied!" state without extra code.
|
|
31
|
+
*
|
|
32
|
+
* ```html
|
|
33
|
+
* <button mkButton [mkCopyToClipboard]="token" #c="mkCopyToClipboard">
|
|
34
|
+
* {{ c.justCopied() ? 'Copied!' : 'Copy' }}
|
|
35
|
+
* </button>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare class MkCopyToClipboard {
|
|
39
|
+
private readonly document;
|
|
40
|
+
private readonly isBrowser;
|
|
41
|
+
/** The text to copy. */
|
|
42
|
+
readonly text: _angular_core.InputSignal<string>;
|
|
43
|
+
/** How long (ms) the `copied` signal stays true after a successful copy. */
|
|
44
|
+
readonly feedbackDuration: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
45
|
+
/** Emits the copied text on success. */
|
|
46
|
+
readonly copied: _angular_core.OutputEmitterRef<string>;
|
|
47
|
+
/** Emits the error on failure. */
|
|
48
|
+
readonly copyFailed: _angular_core.OutputEmitterRef<unknown>;
|
|
49
|
+
private readonly _copied;
|
|
50
|
+
/** True for `feedbackDuration` ms after a successful copy. */
|
|
51
|
+
readonly justCopied: _angular_core.Signal<boolean>;
|
|
52
|
+
private resetTimer?;
|
|
53
|
+
/** Copy the text now. Bound to the host click, but callable directly too. */
|
|
54
|
+
copy(): Promise<void>;
|
|
55
|
+
private write;
|
|
56
|
+
private flagCopied;
|
|
57
|
+
ngOnDestroy(): void;
|
|
58
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkCopyToClipboard, never>;
|
|
59
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkCopyToClipboard, "[mkCopyToClipboard]", ["mkCopyToClipboard"], { "text": { "alias": "mkCopyToClipboard"; "required": true; "isSignal": true; }; "feedbackDuration": { "alias": "mkCopyFeedbackDuration"; "required": false; "isSignal": true; }; }, { "copied": "copiedText"; "copyFailed": "copyFailed"; }, never, never, true, never>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Grows a `<textarea>` to fit its content, between `minRows` and `maxRows`
|
|
64
|
+
* (0 = unbounded). Resizes on input and when the bound value changes.
|
|
65
|
+
*
|
|
66
|
+
* ```html
|
|
67
|
+
* <textarea mkInput mkAutosize [mkAutosizeMaxRows]="8" [(ngModel)]="note"></textarea>
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare class MkAutosize {
|
|
71
|
+
private readonly el;
|
|
72
|
+
private readonly document;
|
|
73
|
+
private readonly isBrowser;
|
|
74
|
+
/** Minimum height in rows. */
|
|
75
|
+
readonly minRows: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
76
|
+
/** Maximum height in rows before scrolling (0 = unbounded). */
|
|
77
|
+
readonly maxRows: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
78
|
+
/** Bind the textarea's value so programmatic changes trigger a resize. */
|
|
79
|
+
readonly value: _angular_core.InputSignal<unknown>;
|
|
80
|
+
constructor();
|
|
81
|
+
/** Recompute the textarea height from its content. */
|
|
82
|
+
resize(): void;
|
|
83
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkAutosize, never>;
|
|
84
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkAutosize, "textarea[mkAutosize]", never, { "minRows": { "alias": "mkAutosizeMinRows"; "required": false; "isSignal": true; }; "maxRows": { "alias": "mkAutosizeMaxRows"; "required": false; "isSignal": true; }; "value": { "alias": "mkAutosizeValue"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Focuses the host element once it renders. Handy for dialogs, drawers and
|
|
89
|
+
* newly-revealed forms. Disable conditionally with `[mkAutofocus]="cond"`.
|
|
90
|
+
*
|
|
91
|
+
* ```html
|
|
92
|
+
* <input mkInput mkAutofocus />
|
|
93
|
+
* <input mkInput [mkAutofocus]="isEditing()" [mkAutofocusDelay]="100" />
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
declare class MkAutofocus {
|
|
97
|
+
private readonly el;
|
|
98
|
+
private readonly document;
|
|
99
|
+
private readonly isBrowser;
|
|
100
|
+
/** Whether to focus. Bare `mkAutofocus` = true. */
|
|
101
|
+
readonly enabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
102
|
+
/** Delay before focusing, in ms (e.g. to wait out an entrance animation). */
|
|
103
|
+
readonly delay: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
104
|
+
private timeoutId?;
|
|
105
|
+
constructor();
|
|
106
|
+
private focus;
|
|
107
|
+
ngOnDestroy(): void;
|
|
108
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkAutofocus, never>;
|
|
109
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkAutofocus, "[mkAutofocus]", never, { "enabled": { "alias": "mkAutofocus"; "required": false; "isSignal": true; }; "delay": { "alias": "mkAutofocusDelay"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Scrollspy — tracks which section is currently in view and exposes its `id`,
|
|
114
|
+
* so a table of contents / "on this page" nav can highlight the active link.
|
|
115
|
+
*
|
|
116
|
+
* Apply it to any element (typically the nav) and point `mkScrollspy` at a CSS
|
|
117
|
+
* selector matching the section elements to watch (each needs an `id`). Read the
|
|
118
|
+
* active id via the exported instance, or bind `(activeChange)`:
|
|
119
|
+
*
|
|
120
|
+
* ```html
|
|
121
|
+
* <nav mkScrollspy="main :is(h2, h3)[id]" #spy="mkScrollspy">
|
|
122
|
+
* <a href="#intro" [class.is-active]="spy.activeId() === 'intro'">Intro</a>
|
|
123
|
+
* …
|
|
124
|
+
* </nav>
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* A section activates once its top passes `offset` px below the top of the
|
|
128
|
+
* scroll root (default the viewport). When scrolling through a gap between
|
|
129
|
+
* sections the previous one stays active until the next crosses the line.
|
|
130
|
+
*/
|
|
131
|
+
declare class MkScrollspy {
|
|
132
|
+
private readonly document;
|
|
133
|
+
private readonly isBrowser;
|
|
134
|
+
/** CSS selector for the section elements to track (queried within `root`). */
|
|
135
|
+
readonly sections: _angular_core.InputSignal<string>;
|
|
136
|
+
/**
|
|
137
|
+
* Scroll container. Sections are queried inside it and its top edge is the
|
|
138
|
+
* activation reference. Defaults to the whole document / viewport.
|
|
139
|
+
*/
|
|
140
|
+
readonly root: _angular_core.InputSignal<HTMLElement | null>;
|
|
141
|
+
/**
|
|
142
|
+
* Distance (px) below the scroll root's top at which a section becomes
|
|
143
|
+
* active. Raise it to account for a sticky header.
|
|
144
|
+
*/
|
|
145
|
+
readonly offset: _angular_core.InputSignal<number>;
|
|
146
|
+
/** The id of the currently active section (`null` before the first one). */
|
|
147
|
+
readonly activeId: _angular_core.WritableSignal<string | null>;
|
|
148
|
+
/** Emitted whenever the active section changes. */
|
|
149
|
+
readonly activeChange: _angular_core.OutputEmitterRef<string | null>;
|
|
150
|
+
private scrollSource?;
|
|
151
|
+
constructor();
|
|
152
|
+
/** Re-evaluate the active section (call after sections change). */
|
|
153
|
+
refresh(): void;
|
|
154
|
+
private querySections;
|
|
155
|
+
private computeRaf;
|
|
156
|
+
/** rAF-coalesced — several scroll ticks per frame trigger one layout read. */
|
|
157
|
+
private readonly onScroll;
|
|
158
|
+
private compute;
|
|
159
|
+
/** Whether the scroll root is scrolled to (or within 1px of) its bottom. */
|
|
160
|
+
private atBottom;
|
|
161
|
+
private setActive;
|
|
162
|
+
ngOnDestroy(): void;
|
|
163
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkScrollspy, never>;
|
|
164
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkScrollspy, "[mkScrollspy]", ["mkScrollspy"], { "sections": { "alias": "mkScrollspy"; "required": true; "isSignal": true; }; "root": { "alias": "root"; "required": false; "isSignal": true; }; "offset": { "alias": "offset"; "required": false; "isSignal": true; }; }, { "activeChange": "activeChange"; }, never, never, true, never>;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Intersect — a thin `IntersectionObserver` wrapper. Emits `(mkIntersect)` with
|
|
169
|
+
* the host's visibility whenever it enters or leaves the viewport (or `root`),
|
|
170
|
+
* and exposes an `intersecting` signal via `exportAs`. Set `once` to fire a
|
|
171
|
+
* single time and disconnect — the primitive for reveal-on-scroll animations
|
|
172
|
+
* and lazy loading.
|
|
173
|
+
*
|
|
174
|
+
* ```html
|
|
175
|
+
* <img [attr.src]="loaded() ? src : null" mkIntersect once
|
|
176
|
+
* (mkIntersect)="loaded.set(true)" />
|
|
177
|
+
*
|
|
178
|
+
* <div mkIntersect #io="mkIntersect" [class.in-view]="io.intersecting()">…</div>
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
declare class MkIntersect {
|
|
182
|
+
private readonly host;
|
|
183
|
+
private readonly document;
|
|
184
|
+
private readonly injector;
|
|
185
|
+
private readonly isBrowser;
|
|
186
|
+
/** Element used as the viewport for the check (defaults to the browser one). */
|
|
187
|
+
readonly root: _angular_core.InputSignal<HTMLElement | null>;
|
|
188
|
+
/** Margin grown/shrunk around the root before computing intersection. */
|
|
189
|
+
readonly rootMargin: _angular_core.InputSignal<string>;
|
|
190
|
+
/** Visibility ratio(s) at which the observer fires. */
|
|
191
|
+
readonly threshold: _angular_core.InputSignal<number | number[]>;
|
|
192
|
+
/** Emit the first time the host becomes visible, then disconnect. */
|
|
193
|
+
readonly once: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
194
|
+
/** Emits the host's `isIntersecting` state on every change. */
|
|
195
|
+
readonly mkIntersect: _angular_core.OutputEmitterRef<boolean>;
|
|
196
|
+
/** The current visibility, also readable via `exportAs`. */
|
|
197
|
+
readonly intersecting: _angular_core.WritableSignal<boolean>;
|
|
198
|
+
private observer?;
|
|
199
|
+
constructor();
|
|
200
|
+
private onEntries;
|
|
201
|
+
private disconnect;
|
|
202
|
+
ngOnDestroy(): void;
|
|
203
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkIntersect, never>;
|
|
204
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkIntersect, "[mkIntersect]", ["mkIntersect"], { "root": { "alias": "root"; "required": false; "isSignal": true; }; "rootMargin": { "alias": "rootMargin"; "required": false; "isSignal": true; }; "threshold": { "alias": "threshold"; "required": false; "isSignal": true; }; "once": { "alias": "once"; "required": false; "isSignal": true; }; }, { "mkIntersect": "mkIntersect"; }, never, never, true, never>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* InfiniteScroll — emits `(mkInfiniteScroll)` when the host scroll container is
|
|
209
|
+
* scrolled within `distance` px of its bottom, so a list can load its next
|
|
210
|
+
* page. Apply it to the scrollable element (`overflow: auto`). It fires once per
|
|
211
|
+
* approach and re-arms after you scroll back up past the threshold, so a single
|
|
212
|
+
* gesture never triggers repeated loads. Set `disabled` while a page is loading
|
|
213
|
+
* or once everything is loaded.
|
|
214
|
+
*
|
|
215
|
+
* ```html
|
|
216
|
+
* <div class="feed" mkInfiniteScroll [disabled]="loading() || done()"
|
|
217
|
+
* (mkInfiniteScroll)="loadMore()">
|
|
218
|
+
* @for (item of items(); track item.id) { … }
|
|
219
|
+
* </div>
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
declare class MkInfiniteScroll {
|
|
223
|
+
private readonly host;
|
|
224
|
+
private readonly isBrowser;
|
|
225
|
+
/** Distance (px) from the bottom at which the output fires. */
|
|
226
|
+
readonly distance: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
227
|
+
/** Pause firing (e.g. while a page is loading or all items are loaded). */
|
|
228
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
229
|
+
/** Emitted when the host is scrolled near its bottom. */
|
|
230
|
+
readonly mkInfiniteScroll: _angular_core.OutputEmitterRef<void>;
|
|
231
|
+
/** Whether the next bottom-approach is allowed to fire. */
|
|
232
|
+
private armed;
|
|
233
|
+
constructor();
|
|
234
|
+
private readonly onScroll;
|
|
235
|
+
private check;
|
|
236
|
+
ngOnDestroy(): void;
|
|
237
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkInfiniteScroll, never>;
|
|
238
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkInfiniteScroll, "[mkInfiniteScroll]", ["mkInfiniteScroll"], { "distance": { "alias": "distance"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "mkInfiniteScroll": "mkInfiniteScroll"; }, never, never, true, never>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Ripple — a Material-style touch ripple that expands from the pointer on press.
|
|
243
|
+
* Apply it to any pressable surface (button, list item, card). The directive
|
|
244
|
+
* makes the host a positioned, clipping container and paints a short-lived wave;
|
|
245
|
+
* it respects `prefers-reduced-motion`.
|
|
246
|
+
*
|
|
247
|
+
* ```html
|
|
248
|
+
* <button mkRipple>Save</button>
|
|
249
|
+
* <li mkRipple mkRippleColor="var(--mk-primary)">Item</li>
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
declare class MkRipple {
|
|
253
|
+
private readonly host;
|
|
254
|
+
private readonly document;
|
|
255
|
+
private readonly isBrowser;
|
|
256
|
+
/** Wave colour (any CSS colour). Defaults to the host's `currentColor`. */
|
|
257
|
+
readonly color: _angular_core.InputSignal<string>;
|
|
258
|
+
/** Start every ripple from the centre instead of the pointer. */
|
|
259
|
+
readonly centered: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
260
|
+
/** Turn the effect off. */
|
|
261
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
262
|
+
protected spawn(event: PointerEvent): void;
|
|
263
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkRipple, never>;
|
|
264
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkRipple, "[mkRipple]", never, { "color": { "alias": "mkRippleColor"; "required": false; "isSignal": true; }; "centered": { "alias": "mkRippleCentered"; "required": false; "isSignal": true; }; "disabled": { "alias": "mkRippleDisabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Result of applying a mask: the formatted string and its raw token chars. */
|
|
268
|
+
interface MkMaskResult {
|
|
269
|
+
masked: string;
|
|
270
|
+
unmasked: string;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Format `value` against a mask `pattern`. Pattern tokens: `0` = digit,
|
|
274
|
+
* `A` = letter, `*` = alphanumeric; any other character is a literal that is
|
|
275
|
+
* inserted automatically. Characters that don't fit the current token are
|
|
276
|
+
* skipped, so pasted / mistyped input is coerced into shape.
|
|
277
|
+
*
|
|
278
|
+
* Returns the `masked` (display) string and the `unmasked` string (only the
|
|
279
|
+
* token characters — the value you usually store).
|
|
280
|
+
*/
|
|
281
|
+
declare function mkApplyMask(value: string, pattern: string): MkMaskResult;
|
|
282
|
+
/**
|
|
283
|
+
* Compute where the caret belongs after re-masking: after the same number of
|
|
284
|
+
* value (alphanumeric) characters the caret used to sit behind, absorbing any
|
|
285
|
+
* literal(s) that immediately follow — so typing "1" in "(0)" lands the caret
|
|
286
|
+
* after the "(". Shared by {@link MkMask} and the masked form controls.
|
|
287
|
+
*/
|
|
288
|
+
declare function mkMaskCaret(oldValue: string, oldCaret: number, masked: string): number;
|
|
289
|
+
/**
|
|
290
|
+
* Mask — formats an `<input>` as the user types against a token pattern
|
|
291
|
+
* (phone, card, date, custom). Apply it to any text input; read the raw value
|
|
292
|
+
* via `(unmaskedChange)` (or the exported `unmasked` getter) and the formatted
|
|
293
|
+
* value via `(maskedChange)`.
|
|
294
|
+
*
|
|
295
|
+
* ```html
|
|
296
|
+
* <input mkInput mkMask="(000) 000-0000" (unmaskedChange)="phone.set($event)" />
|
|
297
|
+
* <input mkInput mkMask="0000 0000 0000 0000" />
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
declare class MkMask {
|
|
301
|
+
private readonly host;
|
|
302
|
+
/** The mask pattern (token string). */
|
|
303
|
+
readonly pattern: _angular_core.InputSignal<string>;
|
|
304
|
+
/** Override the derived `inputmode` (`numeric` for digit-only masks). */
|
|
305
|
+
readonly maskInputmode: _angular_core.InputSignal<string | null>;
|
|
306
|
+
/** Mobile keyboard hint: numeric for digit-only patterns unless overridden. */
|
|
307
|
+
protected readonly inputMode: _angular_core.Signal<string>;
|
|
308
|
+
/** The formatted value, on every change. */
|
|
309
|
+
readonly maskedChange: _angular_core.OutputEmitterRef<string>;
|
|
310
|
+
/** The raw value (token characters only), on every change. */
|
|
311
|
+
readonly unmaskedChange: _angular_core.OutputEmitterRef<string>;
|
|
312
|
+
private _masked;
|
|
313
|
+
private _unmasked;
|
|
314
|
+
/** The current formatted value. */
|
|
315
|
+
get value(): string;
|
|
316
|
+
/** The current raw value (token characters only). */
|
|
317
|
+
get unmasked(): string;
|
|
318
|
+
protected onInput(): void;
|
|
319
|
+
/** Position the caret after the same number of value chars as before. */
|
|
320
|
+
private setCaret;
|
|
321
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkMask, never>;
|
|
322
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkMask, "[mkMask]", ["mkMask"], { "pattern": { "alias": "mkMask"; "required": true; "isSignal": true; }; "maskInputmode": { "alias": "mkMaskInputmode"; "required": false; "isSignal": true; }; }, { "maskedChange": "maskedChange"; "unmaskedChange": "unmaskedChange"; }, never, never, true, never>;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** The attributes a field preset can control. */
|
|
326
|
+
declare const MK_FIELD_ATTRS: readonly ["type", "inputmode", "autocomplete", "autocapitalize", "autocorrect", "spellcheck", "enterkeyhint"];
|
|
327
|
+
type MkFieldAttr = (typeof MK_FIELD_ATTRS)[number];
|
|
328
|
+
/** The attribute bundle applied for one `MkFieldKind`. Omitted = not set. */
|
|
329
|
+
type MkFieldPreset = Partial<Record<MkFieldAttr, string>>;
|
|
330
|
+
/** Semantic field kinds understood by {@link MkField}. */
|
|
331
|
+
type MkFieldKind = 'email' | 'tel' | 'url' | 'search' | 'numeric' | 'given-name' | 'family-name' | 'name' | 'organization' | 'street' | 'address-line1' | 'address-line2' | 'city' | 'region' | 'country' | 'postal-code' | 'username' | 'one-time-code';
|
|
332
|
+
/**
|
|
333
|
+
* The attribute bundle per field kind. Exported so hosts can inspect what a
|
|
334
|
+
* kind resolves to, or spread an entry into their own preset table.
|
|
335
|
+
*
|
|
336
|
+
* `autocorrect` (non-standard, honoured by iOS Safari) is set to `off`
|
|
337
|
+
* wherever `spellcheck` is `false`.
|
|
338
|
+
*/
|
|
339
|
+
declare const MK_FIELD_PRESETS: Readonly<Record<MkFieldKind, MkFieldPreset>>;
|
|
340
|
+
/**
|
|
341
|
+
* Field — applies the semantic mobile-keyboard and autofill attribute bundle
|
|
342
|
+
* for a kind of field, so you never again ship an email input that iOS
|
|
343
|
+
* capitalises or an address input the browser can't autofill.
|
|
344
|
+
*
|
|
345
|
+
* It sets `type`, `inputmode`, `autocomplete`, `autocapitalize`, `autocorrect`,
|
|
346
|
+
* `spellcheck` and `enterkeyhint` (see {@link MK_FIELD_PRESETS}) and nothing
|
|
347
|
+
* else — no styling, no value handling — so it composes with `mkInput`,
|
|
348
|
+
* `mk-form-field`, `[(ngModel)]` and reactive forms untouched.
|
|
349
|
+
*
|
|
350
|
+
* ```html
|
|
351
|
+
* <input mkInput mkField="email" formControlName="email" />
|
|
352
|
+
* <input mkInput mkField="street" formControlName="street" />
|
|
353
|
+
* <input mkInput [mkField]="isCompany() ? 'organization' : 'name'" />
|
|
354
|
+
* ```
|
|
355
|
+
*
|
|
356
|
+
* ## Precedence
|
|
357
|
+
*
|
|
358
|
+
* A **static** attribute you write on the element wins: the directive reads the
|
|
359
|
+
* seven attributes once at construction (before any host binding has run) and
|
|
360
|
+
* keeps whatever was already there, filling in only what you left out. So
|
|
361
|
+
* `<input mkField="username" type="password" />` stays a password field but
|
|
362
|
+
* still gets `autocomplete="username"`, `autocapitalize="off"`, …
|
|
363
|
+
*
|
|
364
|
+
* A **template binding** for the same attribute (`[attr.autocomplete]="…"`)
|
|
365
|
+
* does *not* win — Angular applies directive host bindings after template
|
|
366
|
+
* bindings, so the preset overwrites it. Use a static attribute to override, or
|
|
367
|
+
* drop `mkField` and set the bundle yourself.
|
|
368
|
+
*/
|
|
369
|
+
declare class MkField {
|
|
370
|
+
private readonly el;
|
|
371
|
+
/** Which kind of field this is. */
|
|
372
|
+
readonly kind: _angular_core.InputSignal<MkFieldKind>;
|
|
373
|
+
/**
|
|
374
|
+
* Attributes the consumer put on the element themselves, read once before
|
|
375
|
+
* any host binding has run — these always win over the preset.
|
|
376
|
+
*/
|
|
377
|
+
private readonly authored;
|
|
378
|
+
/** The resolved attribute bundle: authored value, else preset, else unset. */
|
|
379
|
+
protected readonly attrs: _angular_core.Signal<Record<"type" | "inputmode" | "autocomplete" | "autocapitalize" | "autocorrect" | "spellcheck" | "enterkeyhint", string | null>>;
|
|
380
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkField, never>;
|
|
381
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkField, "[mkField]", never, { "kind": { "alias": "mkField"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* A single undoable step recorded in an {@link MkHistoryStack}.
|
|
386
|
+
*
|
|
387
|
+
* The **first** execution of an action is the caller's job — `push()` records
|
|
388
|
+
* an action that has *already happened*. `undo()` reverts it; `redo()` applies
|
|
389
|
+
* it again after an undo.
|
|
390
|
+
*/
|
|
391
|
+
interface MkHistoryEntry {
|
|
392
|
+
/** Human label for menus/tooltips ("Delete row", "Move card"). */
|
|
393
|
+
label: string;
|
|
394
|
+
/** Revert the action. */
|
|
395
|
+
undo(): void;
|
|
396
|
+
/** Re-apply the action after an undo. */
|
|
397
|
+
redo(): void;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* MkHistoryStack — a pure, signal-based, linear undo/redo command stack with
|
|
401
|
+
* no Angular DI involved. Instantiate it directly for a local editing session
|
|
402
|
+
* (a dialog, a canvas, a form wizard), or use the app-wide singleton
|
|
403
|
+
* {@link MkHistoryService}.
|
|
404
|
+
*
|
|
405
|
+
* ```ts
|
|
406
|
+
* const stack = new MkHistoryStack();
|
|
407
|
+
* deleteRow(row); // 1. do the work yourself
|
|
408
|
+
* stack.push({ // 2. record how to revert/redo it
|
|
409
|
+
* label: 'Delete row',
|
|
410
|
+
* undo: () => insertRow(row),
|
|
411
|
+
* redo: () => deleteRow(row),
|
|
412
|
+
* });
|
|
413
|
+
* stack.undo(); // row is back
|
|
414
|
+
* ```
|
|
415
|
+
*
|
|
416
|
+
* ### Linear history
|
|
417
|
+
* `push()` clears the redo branch — after undoing twice and pushing a new
|
|
418
|
+
* entry, the two undone entries are gone (the standard editor model).
|
|
419
|
+
*
|
|
420
|
+
* ### ⚠️ Re-entrancy guard — the classic footgun
|
|
421
|
+
* Pushes made **while** `undo()` or `redo()` is executing are **silently
|
|
422
|
+
* ignored**. Naive consumers often record history from a generic change
|
|
423
|
+
* handler (`(cellEdit)`, `valueChanges`, …); when an undo replays the old
|
|
424
|
+
* value, that same handler fires again and would push a mirror entry,
|
|
425
|
+
* corrupting the stack into an undo/redo ping-pong. The guard makes that
|
|
426
|
+
* pattern safe by construction. If you need to record something new in
|
|
427
|
+
* response to an undo, do it after `undo()` returns.
|
|
428
|
+
*/
|
|
429
|
+
declare class MkHistoryStack {
|
|
430
|
+
private readonly undoStack;
|
|
431
|
+
private readonly redoStack;
|
|
432
|
+
/** True while an entry's `undo()`/`redo()` is running (re-entrancy guard). */
|
|
433
|
+
private replaying;
|
|
434
|
+
/** Depth of nested `batch()` calls; > 0 while a batch is collecting. */
|
|
435
|
+
private batchDepth;
|
|
436
|
+
private batchLabel;
|
|
437
|
+
private batchBuffer;
|
|
438
|
+
private maxSize;
|
|
439
|
+
/** True when at least one entry can be undone. */
|
|
440
|
+
readonly canUndo: Signal<boolean>;
|
|
441
|
+
/** True when at least one undone entry can be redone. */
|
|
442
|
+
readonly canRedo: Signal<boolean>;
|
|
443
|
+
/** Label of the entry `undo()` would revert, or `null` ("Undo *Delete row*"). */
|
|
444
|
+
readonly undoLabel: Signal<string | null>;
|
|
445
|
+
/** Label of the entry `redo()` would re-apply, or `null`. */
|
|
446
|
+
readonly redoLabel: Signal<string | null>;
|
|
447
|
+
/** Number of undoable entries currently on the stack. */
|
|
448
|
+
readonly size: Signal<number>;
|
|
449
|
+
/**
|
|
450
|
+
* Maximum number of undoable entries kept (default `100`). When exceeded the
|
|
451
|
+
* **oldest** entries are dropped. Settable at any time; lowering it trims
|
|
452
|
+
* the stack immediately. Values below 1 are clamped to 1.
|
|
453
|
+
*/
|
|
454
|
+
get limit(): number;
|
|
455
|
+
set limit(value: number);
|
|
456
|
+
/**
|
|
457
|
+
* Record an already-performed action. Clears the redo branch and evicts the
|
|
458
|
+
* oldest entry beyond {@link limit}. Ignored while `undo()`/`redo()` is
|
|
459
|
+
* executing (see the re-entrancy note on the class) — inside a `batch()` the
|
|
460
|
+
* entry is collected into the composite instead.
|
|
461
|
+
*/
|
|
462
|
+
push(entry: MkHistoryEntry): void;
|
|
463
|
+
/** Undo the most recent entry. Returns `false` when there is nothing to undo. */
|
|
464
|
+
undo(): boolean;
|
|
465
|
+
/** Redo the most recently undone entry. Returns `false` when there is nothing to redo. */
|
|
466
|
+
redo(): boolean;
|
|
467
|
+
/** Drop all entries (both branches) and any in-flight batch collection. */
|
|
468
|
+
clear(): void;
|
|
469
|
+
/**
|
|
470
|
+
* Group every `push()` made during `work()` into **one** undoable step.
|
|
471
|
+
* Undo runs the children in reverse order; redo runs them forward. Nested
|
|
472
|
+
* `batch()` calls flatten into the outermost batch (its label wins). An
|
|
473
|
+
* empty batch records nothing. Returns `work()`'s result; if `work()`
|
|
474
|
+
* throws, entries pushed before the throw are still committed.
|
|
475
|
+
*/
|
|
476
|
+
batch<T>(label: string, work: () => T): T;
|
|
477
|
+
/**
|
|
478
|
+
* Create an **independent** stack with the same API — e.g. for a dialog's
|
|
479
|
+
* local editing session that should not pollute the app-wide history.
|
|
480
|
+
* Discard it (or `clear()` it) when the session ends.
|
|
481
|
+
*/
|
|
482
|
+
createScope(): MkHistoryStack;
|
|
483
|
+
/** Append to the undo stack: clear the redo branch, evict beyond the limit. */
|
|
484
|
+
private commit;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* MkHistoryService — the app-wide {@link MkHistoryStack} singleton. Inject it
|
|
488
|
+
* anywhere; call {@link MkHistoryStack.createScope} for an isolated local
|
|
489
|
+
* stack, or wire `mod+z` / `mod+shift+z` / `mod+y` with
|
|
490
|
+
* {@link registerHistoryHotkeys}.
|
|
491
|
+
*
|
|
492
|
+
* ### Recipe — undoable `mk-table` cell edits
|
|
493
|
+
* Wire the table's `(cellEdit)` output so each saved cell becomes one
|
|
494
|
+
* undoable step. Apply the edit yourself first (`push()` records an action
|
|
495
|
+
* that already happened), and match rows by a stable id — the row object is
|
|
496
|
+
* replaced by the immutable update:
|
|
497
|
+
*
|
|
498
|
+
* ```ts
|
|
499
|
+
* // <mk-table [rows]="rows()" [columns]="columns" (cellEdit)="onCellEdit($event)" />
|
|
500
|
+
* private readonly history = inject(MkHistoryService);
|
|
501
|
+
* readonly rows = signal<Order[]>([...]);
|
|
502
|
+
*
|
|
503
|
+
* onCellEdit(edit: MkCellEdit<Order>): void {
|
|
504
|
+
* const previous = edit.row[edit.key as keyof Order];
|
|
505
|
+
* const apply = (value: unknown) =>
|
|
506
|
+
* this.rows.update((rows) =>
|
|
507
|
+
* rows.map((r) => (r.id === edit.row.id ? { ...r, [edit.key]: value } : r)),
|
|
508
|
+
* );
|
|
509
|
+
* apply(edit.value); // first execution is the caller's job
|
|
510
|
+
* this.history.push({
|
|
511
|
+
* label: `Edit ${edit.key}`,
|
|
512
|
+
* undo: () => apply(previous),
|
|
513
|
+
* redo: () => apply(edit.value),
|
|
514
|
+
* });
|
|
515
|
+
* }
|
|
516
|
+
* ```
|
|
517
|
+
*
|
|
518
|
+
* Undoing replays the old value through the same `apply` function; if your
|
|
519
|
+
* change pipeline re-emits into `onCellEdit` during that replay, the
|
|
520
|
+
* re-entrancy guard drops the spurious `push()` (see {@link MkHistoryStack}).
|
|
521
|
+
*/
|
|
522
|
+
declare class MkHistoryService extends MkHistoryStack {
|
|
523
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkHistoryService, never>;
|
|
524
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<MkHistoryService>;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Opt-in helper wiring the standard editor shortcuts to a history stack
|
|
529
|
+
* through {@link MkHotkeysService}:
|
|
530
|
+
*
|
|
531
|
+
* - `mod+z` → `undo()`
|
|
532
|
+
* - `mod+shift+z` **and** `mod+y` → `redo()`
|
|
533
|
+
*
|
|
534
|
+
* Must be called in an **injection context** (constructor, field initializer,
|
|
535
|
+
* or `runInInjectionContext`). Nothing is registered automatically — apps own
|
|
536
|
+
* their shortcuts and call this where (and if) they want them:
|
|
537
|
+
*
|
|
538
|
+
* ```ts
|
|
539
|
+
* export class AppShell {
|
|
540
|
+
* private readonly disposeHistoryKeys = registerHistoryHotkeys();
|
|
541
|
+
* }
|
|
542
|
+
* ```
|
|
543
|
+
*
|
|
544
|
+
* Pass a specific stack (e.g. a `createScope()` stack for a dialog) to bind
|
|
545
|
+
* the shortcuts to it instead of the app-wide {@link MkHistoryService}.
|
|
546
|
+
*
|
|
547
|
+
* Returns a dispose function that unregisters all three shortcuts; it is also
|
|
548
|
+
* called automatically when the current injection context is destroyed.
|
|
549
|
+
*
|
|
550
|
+
* Notes:
|
|
551
|
+
* - `MkHotkeysService` ignores keydowns while an editable element
|
|
552
|
+
* (input/textarea/select/contentEditable) is focused, so native text-field
|
|
553
|
+
* undo keeps working — no extra handling needed here.
|
|
554
|
+
* - The shortcuts register with `preventDefault` so the browser's own
|
|
555
|
+
* history/document undo does not fire alongside yours.
|
|
556
|
+
*/
|
|
557
|
+
declare function registerHistoryHotkeys(history?: MkHistoryStack): () => void;
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* A single hotkey combo parsed into a normalized key + modifier flags.
|
|
561
|
+
* Produced by {@link mkParseHotkey}; consumed by {@link mkMatchesHotkey}.
|
|
562
|
+
*/
|
|
563
|
+
interface MkParsedHotkey {
|
|
564
|
+
/** The non-modifier key, lowercased and alias-normalized (e.g. `k`, `escape`, `?`). */
|
|
565
|
+
key: string;
|
|
566
|
+
/** `mod` — Cmd on macOS, Ctrl elsewhere. */
|
|
567
|
+
mod: boolean;
|
|
568
|
+
/** The Control key. */
|
|
569
|
+
ctrl: boolean;
|
|
570
|
+
/** The Meta / Command / Windows key. */
|
|
571
|
+
meta: boolean;
|
|
572
|
+
/** The Alt / Option key. */
|
|
573
|
+
alt: boolean;
|
|
574
|
+
/** The Shift key. */
|
|
575
|
+
shift: boolean;
|
|
576
|
+
}
|
|
577
|
+
/** Options accepted by {@link MkHotkeysService.register}. */
|
|
578
|
+
interface MkHotkeyOptions {
|
|
579
|
+
/** Call `preventDefault()` on the event when the hotkey fires. Default `false`. */
|
|
580
|
+
preventDefault?: boolean;
|
|
581
|
+
/** Also fire while an editable field (input/textarea/select/contentEditable) is focused. Default `false`. */
|
|
582
|
+
allowInInput?: boolean;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Parse a single hotkey combo (e.g. `'mod+k'`, `'ctrl+shift+p'`, `'?'`) into a
|
|
586
|
+
* structured {@link MkParsedHotkey}. Tokens are joined by `+`; the last
|
|
587
|
+
* non-modifier token is the trigger key. Pure and SSR-safe.
|
|
588
|
+
*
|
|
589
|
+
* For a chord (space-separated sequence like `'g i'`), split on whitespace and
|
|
590
|
+
* parse each step — {@link MkHotkeysService} does this for you.
|
|
591
|
+
*/
|
|
592
|
+
declare function mkParseHotkey(combo: string): MkParsedHotkey;
|
|
593
|
+
/**
|
|
594
|
+
* Does a `keydown` event satisfy a hotkey? `combo` may be a raw string (parsed
|
|
595
|
+
* on the fly) or an already-parsed {@link MkParsedHotkey}.
|
|
596
|
+
*
|
|
597
|
+
* The key is compared case-insensitively; modifier flags must match exactly.
|
|
598
|
+
* `mod` resolves to Meta on macOS and Ctrl elsewhere. Shift is not enforced for
|
|
599
|
+
* symbol keys (e.g. `'?'`), whose shifted state varies by keyboard layout.
|
|
600
|
+
*/
|
|
601
|
+
declare function mkMatchesHotkey(event: KeyboardEvent, combo: string | MkParsedHotkey): boolean;
|
|
602
|
+
/**
|
|
603
|
+
* MkHotkeysService — app-wide keyboard shortcuts. Register a combo string and a
|
|
604
|
+
* handler; a single `keydown` listener (attached lazily on the first
|
|
605
|
+
* registration, browser only) dispatches to every match.
|
|
606
|
+
*
|
|
607
|
+
* Supports single combos (`'mod+k'`, `'ctrl+shift+p'`, `'?'`) and simple
|
|
608
|
+
* two-step chords (`'g i'`). By default hotkeys are ignored while an editable
|
|
609
|
+
* field is focused — pass `allowInInput` to opt in per registration.
|
|
610
|
+
*
|
|
611
|
+
* ```ts
|
|
612
|
+
* const off = hotkeys.register('mod+k', (e) => openPalette(), { preventDefault: true });
|
|
613
|
+
* // …later
|
|
614
|
+
* off();
|
|
615
|
+
* ```
|
|
616
|
+
*/
|
|
617
|
+
declare class MkHotkeysService {
|
|
618
|
+
private readonly document;
|
|
619
|
+
private readonly isBrowser;
|
|
620
|
+
private readonly registrations;
|
|
621
|
+
private attached;
|
|
622
|
+
/** The first key of an in-progress chord, awaiting its second key. */
|
|
623
|
+
private pendingEvent;
|
|
624
|
+
private pendingTimer?;
|
|
625
|
+
/**
|
|
626
|
+
* Register `combo` → `handler`. Returns a disposer that unregisters it.
|
|
627
|
+
* `combo` may be a single combo or a space-separated two-step chord.
|
|
628
|
+
*/
|
|
629
|
+
register(combo: string, handler: (e: KeyboardEvent) => void, options?: MkHotkeyOptions): () => void;
|
|
630
|
+
/** Remove every registration and detach the listener. */
|
|
631
|
+
unregisterAll(): void;
|
|
632
|
+
private ensureAttached;
|
|
633
|
+
private detach;
|
|
634
|
+
private readonly onKeydown;
|
|
635
|
+
private trigger;
|
|
636
|
+
private clearPending;
|
|
637
|
+
private isEditable;
|
|
638
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkHotkeysService, never>;
|
|
639
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<MkHotkeysService>;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* `[mkHotkey]` — declaratively bind a keyboard shortcut to a host element.
|
|
644
|
+
*
|
|
645
|
+
* When the combo fires, a `<button>` or `<a>` host is `click()`ed; any other
|
|
646
|
+
* host emits `(mkHotkeyPressed)`. Registration is scoped to the element's
|
|
647
|
+
* lifetime (auto-unregistered on destroy) via {@link MkHotkeysService}.
|
|
648
|
+
*
|
|
649
|
+
* ```html
|
|
650
|
+
* <button mkHotkey="mod+s" (click)="save()">Save</button>
|
|
651
|
+
* <div mkHotkey="?" (mkHotkeyPressed)="showHelp()">…</div>
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
654
|
+
declare class MkHotkey {
|
|
655
|
+
private readonly host;
|
|
656
|
+
private readonly hotkeys;
|
|
657
|
+
/** The combo (or space-separated chord) to bind, e.g. `mod+k`, `g i`, `?`. */
|
|
658
|
+
readonly mkHotkey: _angular_core.InputSignal<string>;
|
|
659
|
+
/** Fire even while an editable field is focused. */
|
|
660
|
+
readonly mkHotkeyAllowInInput: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
661
|
+
/** Call `preventDefault()` when the hotkey fires. Defaults to `true`. */
|
|
662
|
+
readonly mkHotkeyPreventDefault: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
663
|
+
/** Emitted when the hotkey fires and the host is not a button/link. */
|
|
664
|
+
readonly mkHotkeyPressed: _angular_core.OutputEmitterRef<KeyboardEvent>;
|
|
665
|
+
/**
|
|
666
|
+
* The combo in `aria-keyshortcuts` syntax — per-step tokens `+`-joined
|
|
667
|
+
* (`Control+K`), chord steps space-separated (`G I`). Exposed on the host.
|
|
668
|
+
*/
|
|
669
|
+
protected readonly ariaKeyshortcuts: _angular_core.Signal<string | null>;
|
|
670
|
+
private dispose?;
|
|
671
|
+
constructor();
|
|
672
|
+
private onTrigger;
|
|
673
|
+
ngOnDestroy(): void;
|
|
674
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkHotkey, never>;
|
|
675
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkHotkey, "[mkHotkey]", ["mkHotkey"], { "mkHotkey": { "alias": "mkHotkey"; "required": true; "isSignal": true; }; "mkHotkeyAllowInInput": { "alias": "mkHotkeyAllowInInput"; "required": false; "isSignal": true; }; "mkHotkeyPreventDefault": { "alias": "mkHotkeyPreventDefault"; "required": false; "isSignal": true; }; }, { "mkHotkeyPressed": "mkHotkeyPressed"; }, never, never, true, never>;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Permission policy — the contract the permission directives (`*mkCan`,
|
|
680
|
+
* `*mkCannot`, `[mkCanDisable]`) check against. Provide an implementation
|
|
681
|
+
* once at bootstrap; the directives inject it optionally and, when no policy
|
|
682
|
+
* is provided at all, treat every permission as **granted** — an app that
|
|
683
|
+
* never wires permissions must not have its whole UI hidden.
|
|
684
|
+
*
|
|
685
|
+
* `can` may return a plain `boolean` (static role checks) or a
|
|
686
|
+
* `Signal<boolean>` (live permissions). The directives normalise both, and a
|
|
687
|
+
* signal-returning policy re-evaluates reactively: views appear/disappear and
|
|
688
|
+
* controls enable/disable the moment the underlying permissions change.
|
|
689
|
+
*
|
|
690
|
+
* ```ts
|
|
691
|
+
* // A signal-based policy fed from an auth service:
|
|
692
|
+
* @Injectable()
|
|
693
|
+
* export class AppPermissionPolicy extends MkPermissionPolicy {
|
|
694
|
+
* private readonly auth = inject(AuthService);
|
|
695
|
+
*
|
|
696
|
+
* can(permission: string): Signal<boolean> {
|
|
697
|
+
* // auth.permissions is a Signal<string[]> that updates on login/logout.
|
|
698
|
+
* return computed(() => this.auth.permissions().includes(permission));
|
|
699
|
+
* }
|
|
700
|
+
* }
|
|
701
|
+
*
|
|
702
|
+
* bootstrapApplication(App, {
|
|
703
|
+
* providers: [
|
|
704
|
+
* { provide: MkPermissionPolicy, useClass: AppPermissionPolicy },
|
|
705
|
+
* ],
|
|
706
|
+
* });
|
|
707
|
+
* ```
|
|
708
|
+
*
|
|
709
|
+
* A static policy is just as valid:
|
|
710
|
+
*
|
|
711
|
+
* ```ts
|
|
712
|
+
* class StaticPolicy extends MkPermissionPolicy {
|
|
713
|
+
* can(permission: string): boolean {
|
|
714
|
+
* return CURRENT_USER.permissions.includes(permission);
|
|
715
|
+
* }
|
|
716
|
+
* }
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
declare abstract class MkPermissionPolicy {
|
|
720
|
+
/**
|
|
721
|
+
* Whether the current user holds `permission`. Return a `Signal<boolean>`
|
|
722
|
+
* to make the verdict live; a plain boolean is treated as static.
|
|
723
|
+
*/
|
|
724
|
+
abstract can(permission: string): boolean | Signal<boolean>;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Resolve a policy verdict to a boolean, unwrapping signal-based results.
|
|
728
|
+
* Call it inside a reactive context (`computed` / `effect`) so a
|
|
729
|
+
* signal-returning policy stays live. A missing policy grants everything.
|
|
730
|
+
*/
|
|
731
|
+
declare function mkPermissionGranted(policy: MkPermissionPolicy | null, permission: string): boolean;
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Structural directive that renders its template only while the app's
|
|
735
|
+
* {@link MkPermissionPolicy} grants the given permission. With a
|
|
736
|
+
* signal-based policy the check is live — the view is created and destroyed
|
|
737
|
+
* as permissions change. When no policy is provided, everything is granted.
|
|
738
|
+
*
|
|
739
|
+
* ```html
|
|
740
|
+
* <button *mkCan="'posts.delete'" (click)="remove()">Delete</button>
|
|
741
|
+
*
|
|
742
|
+
* <!-- With a fallback template, like *ngIf's else: -->
|
|
743
|
+
* <section *mkCan="'billing.view'; else locked">…</section>
|
|
744
|
+
* <ng-template #locked><mk-empty-state title="No access" /></ng-template>
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
declare class MkCan {
|
|
748
|
+
/** The permission key to check, e.g. `'posts.delete'`. */
|
|
749
|
+
readonly permission: _angular_core.InputSignal<string>;
|
|
750
|
+
/** Template rendered while the permission is denied (`; else ref`). */
|
|
751
|
+
readonly elseTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
752
|
+
constructor();
|
|
753
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkCan, never>;
|
|
754
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkCan, "[mkCan]", never, { "permission": { "alias": "mkCan"; "required": true; "isSignal": true; }; "elseTemplate": { "alias": "mkCanElse"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* The negation of {@link MkCan}: renders its template only while the
|
|
758
|
+
* permission is **denied** — for upsell hints, "ask an admin" notes and the
|
|
759
|
+
* like. With no policy provided everything is granted, so `*mkCannot`
|
|
760
|
+
* renders nothing.
|
|
761
|
+
*
|
|
762
|
+
* ```html
|
|
763
|
+
* <p *mkCannot="'reports.export'">Exporting requires an admin role.</p>
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
766
|
+
declare class MkCannot {
|
|
767
|
+
/** The permission key to check. */
|
|
768
|
+
readonly permission: _angular_core.InputSignal<string>;
|
|
769
|
+
/** Template rendered while the permission is granted (`; else ref`). */
|
|
770
|
+
readonly elseTemplate: _angular_core.InputSignal<TemplateRef<unknown> | null>;
|
|
771
|
+
constructor();
|
|
772
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkCannot, never>;
|
|
773
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkCannot, "[mkCannot]", never, { "permission": { "alias": "mkCannot"; "required": true; "isSignal": true; }; "elseTemplate": { "alias": "mkCannotElse"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Disables the host while the app's {@link MkPermissionPolicy} denies the
|
|
778
|
+
* given permission — the "keep it visible but inert" alternative to `*mkCan`
|
|
779
|
+
* for buttons, inputs and menu items.
|
|
780
|
+
*
|
|
781
|
+
* While denied, the host gets `aria-disabled="true"`, and — when the element
|
|
782
|
+
* supports it (button, input, select, textarea, fieldset…) — its native
|
|
783
|
+
* `disabled` property is set too. Non-disableable elements (links, custom
|
|
784
|
+
* menu items) get `aria-disabled` only; pair it with CSS / a click guard as
|
|
785
|
+
* needed. The directive owns the disabled state: it also re-enables the host
|
|
786
|
+
* when the permission is granted. With a signal-based policy the state flips
|
|
787
|
+
* live; with no policy provided, everything is granted.
|
|
788
|
+
*
|
|
789
|
+
* ```html
|
|
790
|
+
* <button [mkCanDisable]="'posts.delete'" (click)="remove()">Delete</button>
|
|
791
|
+
* <a [mkCanDisable]="'billing.view'" routerLink="/billing">Billing</a>
|
|
792
|
+
* ```
|
|
793
|
+
*/
|
|
794
|
+
declare class MkCanDisable {
|
|
795
|
+
private readonly policy;
|
|
796
|
+
private readonly host;
|
|
797
|
+
/** The permission key to check, e.g. `'posts.delete'`. */
|
|
798
|
+
readonly permission: _angular_core.InputSignal<string>;
|
|
799
|
+
/** Whether the permission is currently denied. */
|
|
800
|
+
protected readonly denied: _angular_core.Signal<boolean>;
|
|
801
|
+
constructor();
|
|
802
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkCanDisable, never>;
|
|
803
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkCanDisable, "[mkCanDisable]", never, { "permission": { "alias": "mkCanDisable"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
export { MK_FIELD_PRESETS, MkAutofocus, MkAutosize, MkCan, MkCanDisable, MkCannot, MkClickOutside, MkCopyToClipboard, MkField, MkHistoryService, MkHistoryStack, MkHotkey, MkHotkeysService, MkInfiniteScroll, MkIntersect, MkMask, MkPermissionPolicy, MkRipple, MkScrollspy, mkApplyMask, mkMaskCaret, mkMatchesHotkey, mkParseHotkey, mkPermissionGranted, registerHistoryHotkeys };
|
|
807
|
+
export type { MkFieldKind, MkFieldPreset, MkHistoryEntry, MkHotkeyOptions, MkMaskResult, MkParsedHotkey };
|