@mk-kit/ui 0.41.0 → 0.43.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 +4 -4
- package/THIRD_PARTY_NOTICES.md +4 -2
- package/fesm2022/mk-kit-ui-block-editor.mjs +4 -2
- package/fesm2022/mk-kit-ui-block-editor.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-checkbox.mjs +4 -2
- package/fesm2022/mk-kit-ui-checkbox.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-core.mjs +218 -1
- package/fesm2022/mk-kit-ui-core.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-data.mjs +801 -16
- package/fesm2022/mk-kit-ui-data.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-datetime.mjs +90 -52
- package/fesm2022/mk-kit-ui-datetime.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-directives.mjs +395 -2
- package/fesm2022/mk-kit-ui-directives.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-dnd.mjs +183 -29
- package/fesm2022/mk-kit-ui-dnd.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-dynamic-form.mjs +149 -2
- package/fesm2022/mk-kit-ui-dynamic-form.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-feedback.mjs +1 -1
- package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-forms.mjs +331 -81
- package/fesm2022/mk-kit-ui-forms.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-icon-extended.mjs +425 -0
- package/fesm2022/mk-kit-ui-icon-extended.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-icon.mjs +296 -459
- package/fesm2022/mk-kit-ui-icon.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-query-builder.mjs +1 -1
- package/fesm2022/mk-kit-ui-query-builder.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-rich-text.mjs +4 -2
- package/fesm2022/mk-kit-ui-rich-text.mjs.map +1 -1
- package/fesm2022/mk-kit-ui-table.mjs +36 -8
- package/fesm2022/mk-kit-ui-table.mjs.map +1 -1
- package/fesm2022/mk-kit-ui.mjs +1 -0
- package/fesm2022/mk-kit-ui.mjs.map +1 -1
- package/package.json +5 -1
- package/schematics/migrate-primeng/transform.js +4 -1
- package/schematics/ng-add/index.js +30 -2
- package/schematics/ng-add/schema.json +6 -0
- package/styles/mk-kit.css +604 -0
- package/types/mk-kit-ui-block-editor.d.ts +2 -0
- package/types/mk-kit-ui-checkbox.d.ts +2 -0
- package/types/mk-kit-ui-core.d.ts +119 -2
- package/types/mk-kit-ui-data.d.ts +359 -3
- package/types/mk-kit-ui-datetime.d.ts +41 -19
- package/types/mk-kit-ui-directives.d.ts +234 -3
- package/types/mk-kit-ui-dnd.d.ts +96 -14
- package/types/mk-kit-ui-dynamic-form.d.ts +44 -1
- package/types/mk-kit-ui-forms.d.ts +160 -20
- package/types/mk-kit-ui-icon-extended.d.ts +83 -0
- package/types/mk-kit-ui-icon.d.ts +100 -17
- package/types/mk-kit-ui-rich-text.d.ts +2 -0
- package/types/mk-kit-ui-table.d.ts +38 -21
- package/types/mk-kit-ui.d.ts +1 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, ChangeDetectionStrategy, Component, booleanAttribute,
|
|
2
|
+
import { input, ChangeDetectionStrategy, Component, booleanAttribute, inject, ElementRef, Renderer2, DOCUMENT, PLATFORM_ID, numberAttribute, computed, effect, Directive, signal, contentChildren, output, TemplateRef, model, contentChild, linkedSignal, DestroyRef, afterNextRender, afterRenderEffect, untracked, Injector, viewChild, forwardRef, ViewEncapsulation } from '@angular/core';
|
|
3
|
+
import { isPlatformBrowser, NgTemplateOutlet, DOCUMENT as DOCUMENT$1 } from '@angular/common';
|
|
4
|
+
import { mkUniqueId, MK_I18N, MkFieldContext, mkInjectFieldTouched, mkHighlight } from '@mk-kit/ui/core';
|
|
3
5
|
import { MkIcon } from '@mk-kit/ui/icon';
|
|
4
|
-
import { mkUniqueId, MK_I18N, MkFieldContext, mkHighlight } from '@mk-kit/ui/core';
|
|
5
|
-
import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
6
6
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
7
7
|
import { MkCopyToClipboard } from '@mk-kit/ui/directives';
|
|
8
8
|
import { mkMoveItemInArray, MkDropList, MkDrag } from '@mk-kit/ui/dnd';
|
|
@@ -151,6 +151,186 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
151
151
|
}, template: "@if (!dot()) {\n <ng-content />\n}\n", styles: ["@charset \"UTF-8\";:host{--_main: var(--mk-primary);--_contrast: var(--mk-primary-contrast);--_subtle: var(--mk-primary-subtle);--_subtle-text: var(--mk-primary-subtle-text);display:inline-flex;align-items:center;justify-content:center;gap:var(--mk-space-1);height:1.375rem;min-width:1.375rem;padding:0 var(--mk-space-2);border:var(--mk-border-width) solid transparent;border-radius:var(--mk-radius-pill);font-family:inherit;font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-medium);line-height:1;white-space:nowrap;vertical-align:middle}:host([data-tone=primary]){--_main: var(--mk-primary);--_hover: var(--mk-primary-hover);--_active: var(--mk-primary-active);--_contrast: var(--mk-primary-contrast);--_subtle: var(--mk-primary-subtle);--_subtle-text: var(--mk-primary-subtle-text)}:host([data-tone=success]){--_main: var(--mk-success);--_hover: var(--mk-success-hover);--_active: var(--mk-success-hover);--_contrast: var(--mk-success-contrast);--_subtle: var(--mk-success-subtle);--_subtle-text: var(--mk-success-subtle-text)}:host([data-tone=warning]){--_main: var(--mk-warning);--_hover: var(--mk-warning-hover);--_active: var(--mk-warning-hover);--_contrast: var(--mk-warning-contrast);--_subtle: var(--mk-warning-subtle);--_subtle-text: var(--mk-warning-subtle-text)}:host([data-tone=danger]){--_main: var(--mk-danger);--_hover: var(--mk-danger-hover);--_active: var(--mk-danger-hover);--_contrast: var(--mk-danger-contrast);--_subtle: var(--mk-danger-subtle);--_subtle-text: var(--mk-danger-subtle-text)}:host([data-tone=info]){--_main: var(--mk-info);--_hover: var(--mk-info-hover);--_active: var(--mk-info-hover);--_contrast: var(--mk-info-contrast);--_subtle: var(--mk-info-subtle);--_subtle-text: var(--mk-info-subtle-text)}:host([data-tone=neutral]){--_main: var(--mk-surface-inverse);--_hover: var(--mk-surface-inverse);--_active: var(--mk-surface-inverse);--_contrast: var(--mk-text-inverse);--_subtle: var(--mk-neutral-subtle);--_subtle-text: var(--mk-neutral-subtle-text)}:host(.mk-badge--sm){height:1.125rem;min-width:1.125rem;padding:0 var(--mk-space-1);font-size:var(--mk-font-size-xs)}:host(.mk-badge--lg){height:1.625rem;min-width:1.625rem;padding:0 var(--mk-space-3);font-size:var(--mk-font-size-sm)}:host(.mk-badge--solid){background-color:var(--_main);color:var(--_contrast)}:host(.mk-badge--soft){background-color:var(--_subtle);color:var(--_subtle-text)}:host(.mk-badge--outline){background-color:transparent;color:var(--_subtle-text);border-color:color-mix(in srgb,var(--_main) 42%,var(--mk-border-strong))}:host(.mk-badge--dot){width:.625rem;height:.625rem;min-width:0;padding:0;border-radius:var(--mk-radius-circle)}:host(.mk-badge--dot.mk-badge--sm){width:.5rem;height:.5rem}:host(.mk-badge--dot.mk-badge--lg){width:.75rem;height:.75rem}\n"] }]
|
|
152
152
|
}], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }] } });
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Badge overlay — anchors a small badge (count, dot or short text) to a corner
|
|
156
|
+
* of ANY element: an icon button, an avatar, a tab label. The host stays a
|
|
157
|
+
* single tab stop; the badge is a decorative, non-focusable child.
|
|
158
|
+
*
|
|
159
|
+
* - Numbers above `mkBadgeOverlayMax` (default 99) collapse to `99+`.
|
|
160
|
+
* - `mkBadgeOverlayDot` renders a textless dot ("something is new").
|
|
161
|
+
* - `mkBadgeOverlayAriaLabel` is the screen-reader text ("3 unread"). It is
|
|
162
|
+
* rendered in a visually hidden span and wired to the host with
|
|
163
|
+
* `aria-describedby`, so it is announced after the host's own name even
|
|
164
|
+
* when the host has an `aria-label`. When it is given, the visible badge is
|
|
165
|
+
* `aria-hidden` so the count is not read twice.
|
|
166
|
+
* - Positions use logical insets, so `top-end` sits at the top-right in LTR
|
|
167
|
+
* and the top-left in RTL.
|
|
168
|
+
*
|
|
169
|
+
* The badge is painted with the global `.mk-badge-overlay` styles from
|
|
170
|
+
* `@mk-kit/ui/styles.css` and follows the `--mk-*` tone tokens.
|
|
171
|
+
*
|
|
172
|
+
* ```html
|
|
173
|
+
* <button mkButton iconOnly aria-label="Notifications"
|
|
174
|
+
* [mkBadgeOverlay]="unread()" mkBadgeOverlayTone="danger"
|
|
175
|
+
* [mkBadgeOverlayAriaLabel]="unread() + ' unread'">
|
|
176
|
+
* <mk-icon name="bell" />
|
|
177
|
+
* </button>
|
|
178
|
+
*
|
|
179
|
+
* <mk-avatar name="Ada Lovelace" mkBadgeOverlay mkBadgeOverlayDot
|
|
180
|
+
* mkBadgeOverlayTone="success" mkBadgeOverlayPosition="bottom-end" />
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
class MkBadgeOverlay {
|
|
184
|
+
host = inject(ElementRef);
|
|
185
|
+
renderer = inject(Renderer2);
|
|
186
|
+
document = inject(DOCUMENT);
|
|
187
|
+
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
188
|
+
/**
|
|
189
|
+
* Badge content — a count or a short label. `null` / empty hides the badge
|
|
190
|
+
* unless `mkBadgeOverlayDot` is set. Numeric strings are treated as numbers
|
|
191
|
+
* so `mkBadgeOverlay="120"` also collapses to `99+`.
|
|
192
|
+
*/
|
|
193
|
+
content = input(null, { ...(ngDevMode ? { debugName: "content" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlay' });
|
|
194
|
+
/** Corner the badge is anchored to (logical: `end` flips in RTL). */
|
|
195
|
+
position = input('top-end', { ...(ngDevMode ? { debugName: "position" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayPosition' });
|
|
196
|
+
/** Semantic color tone (same scale as `mk-badge`). */
|
|
197
|
+
tone = input('primary', { ...(ngDevMode ? { debugName: "tone" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayTone' });
|
|
198
|
+
/** Render a textless dot instead of the content. */
|
|
199
|
+
dot = input(false, { ...(ngDevMode ? { debugName: "dot" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayDot',
|
|
200
|
+
transform: booleanAttribute });
|
|
201
|
+
/** Numbers above this render as `<max>+`. `0` disables the cap. */
|
|
202
|
+
max = input(99, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayMax',
|
|
203
|
+
transform: numberAttribute });
|
|
204
|
+
/** Hide the badge while keeping the directive (and its aria wiring) in place. */
|
|
205
|
+
hidden = input(false, { ...(ngDevMode ? { debugName: "hidden" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayHidden',
|
|
206
|
+
transform: booleanAttribute });
|
|
207
|
+
/**
|
|
208
|
+
* Screen-reader text for the badge, e.g. `"3 unread"`. Announced as the
|
|
209
|
+
* host's description; the visible badge becomes `aria-hidden`.
|
|
210
|
+
*/
|
|
211
|
+
ariaLabel = input('', { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'mkBadgeOverlayAriaLabel' });
|
|
212
|
+
/** The text painted in the badge (`''` in dot mode). */
|
|
213
|
+
label = computed(() => {
|
|
214
|
+
if (this.dot())
|
|
215
|
+
return '';
|
|
216
|
+
const raw = this.content();
|
|
217
|
+
if (raw === null || raw === undefined || raw === '')
|
|
218
|
+
return '';
|
|
219
|
+
const max = this.max();
|
|
220
|
+
const n = typeof raw === 'number' ? raw : /^\d+$/.test(raw) ? Number(raw) : NaN;
|
|
221
|
+
if (Number.isFinite(n) && max > 0 && n > max)
|
|
222
|
+
return `${max}+`;
|
|
223
|
+
return String(raw);
|
|
224
|
+
}, /* @ts-ignore */
|
|
225
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
226
|
+
/** Whether the badge is currently rendered. */
|
|
227
|
+
visible = computed(() => !this.hidden() && (this.dot() || this.label() !== ''), /* @ts-ignore */
|
|
228
|
+
...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
229
|
+
srId = mkUniqueId('mk-badge-overlay');
|
|
230
|
+
badge = null;
|
|
231
|
+
sr = null;
|
|
232
|
+
/** Set when WE made the host positioned, so we can undo it on removal. */
|
|
233
|
+
positionedHost = false;
|
|
234
|
+
constructor() {
|
|
235
|
+
if (!this.isBrowser)
|
|
236
|
+
return;
|
|
237
|
+
effect(() => {
|
|
238
|
+
const visible = this.visible();
|
|
239
|
+
if (!visible) {
|
|
240
|
+
this.detach();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const badge = this.attach();
|
|
244
|
+
const dot = this.dot();
|
|
245
|
+
const srText = this.ariaLabel().trim();
|
|
246
|
+
this.renderer.setAttribute(badge, 'data-tone', this.tone());
|
|
247
|
+
this.renderer.setAttribute(badge, 'data-position', this.position());
|
|
248
|
+
if (dot) {
|
|
249
|
+
this.renderer.addClass(badge, 'mk-badge-overlay--dot');
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
this.renderer.removeClass(badge, 'mk-badge-overlay--dot');
|
|
253
|
+
}
|
|
254
|
+
this.renderer.setProperty(badge, 'textContent', this.label());
|
|
255
|
+
// A dot has nothing to read; a labelled badge is described via the
|
|
256
|
+
// hidden span instead — either way the visual is hidden from AT.
|
|
257
|
+
if (dot || srText) {
|
|
258
|
+
this.renderer.setAttribute(badge, 'aria-hidden', 'true');
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
this.renderer.removeAttribute(badge, 'aria-hidden');
|
|
262
|
+
}
|
|
263
|
+
this.syncSr(srText);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
attach() {
|
|
267
|
+
if (this.badge)
|
|
268
|
+
return this.badge;
|
|
269
|
+
const el = this.host.nativeElement;
|
|
270
|
+
const view = this.document.defaultView;
|
|
271
|
+
const position = view?.getComputedStyle(el).position;
|
|
272
|
+
if (!position || position === 'static') {
|
|
273
|
+
this.renderer.setStyle(el, 'position', 'relative');
|
|
274
|
+
this.positionedHost = true;
|
|
275
|
+
}
|
|
276
|
+
const badge = this.renderer.createElement('span');
|
|
277
|
+
this.renderer.addClass(badge, 'mk-badge-overlay');
|
|
278
|
+
this.renderer.appendChild(el, badge);
|
|
279
|
+
this.badge = badge;
|
|
280
|
+
return badge;
|
|
281
|
+
}
|
|
282
|
+
detach() {
|
|
283
|
+
if (this.badge) {
|
|
284
|
+
this.renderer.removeChild(this.host.nativeElement, this.badge);
|
|
285
|
+
this.badge = null;
|
|
286
|
+
}
|
|
287
|
+
this.syncSr('');
|
|
288
|
+
if (this.positionedHost) {
|
|
289
|
+
this.renderer.removeStyle(this.host.nativeElement, 'position');
|
|
290
|
+
this.positionedHost = false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/** Create / update / remove the hidden description and its aria-describedby link. */
|
|
294
|
+
syncSr(text) {
|
|
295
|
+
const el = this.host.nativeElement;
|
|
296
|
+
if (text) {
|
|
297
|
+
if (!this.sr) {
|
|
298
|
+
const sr = this.renderer.createElement('span');
|
|
299
|
+
this.renderer.addClass(sr, 'mk-visually-hidden');
|
|
300
|
+
this.renderer.setAttribute(sr, 'id', this.srId);
|
|
301
|
+
this.renderer.appendChild(el, sr);
|
|
302
|
+
this.sr = sr;
|
|
303
|
+
const ids = (el.getAttribute('aria-describedby') ?? '').split(/\s+/).filter(Boolean);
|
|
304
|
+
if (!ids.includes(this.srId))
|
|
305
|
+
ids.push(this.srId);
|
|
306
|
+
this.renderer.setAttribute(el, 'aria-describedby', ids.join(' '));
|
|
307
|
+
}
|
|
308
|
+
this.renderer.setProperty(this.sr, 'textContent', text);
|
|
309
|
+
}
|
|
310
|
+
else if (this.sr) {
|
|
311
|
+
this.renderer.removeChild(el, this.sr);
|
|
312
|
+
this.sr = null;
|
|
313
|
+
const ids = (el.getAttribute('aria-describedby') ?? '')
|
|
314
|
+
.split(/\s+/)
|
|
315
|
+
.filter((id) => id && id !== this.srId);
|
|
316
|
+
if (ids.length) {
|
|
317
|
+
this.renderer.setAttribute(el, 'aria-describedby', ids.join(' '));
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
this.renderer.removeAttribute(el, 'aria-describedby');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBadgeOverlay, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
325
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: MkBadgeOverlay, isStandalone: true, selector: "[mkBadgeOverlay]", inputs: { content: { classPropertyName: "content", publicName: "mkBadgeOverlay", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "mkBadgeOverlayPosition", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "mkBadgeOverlayTone", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "mkBadgeOverlayDot", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "mkBadgeOverlayMax", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "mkBadgeOverlayHidden", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "mkBadgeOverlayAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
326
|
+
}
|
|
327
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkBadgeOverlay, decorators: [{
|
|
328
|
+
type: Directive,
|
|
329
|
+
args: [{
|
|
330
|
+
selector: '[mkBadgeOverlay]',
|
|
331
|
+
}]
|
|
332
|
+
}], ctorParameters: () => [], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlay", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayPosition", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayTone", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayDot", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayMax", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayHidden", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkBadgeOverlayAriaLabel", required: false }] }] } });
|
|
333
|
+
|
|
154
334
|
/**
|
|
155
335
|
* Tag — a non-interactive label for categories, keywords or metadata. Unlike
|
|
156
336
|
* {@link MkChip} it carries no interaction; use it purely for display.
|
|
@@ -500,6 +680,434 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
500
680
|
}, template: "<div class=\"mk-timeline-item__marker\">\n <span class=\"mk-timeline-item__dot\">\n @if (icon()) {\n <mk-icon [name]=\"icon()\" [size]=\"14\" />\n }\n </span>\n <span class=\"mk-timeline-item__connector\" aria-hidden=\"true\"></span>\n</div>\n\n<div class=\"mk-timeline-item__content\">\n @if (time()) {\n <span class=\"mk-timeline-item__time\">{{ time() }}</span>\n }\n @if (heading()) {\n <p class=\"mk-timeline-item__heading\">{{ heading() }}</p>\n }\n <div class=\"mk-timeline-item__body\">\n <ng-content />\n </div>\n</div>\n", styles: [":host{--_dot: var(--mk-primary);--_marker: 1.75rem;display:flex;gap:var(--mk-space-3);font-family:var(--mk-font-sans);color:var(--mk-text)}:host([data-tone=primary]){--_dot: var(--mk-primary)}:host([data-tone=success]){--_dot: var(--mk-success)}:host([data-tone=warning]){--_dot: var(--mk-warning)}:host([data-tone=danger]){--_dot: var(--mk-danger)}:host([data-tone=info]){--_dot: var(--mk-info)}:host([data-tone=neutral]){--_dot: var(--mk-text-muted)}.mk-timeline-item__marker{position:relative;flex:none;display:flex;justify-content:center;width:var(--_marker)}.mk-timeline-item__dot{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;width:.75rem;height:.75rem;margin-top:.5rem;border-radius:var(--mk-radius-circle);background-color:var(--_dot);color:var(--mk-primary-contrast);box-shadow:0 0 0 3px var(--mk-surface)}:host(.mk-timeline-item--icon) .mk-timeline-item__dot{width:var(--_marker);height:var(--_marker);margin-top:0;color:var(--_dot);background-color:color-mix(in srgb,var(--_dot) 16%,var(--mk-surface));box-shadow:0 0 0 3px var(--mk-surface),inset 0 0 0 1.5px color-mix(in srgb,var(--_dot) 40%,transparent)}.mk-timeline-item__connector{position:absolute;top:var(--_marker);bottom:0;left:50%;width:2px;transform:translate(-50%);background-color:var(--mk-border)}:host(:last-child) .mk-timeline-item__connector{display:none}.mk-timeline-item__content{flex:1 1 auto;min-width:0;padding-bottom:var(--mk-space-5)}:host(:last-child) .mk-timeline-item__content{padding-bottom:0}.mk-timeline-item__time{display:block;font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-medium);color:var(--mk-text-subtle)}.mk-timeline-item__heading{margin:0;font-size:var(--mk-font-size-md);font-weight:var(--mk-font-weight-semibold);line-height:var(--mk-line-height-snug);color:var(--mk-text)}.mk-timeline-item__body{margin-top:var(--mk-space-1);font-size:var(--mk-font-size-md);line-height:var(--mk-line-height-normal);color:var(--mk-text-muted)}.mk-timeline-item__body:empty{display:none}\n"] }]
|
|
501
681
|
}], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], time: [{ type: i0.Input, args: [{ isSignal: true, alias: "time", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }] } });
|
|
502
682
|
|
|
683
|
+
/**
|
|
684
|
+
* Build the nested `nodes` input from a flat `parentId` list (the shape most
|
|
685
|
+
* HR / directory APIs return). Row order is preserved among siblings; rows
|
|
686
|
+
* whose `parentId` does not match any row become roots.
|
|
687
|
+
*
|
|
688
|
+
* ```ts
|
|
689
|
+
* const nodes = mkOrgChartFromFlat([
|
|
690
|
+
* { id: 'ceo', label: 'Ada' },
|
|
691
|
+
* { id: 'cto', label: 'Grace', parentId: 'ceo' },
|
|
692
|
+
* ]);
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
function mkOrgChartFromFlat(list) {
|
|
696
|
+
const byId = new Map();
|
|
697
|
+
for (const row of list) {
|
|
698
|
+
const node = { id: row.id };
|
|
699
|
+
if (row.label !== undefined)
|
|
700
|
+
node.label = row.label;
|
|
701
|
+
if (row.data !== undefined)
|
|
702
|
+
node.data = row.data;
|
|
703
|
+
if (row.expanded !== undefined)
|
|
704
|
+
node.expanded = row.expanded;
|
|
705
|
+
byId.set(row.id, node);
|
|
706
|
+
}
|
|
707
|
+
const roots = [];
|
|
708
|
+
for (const row of list) {
|
|
709
|
+
const node = byId.get(row.id);
|
|
710
|
+
const parent = row.parentId != null ? byId.get(row.parentId) : undefined;
|
|
711
|
+
if (parent && parent !== node)
|
|
712
|
+
(parent.children ??= []).push(node);
|
|
713
|
+
else
|
|
714
|
+
roots.push(node);
|
|
715
|
+
}
|
|
716
|
+
return roots;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Custom node card for `mk-org-chart`:
|
|
720
|
+
*
|
|
721
|
+
* ```html
|
|
722
|
+
* <mk-org-chart [nodes]="nodes">
|
|
723
|
+
* <ng-template mkOrgChartNodeDef let-node let-selected="selected">
|
|
724
|
+
* <strong>{{ node.label }}</strong> — {{ node.data?.title }}
|
|
725
|
+
* </ng-template>
|
|
726
|
+
* </mk-org-chart>
|
|
727
|
+
* ```
|
|
728
|
+
*
|
|
729
|
+
* Bind the same array you pass to `nodes` to get a typed `node.data` in the
|
|
730
|
+
* template: `<ng-template [mkOrgChartNodeDef]="nodes" let-node>`.
|
|
731
|
+
*/
|
|
732
|
+
class MkOrgChartNodeDef {
|
|
733
|
+
/**
|
|
734
|
+
* Type-inference hook only (the value is not used at runtime): bind the
|
|
735
|
+
* `nodes` array so `let-node` is typed as `MkOrgChartNode<T>`.
|
|
736
|
+
*/
|
|
737
|
+
mkOrgChartNodeDef = input(undefined, /* @ts-ignore */
|
|
738
|
+
...(ngDevMode ? [{ debugName: "mkOrgChartNodeDef" }] : /* istanbul ignore next */ []));
|
|
739
|
+
template = inject((TemplateRef));
|
|
740
|
+
static ngTemplateContextGuard(_dir, ctx) {
|
|
741
|
+
return true;
|
|
742
|
+
}
|
|
743
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkOrgChartNodeDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
744
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: MkOrgChartNodeDef, isStandalone: true, selector: "ng-template[mkOrgChartNodeDef]", inputs: { mkOrgChartNodeDef: { classPropertyName: "mkOrgChartNodeDef", publicName: "mkOrgChartNodeDef", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
745
|
+
}
|
|
746
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkOrgChartNodeDef, decorators: [{
|
|
747
|
+
type: Directive,
|
|
748
|
+
args: [{ selector: 'ng-template[mkOrgChartNodeDef]' }]
|
|
749
|
+
}], propDecorators: { mkOrgChartNodeDef: [{ type: i0.Input, args: [{ isSignal: true, alias: "mkOrgChartNodeDef", required: false }] }] } });
|
|
750
|
+
/**
|
|
751
|
+
* Organisation chart — a reporting-line hierarchy drawn with plain nested
|
|
752
|
+
* lists and CSS connectors (no canvas, prints and themes like everything
|
|
753
|
+
* else). Feed it a tree (`nodes`) or convert a flat `parentId` list with
|
|
754
|
+
* {@link mkOrgChartFromFlat}. Nodes render as a default card (avatar /
|
|
755
|
+
* initials, label, `data.title`) or through an `mkOrgChartNodeDef` template.
|
|
756
|
+
*
|
|
757
|
+
* Follows the ARIA tree pattern (`role="tree"` / `treeitem` / `group`, one
|
|
758
|
+
* roving tab stop). Keyboard (top-down; the axes swap for `orientation="left"`
|
|
759
|
+
* and in RTL): Down = first child (expands a collapsed node first), Up =
|
|
760
|
+
* parent, Left/Right = previous/next sibling, Home/End = first/last visible
|
|
761
|
+
* node, Enter/Space = select (or toggle when not selectable), `*` = expand
|
|
762
|
+
* all siblings, `+`/`-` = expand/collapse.
|
|
763
|
+
*
|
|
764
|
+
* ```html
|
|
765
|
+
* <mk-org-chart
|
|
766
|
+
* [nodes]="company"
|
|
767
|
+
* selectable
|
|
768
|
+
* [(selected)]="personId"
|
|
769
|
+
* collapsible
|
|
770
|
+
* (nodeClick)="open($event)"
|
|
771
|
+
* />
|
|
772
|
+
* ```
|
|
773
|
+
*/
|
|
774
|
+
class MkOrgChart {
|
|
775
|
+
host = inject(ElementRef);
|
|
776
|
+
document = inject(DOCUMENT);
|
|
777
|
+
/** Localised strings (override globally via `provideMkI18n`). */
|
|
778
|
+
i18n = inject(MK_I18N);
|
|
779
|
+
/** The hierarchy to render (roots first). */
|
|
780
|
+
nodes = input([], /* @ts-ignore */
|
|
781
|
+
...(ngDevMode ? [{ debugName: "nodes" }] : /* istanbul ignore next */ []));
|
|
782
|
+
/** Root at the top (children below) or at the inline start (children to the side). */
|
|
783
|
+
orientation = input('top', /* @ts-ignore */
|
|
784
|
+
...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
785
|
+
/** Allow choosing a node (adds selection styling + `aria-selected`). */
|
|
786
|
+
selectable = input(false, { ...(ngDevMode ? { debugName: "selectable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
787
|
+
/** Show a toggle on every parent so branches can be hidden. */
|
|
788
|
+
collapsible = input(false, { ...(ngDevMode ? { debugName: "collapsible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
789
|
+
/** Scale factor applied to the whole chart (clamped to 0.5–2). */
|
|
790
|
+
zoom = input(1, { ...(ngDevMode ? { debugName: "zoom" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
791
|
+
/** Accessible name for the tree. */
|
|
792
|
+
ariaLabel = input(this.i18n.orgChartLabel, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
|
|
793
|
+
/** Two-way selected node id (`null` = nothing selected). */
|
|
794
|
+
selected = model(null, /* @ts-ignore */
|
|
795
|
+
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
796
|
+
/**
|
|
797
|
+
* Two-way ids of the expanded parents. Leave unbound to let the chart
|
|
798
|
+
* manage expansion itself (seeded from each node's `expanded` flag).
|
|
799
|
+
*/
|
|
800
|
+
expanded = model(undefined, /* @ts-ignore */
|
|
801
|
+
...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
802
|
+
/** Emits the node whose card was clicked or activated with Enter/Space. */
|
|
803
|
+
nodeClick = output();
|
|
804
|
+
/** Emits whenever a node is expanded or collapsed. */
|
|
805
|
+
nodeToggle = output();
|
|
806
|
+
nodeDef = contentChild((MkOrgChartNodeDef), /* @ts-ignore */
|
|
807
|
+
...(ngDevMode ? [{ debugName: "nodeDef" }] : /* istanbul ignore next */ []));
|
|
808
|
+
zoomLevel = computed(() => {
|
|
809
|
+
const z = this.zoom();
|
|
810
|
+
return Number.isFinite(z) ? Math.min(2, Math.max(0.5, z)) : 1;
|
|
811
|
+
}, /* @ts-ignore */
|
|
812
|
+
...(ngDevMode ? [{ debugName: "zoomLevel" }] : /* istanbul ignore next */ []));
|
|
813
|
+
/**
|
|
814
|
+
* Expanded parent ids. Controlled by `expanded` when bound; otherwise kept
|
|
815
|
+
* locally — a node's `expanded` flag only seeds ids not present in the
|
|
816
|
+
* previous `nodes` value, so re-emitting the array never reverts branches
|
|
817
|
+
* the user has since toggled.
|
|
818
|
+
*/
|
|
819
|
+
expandedIds = linkedSignal({ ...(ngDevMode ? { debugName: "expandedIds" } : /* istanbul ignore next */ {}), source: () => ({ nodes: this.nodes(), expanded: this.expanded() }),
|
|
820
|
+
computation: ({ nodes, expanded }, previous) => {
|
|
821
|
+
if (expanded)
|
|
822
|
+
return new Set(expanded);
|
|
823
|
+
const prev = previous?.value;
|
|
824
|
+
const known = previous ? collectIds(previous.source.nodes) : null;
|
|
825
|
+
const next = new Set();
|
|
826
|
+
walk(nodes, (node) => {
|
|
827
|
+
if (!node.children?.length)
|
|
828
|
+
return;
|
|
829
|
+
const isKnown = known?.has(node.id) ?? false;
|
|
830
|
+
if (isKnown ? prev.has(node.id) : node.expanded !== false)
|
|
831
|
+
next.add(node.id);
|
|
832
|
+
});
|
|
833
|
+
return next;
|
|
834
|
+
} });
|
|
835
|
+
/** Id of the node holding the roving tab stop. */
|
|
836
|
+
activeId = signal(null, /* @ts-ignore */
|
|
837
|
+
...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
|
|
838
|
+
/** Visible nodes in DOM (pre-order) order. */
|
|
839
|
+
rows = computed(() => {
|
|
840
|
+
const collapsible = this.collapsible();
|
|
841
|
+
const expandedIds = this.expandedIds();
|
|
842
|
+
const out = [];
|
|
843
|
+
const visit = (list, depth, parentId) => {
|
|
844
|
+
const siblings = list.map((n) => n.id);
|
|
845
|
+
for (const node of list) {
|
|
846
|
+
const hasChildren = !!node.children?.length;
|
|
847
|
+
const expanded = hasChildren && (!collapsible || expandedIds.has(node.id));
|
|
848
|
+
out.push({ node, depth, parentId, siblings, hasChildren, expanded });
|
|
849
|
+
if (expanded)
|
|
850
|
+
visit(node.children, depth + 1, node.id);
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
visit(this.nodes(), 1, null);
|
|
854
|
+
return out;
|
|
855
|
+
}, /* @ts-ignore */
|
|
856
|
+
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
857
|
+
rowById = computed(() => new Map(this.rows().map((r) => [r.node.id, r])), /* @ts-ignore */
|
|
858
|
+
...(ngDevMode ? [{ debugName: "rowById" }] : /* istanbul ignore next */ []));
|
|
859
|
+
/** The single id with `tabindex="0"` (falls back to the first visible node). */
|
|
860
|
+
tabStopId = computed(() => {
|
|
861
|
+
const active = this.activeId();
|
|
862
|
+
if (active !== null && this.rowById().has(active))
|
|
863
|
+
return active;
|
|
864
|
+
return this.rows()[0]?.node.id ?? null;
|
|
865
|
+
}, /* @ts-ignore */
|
|
866
|
+
...(ngDevMode ? [{ debugName: "tabStopId" }] : /* istanbul ignore next */ []));
|
|
867
|
+
// ---- template helpers --------------------------------------------------
|
|
868
|
+
isExpanded(node) {
|
|
869
|
+
if (!node.children?.length)
|
|
870
|
+
return false;
|
|
871
|
+
return !this.collapsible() || this.expandedIds().has(node.id);
|
|
872
|
+
}
|
|
873
|
+
isSelected(node) {
|
|
874
|
+
return this.selectable() && this.selected() === node.id;
|
|
875
|
+
}
|
|
876
|
+
tabIndexFor(node) {
|
|
877
|
+
return this.tabStopId() === node.id ? 0 : -1;
|
|
878
|
+
}
|
|
879
|
+
toggleLabel(node) {
|
|
880
|
+
const tpl = this.isExpanded(node) ? this.i18n.orgChartCollapse : this.i18n.orgChartExpand;
|
|
881
|
+
return tpl.replace('{label}', node.label ?? node.id);
|
|
882
|
+
}
|
|
883
|
+
context(node, depth) {
|
|
884
|
+
return {
|
|
885
|
+
$implicit: node,
|
|
886
|
+
node,
|
|
887
|
+
depth,
|
|
888
|
+
expanded: this.isExpanded(node),
|
|
889
|
+
selected: this.isSelected(node),
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
/** `data.title` / `data.avatar` of the default card, read defensively. */
|
|
893
|
+
field(node, key) {
|
|
894
|
+
const data = node.data;
|
|
895
|
+
const value = data?.[key];
|
|
896
|
+
return typeof value === 'string' && value ? value : undefined;
|
|
897
|
+
}
|
|
898
|
+
// ---- state changes -----------------------------------------------------
|
|
899
|
+
/** Expand or collapse a parent (no-op for leaves / non-collapsible charts). */
|
|
900
|
+
toggle(node) {
|
|
901
|
+
this.setExpanded(node, !this.isExpanded(node));
|
|
902
|
+
}
|
|
903
|
+
/** Expand or collapse a parent explicitly. */
|
|
904
|
+
setExpanded(node, expanded) {
|
|
905
|
+
if (!this.collapsible() || !node.children?.length)
|
|
906
|
+
return;
|
|
907
|
+
if (this.expandedIds().has(node.id) === expanded)
|
|
908
|
+
return;
|
|
909
|
+
const next = new Set(this.expandedIds());
|
|
910
|
+
if (expanded)
|
|
911
|
+
next.add(node.id);
|
|
912
|
+
else
|
|
913
|
+
next.delete(node.id);
|
|
914
|
+
this.expandedIds.set(next);
|
|
915
|
+
this.expanded.set([...next]);
|
|
916
|
+
this.nodeToggle.emit({ node, expanded });
|
|
917
|
+
}
|
|
918
|
+
/** Select a node by reference (no-op unless `selectable`). */
|
|
919
|
+
select(node) {
|
|
920
|
+
if (!this.selectable())
|
|
921
|
+
return;
|
|
922
|
+
this.selected.set(node.id);
|
|
923
|
+
}
|
|
924
|
+
activate(node) {
|
|
925
|
+
this.activeId.set(node.id);
|
|
926
|
+
if (this.selectable())
|
|
927
|
+
this.select(node);
|
|
928
|
+
this.nodeClick.emit(node);
|
|
929
|
+
}
|
|
930
|
+
onCardClick(event, node) {
|
|
931
|
+
event.stopPropagation();
|
|
932
|
+
this.activate(node);
|
|
933
|
+
}
|
|
934
|
+
onToggleClick(event, node) {
|
|
935
|
+
event.stopPropagation();
|
|
936
|
+
this.activeId.set(node.id);
|
|
937
|
+
this.toggle(node);
|
|
938
|
+
this.focusItem(node.id);
|
|
939
|
+
}
|
|
940
|
+
/** Keeps the roving tab stop on whichever item last received focus. */
|
|
941
|
+
onFocusIn(event) {
|
|
942
|
+
const id = this.itemIdOf(event.target);
|
|
943
|
+
if (id !== null)
|
|
944
|
+
this.activeId.set(id);
|
|
945
|
+
}
|
|
946
|
+
onKeydown(event) {
|
|
947
|
+
if (!(event instanceof KeyboardEvent))
|
|
948
|
+
return;
|
|
949
|
+
const id = this.itemIdOf(event.target);
|
|
950
|
+
if (id === null)
|
|
951
|
+
return;
|
|
952
|
+
const row = this.rowById().get(id);
|
|
953
|
+
if (!row)
|
|
954
|
+
return;
|
|
955
|
+
const node = row.node;
|
|
956
|
+
const intent = this.intentOf(event.key);
|
|
957
|
+
switch (intent) {
|
|
958
|
+
case 'into':
|
|
959
|
+
event.preventDefault();
|
|
960
|
+
if (row.hasChildren && !row.expanded) {
|
|
961
|
+
this.setExpanded(node, true);
|
|
962
|
+
}
|
|
963
|
+
else if (row.expanded) {
|
|
964
|
+
this.focusItem(node.children[0].id);
|
|
965
|
+
}
|
|
966
|
+
return;
|
|
967
|
+
case 'parent':
|
|
968
|
+
event.preventDefault();
|
|
969
|
+
if (row.parentId !== null)
|
|
970
|
+
this.focusItem(row.parentId);
|
|
971
|
+
return;
|
|
972
|
+
case 'next':
|
|
973
|
+
case 'prev': {
|
|
974
|
+
event.preventDefault();
|
|
975
|
+
const i = row.siblings.indexOf(id);
|
|
976
|
+
const target = row.siblings[intent === 'next' ? i + 1 : i - 1];
|
|
977
|
+
if (target !== undefined)
|
|
978
|
+
this.focusItem(target);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
switch (event.key) {
|
|
983
|
+
case 'Home':
|
|
984
|
+
event.preventDefault();
|
|
985
|
+
this.focusItem(this.rows()[0]?.node.id ?? null);
|
|
986
|
+
break;
|
|
987
|
+
case 'End': {
|
|
988
|
+
event.preventDefault();
|
|
989
|
+
const rows = this.rows();
|
|
990
|
+
this.focusItem(rows[rows.length - 1]?.node.id ?? null);
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
993
|
+
case 'Enter':
|
|
994
|
+
case ' ':
|
|
995
|
+
event.preventDefault();
|
|
996
|
+
if (!this.selectable() && row.hasChildren && this.collapsible()) {
|
|
997
|
+
this.toggle(node);
|
|
998
|
+
}
|
|
999
|
+
this.activate(node);
|
|
1000
|
+
break;
|
|
1001
|
+
case '*': {
|
|
1002
|
+
event.preventDefault();
|
|
1003
|
+
const nodes = this.siblingsOf(row);
|
|
1004
|
+
for (const sibling of nodes)
|
|
1005
|
+
this.setExpanded(sibling, true);
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
case '+':
|
|
1009
|
+
case '=':
|
|
1010
|
+
event.preventDefault();
|
|
1011
|
+
this.setExpanded(node, true);
|
|
1012
|
+
break;
|
|
1013
|
+
case '-':
|
|
1014
|
+
event.preventDefault();
|
|
1015
|
+
this.setExpanded(node, false);
|
|
1016
|
+
break;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Map an arrow key to a tree direction. Top-down: Down = into, Up = parent,
|
|
1021
|
+
* Left/Right = siblings (mirrored in RTL). Left-to-right: Right = into,
|
|
1022
|
+
* Left = parent (mirrored in RTL), Up/Down = siblings.
|
|
1023
|
+
*/
|
|
1024
|
+
intentOf(key) {
|
|
1025
|
+
const rtl = this.isRtl();
|
|
1026
|
+
if (this.orientation() === 'left') {
|
|
1027
|
+
switch (key) {
|
|
1028
|
+
case 'ArrowRight':
|
|
1029
|
+
return rtl ? 'parent' : 'into';
|
|
1030
|
+
case 'ArrowLeft':
|
|
1031
|
+
return rtl ? 'into' : 'parent';
|
|
1032
|
+
case 'ArrowDown':
|
|
1033
|
+
return 'next';
|
|
1034
|
+
case 'ArrowUp':
|
|
1035
|
+
return 'prev';
|
|
1036
|
+
}
|
|
1037
|
+
return null;
|
|
1038
|
+
}
|
|
1039
|
+
switch (key) {
|
|
1040
|
+
case 'ArrowDown':
|
|
1041
|
+
return 'into';
|
|
1042
|
+
case 'ArrowUp':
|
|
1043
|
+
return 'parent';
|
|
1044
|
+
case 'ArrowRight':
|
|
1045
|
+
return rtl ? 'prev' : 'next';
|
|
1046
|
+
case 'ArrowLeft':
|
|
1047
|
+
return rtl ? 'next' : 'prev';
|
|
1048
|
+
}
|
|
1049
|
+
return null;
|
|
1050
|
+
}
|
|
1051
|
+
siblingsOf(row) {
|
|
1052
|
+
const list = row.parentId === null
|
|
1053
|
+
? this.nodes()
|
|
1054
|
+
: (this.rowById().get(row.parentId)?.node.children ?? []);
|
|
1055
|
+
return list.filter((n) => !!n.children?.length);
|
|
1056
|
+
}
|
|
1057
|
+
isRtl() {
|
|
1058
|
+
const el = this.host.nativeElement;
|
|
1059
|
+
const dir = el.closest('[dir]')?.getAttribute('dir');
|
|
1060
|
+
if (dir)
|
|
1061
|
+
return dir.toLowerCase() === 'rtl';
|
|
1062
|
+
const view = this.document.defaultView;
|
|
1063
|
+
return (view?.getComputedStyle(el).direction ?? 'ltr') === 'rtl';
|
|
1064
|
+
}
|
|
1065
|
+
itemIdOf(target) {
|
|
1066
|
+
if (!(target instanceof Element))
|
|
1067
|
+
return null;
|
|
1068
|
+
const item = target.closest('.mk-org-chart__item');
|
|
1069
|
+
if (!item || !this.host.nativeElement.contains(item))
|
|
1070
|
+
return null;
|
|
1071
|
+
return item.dataset['mkId'] ?? null;
|
|
1072
|
+
}
|
|
1073
|
+
focusItem(id) {
|
|
1074
|
+
if (id === null)
|
|
1075
|
+
return;
|
|
1076
|
+
this.activeId.set(id);
|
|
1077
|
+
const items = this.host.nativeElement.querySelectorAll('.mk-org-chart__item');
|
|
1078
|
+
for (const item of Array.from(items)) {
|
|
1079
|
+
if (item.dataset['mkId'] === id) {
|
|
1080
|
+
item.focus();
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkOrgChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1086
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkOrgChart, isStandalone: true, selector: "mk-org-chart", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange", expanded: "expandedChange", nodeClick: "nodeClick", nodeToggle: "nodeToggle" }, host: { properties: { "class.mk-org-chart--top": "orientation() === 'top'", "class.mk-org-chart--left": "orientation() === 'left'", "class.mk-org-chart--selectable": "selectable()", "class.mk-org-chart--collapsible": "collapsible()" }, classAttribute: "mk-org-chart" }, queries: [{ propertyName: "nodeDef", first: true, predicate: (MkOrgChartNodeDef), descendants: true, isSignal: true }], exportAs: ["mkOrgChart"], ngImport: i0, template: "<div class=\"mk-org-chart__scroller\">\n <div class=\"mk-org-chart__canvas\" [style.zoom]=\"zoomLevel()\">\n <ul\n class=\"mk-org-chart__level mk-org-chart__level--root\"\n role=\"tree\"\n [attr.aria-label]=\"ariaLabel() || null\"\n (keydown)=\"onKeydown($event)\"\n (focusin)=\"onFocusIn($event)\"\n >\n @for (node of nodes(); track node.id) {\n <ng-container\n *ngTemplateOutlet=\"itemTpl; context: { node: node, depth: 1 }\"\n />\n }\n </ul>\n </div>\n</div>\n\n<ng-template #itemTpl let-node=\"node\" let-depth=\"depth\">\n <li\n class=\"mk-org-chart__item\"\n role=\"treeitem\"\n [attr.data-mk-id]=\"node.id\"\n [class.mk-org-chart__item--expanded]=\"isExpanded(node)\"\n [class.mk-org-chart__item--collapsed]=\"node.children?.length && !isExpanded(node)\"\n [attr.aria-level]=\"depth\"\n [attr.aria-expanded]=\"node.children?.length ? isExpanded(node) : null\"\n [attr.aria-selected]=\"selectable() ? isSelected(node) : null\"\n [attr.tabindex]=\"tabIndexFor(node)\"\n >\n <div\n class=\"mk-org-chart__node\"\n [class.mk-org-chart__node--selected]=\"isSelected(node)\"\n [class.mk-org-chart__node--custom]=\"!!nodeDef()\"\n (click)=\"onCardClick($event, node)\"\n >\n @if (nodeDef(); as def) {\n <ng-container\n *ngTemplateOutlet=\"def.template; context: context(node, depth)\"\n />\n } @else {\n <mk-avatar\n class=\"mk-org-chart__avatar\"\n size=\"sm\"\n [name]=\"node.label ?? node.id\"\n [src]=\"field(node, 'avatar')\"\n alt=\"\"\n aria-hidden=\"true\"\n />\n <span class=\"mk-org-chart__text\">\n <span class=\"mk-org-chart__label\">{{ node.label ?? node.id }}</span>\n @if (field(node, 'title'); as title) {\n <span class=\"mk-org-chart__title\">{{ title }}</span>\n }\n </span>\n }\n @if (collapsible() && node.children?.length) {\n <button\n type=\"button\"\n class=\"mk-org-chart__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"toggleLabel(node)\"\n [attr.aria-expanded]=\"isExpanded(node)\"\n (click)=\"onToggleClick($event, node)\"\n >\n <span class=\"mk-org-chart__caret\" aria-hidden=\"true\"></span>\n </button>\n }\n </div>\n @if (isExpanded(node)) {\n <ul class=\"mk-org-chart__level mk-org-chart__level--nested\" role=\"group\">\n @for (child of node.children; track child.id) {\n <ng-container\n *ngTemplateOutlet=\"itemTpl; context: { node: child, depth: depth + 1 }\"\n />\n }\n </ul>\n }\n </li>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{--_line: var(--mk-border-strong);--_line-w: var(--mk-border-width);--_gap-main: var(--mk-space-8);--_gap-cross: var(--mk-space-4);--_card-w: 11rem;display:block;font-family:var(--mk-font-sans);color:var(--mk-text)}.mk-org-chart__scroller{overflow:auto;max-width:100%}.mk-org-chart__canvas{display:inline-block;min-width:100%;padding:var(--mk-space-2);box-sizing:border-box}.mk-org-chart__level{display:flex;list-style:none;margin:0;padding:0;position:relative}.mk-org-chart__level--root{justify-content:center}.mk-org-chart__item{position:relative;display:flex;align-items:center;outline:none}:host(.mk-org-chart--top) .mk-org-chart__level{flex-direction:row}:host(.mk-org-chart--top) .mk-org-chart__level--nested{padding-block-start:var(--_gap-main)}:host(.mk-org-chart--top) .mk-org-chart__level--nested:before{content:\"\";position:absolute;top:0;inset-inline-start:50%;width:0;height:var(--_gap-main);border-inline-start:var(--_line-w) solid var(--_line)}:host(.mk-org-chart--top) .mk-org-chart__item{flex-direction:column;padding:var(--_gap-main) calc(var(--_gap-cross) / 2) 0}:host(.mk-org-chart--top) .mk-org-chart__item:before,:host(.mk-org-chart--top) .mk-org-chart__item:after{content:\"\";position:absolute;top:0;width:50%;height:var(--_gap-main);border-block-start:var(--_line-w) solid var(--_line);pointer-events:none}:host(.mk-org-chart--top) .mk-org-chart__item:before{inset-inline-end:50%}:host(.mk-org-chart--top) .mk-org-chart__item:after{inset-inline-start:50%;border-inline-start:var(--_line-w) solid var(--_line)}:host(.mk-org-chart--top) .mk-org-chart__item:first-child:before{display:none}:host(.mk-org-chart--top) .mk-org-chart__item:last-child:after{border-block-start-color:transparent}:host(.mk-org-chart--top) .mk-org-chart__level--root>.mk-org-chart__item{padding-block-start:0}:host(.mk-org-chart--top) .mk-org-chart__level--root>.mk-org-chart__item:before,:host(.mk-org-chart--top) .mk-org-chart__level--root>.mk-org-chart__item:after{display:none}:host(.mk-org-chart--left) .mk-org-chart__level{flex-direction:column}:host(.mk-org-chart--left) .mk-org-chart__level--root{align-items:flex-start}:host(.mk-org-chart--left) .mk-org-chart__level--nested{padding-inline-start:var(--_gap-main)}:host(.mk-org-chart--left) .mk-org-chart__level--nested:before{content:\"\";position:absolute;top:50%;inset-inline-start:0;width:var(--_gap-main);height:0;border-block-start:var(--_line-w) solid var(--_line)}:host(.mk-org-chart--left) .mk-org-chart__item{flex-direction:row;padding-block:calc(var(--_gap-cross) / 2);padding-inline:var(--_gap-main) 0}:host(.mk-org-chart--left) .mk-org-chart__item:before,:host(.mk-org-chart--left) .mk-org-chart__item:after{content:\"\";position:absolute;inset-inline-start:0;width:var(--_gap-main);height:50%;border-inline-start:var(--_line-w) solid var(--_line);pointer-events:none}:host(.mk-org-chart--left) .mk-org-chart__item:before{top:0}:host(.mk-org-chart--left) .mk-org-chart__item:after{top:50%;border-block-start:var(--_line-w) solid var(--_line)}:host(.mk-org-chart--left) .mk-org-chart__item:first-child:before{display:none}:host(.mk-org-chart--left) .mk-org-chart__item:last-child:after{border-inline-start-color:transparent}:host(.mk-org-chart--left) .mk-org-chart__level--root>.mk-org-chart__item{padding-inline-start:0}:host(.mk-org-chart--left) .mk-org-chart__level--root>.mk-org-chart__item:before,:host(.mk-org-chart--left) .mk-org-chart__level--root>.mk-org-chart__item:after{display:none}.mk-org-chart__node{position:relative;z-index:1;display:flex;align-items:center;gap:var(--mk-space-3);box-sizing:border-box;min-width:var(--_card-w);max-width:calc(var(--_card-w) * 2);min-height:2.75rem;padding:var(--mk-space-2) var(--mk-space-3);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);background:var(--mk-surface);box-shadow:var(--mk-shadow-xs);text-align:start;transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),background-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}.mk-org-chart__node--custom{display:block;min-width:0;max-width:none;min-height:0;padding:0;border:0;background:transparent;box-shadow:none}:host(.mk-org-chart--selectable) .mk-org-chart__node{cursor:pointer;-webkit-user-select:none;user-select:none}:host(.mk-org-chart--selectable) .mk-org-chart__node:not(.mk-org-chart__node--custom):hover{border-color:var(--mk-border-strong);background-color:color-mix(in srgb,var(--mk-surface),var(--mk-hover-overlay))}.mk-org-chart__node--selected:not(.mk-org-chart__node--custom),:host(.mk-org-chart--selectable) .mk-org-chart__node--selected:not(.mk-org-chart__node--custom):hover{border-color:var(--mk-primary);background-color:var(--mk-primary-subtle);box-shadow:0 0 0 1px var(--mk-primary)}.mk-org-chart__item:focus-visible>.mk-org-chart__node{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-radius:var(--mk-radius-md)}.mk-org-chart__avatar{flex:none}.mk-org-chart__text{display:flex;flex-direction:column;min-width:0;line-height:var(--mk-line-height-snug)}.mk-org-chart__label{font-size:var(--mk-font-size-sm);font-weight:var(--mk-font-weight-semibold);color:var(--mk-text);overflow-wrap:anywhere}.mk-org-chart__title{font-size:var(--mk-font-size-xs);color:var(--mk-text-muted);overflow-wrap:anywhere}.mk-org-chart__node--selected .mk-org-chart__label{color:var(--mk-primary-subtle-text)}.mk-org-chart__toggle{position:absolute;z-index:2;display:grid;place-items:center;width:1.25rem;height:1.25rem;padding:0;margin:0;border:var(--mk-border-width) solid var(--mk-border-strong);border-radius:var(--mk-radius-circle);background:var(--mk-surface);color:var(--mk-text-muted);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard),color var(--mk-duration-fast) var(--mk-ease-standard)}@media(pointer:coarse){.mk-org-chart__toggle:after{content:\"\";position:absolute;inset:min(0px,(100% - 24px) / 2)}}.mk-org-chart__toggle:hover{background:var(--mk-surface-2);color:var(--mk-text)}.mk-org-chart__toggle:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}:host(.mk-org-chart--top) .mk-org-chart__toggle{bottom:0;left:50%;transform:translate(-50%,50%)}:host(.mk-org-chart--left) .mk-org-chart__toggle{top:50%;inset-inline-end:0;transform:translateY(-50%);margin-inline-end:-.625rem}.mk-org-chart__caret{width:.35rem;height:.35rem;border-inline-end:2px solid currentColor;border-block-end:2px solid currentColor;transition:transform var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-org-chart--top) .mk-org-chart__caret{transform:translateY(-1px) rotate(45deg)}:host(.mk-org-chart--top) .mk-org-chart__item--expanded>.mk-org-chart__node>.mk-org-chart__toggle .mk-org-chart__caret{transform:translateY(1px) rotate(-135deg)}:host(.mk-org-chart--left) .mk-org-chart__caret{transform:translate(-1px) rotate(-45deg)}:host(.mk-org-chart--left) .mk-org-chart__item--expanded>.mk-org-chart__node>.mk-org-chart__toggle .mk-org-chart__caret{transform:translate(1px) rotate(135deg)}:host(.mk-org-chart--left:dir(rtl)) .mk-org-chart__caret{transform:translate(1px) rotate(135deg)}:host(.mk-org-chart--left:dir(rtl)) .mk-org-chart__item--expanded>.mk-org-chart__node>.mk-org-chart__toggle .mk-org-chart__caret{transform:translate(-1px) rotate(-45deg)}@media(prefers-reduced-motion:reduce){.mk-org-chart__node,.mk-org-chart__toggle,.mk-org-chart__caret{transition:none}}@media print{.mk-org-chart__scroller{overflow:visible}.mk-org-chart__canvas{display:block;zoom:1!important}.mk-org-chart__node{box-shadow:none;break-inside:avoid}.mk-org-chart__toggle{display:none}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MkAvatar, selector: "mk-avatar", inputs: ["src", "name", "alt", "size", "shape", "status"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1087
|
+
}
|
|
1088
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkOrgChart, decorators: [{
|
|
1089
|
+
type: Component,
|
|
1090
|
+
args: [{ selector: 'mk-org-chart', exportAs: 'mkOrgChart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, MkAvatar], host: {
|
|
1091
|
+
class: 'mk-org-chart',
|
|
1092
|
+
'[class.mk-org-chart--top]': "orientation() === 'top'",
|
|
1093
|
+
'[class.mk-org-chart--left]': "orientation() === 'left'",
|
|
1094
|
+
'[class.mk-org-chart--selectable]': 'selectable()',
|
|
1095
|
+
'[class.mk-org-chart--collapsible]': 'collapsible()',
|
|
1096
|
+
}, template: "<div class=\"mk-org-chart__scroller\">\n <div class=\"mk-org-chart__canvas\" [style.zoom]=\"zoomLevel()\">\n <ul\n class=\"mk-org-chart__level mk-org-chart__level--root\"\n role=\"tree\"\n [attr.aria-label]=\"ariaLabel() || null\"\n (keydown)=\"onKeydown($event)\"\n (focusin)=\"onFocusIn($event)\"\n >\n @for (node of nodes(); track node.id) {\n <ng-container\n *ngTemplateOutlet=\"itemTpl; context: { node: node, depth: 1 }\"\n />\n }\n </ul>\n </div>\n</div>\n\n<ng-template #itemTpl let-node=\"node\" let-depth=\"depth\">\n <li\n class=\"mk-org-chart__item\"\n role=\"treeitem\"\n [attr.data-mk-id]=\"node.id\"\n [class.mk-org-chart__item--expanded]=\"isExpanded(node)\"\n [class.mk-org-chart__item--collapsed]=\"node.children?.length && !isExpanded(node)\"\n [attr.aria-level]=\"depth\"\n [attr.aria-expanded]=\"node.children?.length ? isExpanded(node) : null\"\n [attr.aria-selected]=\"selectable() ? isSelected(node) : null\"\n [attr.tabindex]=\"tabIndexFor(node)\"\n >\n <div\n class=\"mk-org-chart__node\"\n [class.mk-org-chart__node--selected]=\"isSelected(node)\"\n [class.mk-org-chart__node--custom]=\"!!nodeDef()\"\n (click)=\"onCardClick($event, node)\"\n >\n @if (nodeDef(); as def) {\n <ng-container\n *ngTemplateOutlet=\"def.template; context: context(node, depth)\"\n />\n } @else {\n <mk-avatar\n class=\"mk-org-chart__avatar\"\n size=\"sm\"\n [name]=\"node.label ?? node.id\"\n [src]=\"field(node, 'avatar')\"\n alt=\"\"\n aria-hidden=\"true\"\n />\n <span class=\"mk-org-chart__text\">\n <span class=\"mk-org-chart__label\">{{ node.label ?? node.id }}</span>\n @if (field(node, 'title'); as title) {\n <span class=\"mk-org-chart__title\">{{ title }}</span>\n }\n </span>\n }\n @if (collapsible() && node.children?.length) {\n <button\n type=\"button\"\n class=\"mk-org-chart__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"toggleLabel(node)\"\n [attr.aria-expanded]=\"isExpanded(node)\"\n (click)=\"onToggleClick($event, node)\"\n >\n <span class=\"mk-org-chart__caret\" aria-hidden=\"true\"></span>\n </button>\n }\n </div>\n @if (isExpanded(node)) {\n <ul class=\"mk-org-chart__level mk-org-chart__level--nested\" role=\"group\">\n @for (child of node.children; track child.id) {\n <ng-container\n *ngTemplateOutlet=\"itemTpl; context: { node: child, depth: depth + 1 }\"\n />\n }\n </ul>\n }\n </li>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{--_line: var(--mk-border-strong);--_line-w: var(--mk-border-width);--_gap-main: var(--mk-space-8);--_gap-cross: var(--mk-space-4);--_card-w: 11rem;display:block;font-family:var(--mk-font-sans);color:var(--mk-text)}.mk-org-chart__scroller{overflow:auto;max-width:100%}.mk-org-chart__canvas{display:inline-block;min-width:100%;padding:var(--mk-space-2);box-sizing:border-box}.mk-org-chart__level{display:flex;list-style:none;margin:0;padding:0;position:relative}.mk-org-chart__level--root{justify-content:center}.mk-org-chart__item{position:relative;display:flex;align-items:center;outline:none}:host(.mk-org-chart--top) .mk-org-chart__level{flex-direction:row}:host(.mk-org-chart--top) .mk-org-chart__level--nested{padding-block-start:var(--_gap-main)}:host(.mk-org-chart--top) .mk-org-chart__level--nested:before{content:\"\";position:absolute;top:0;inset-inline-start:50%;width:0;height:var(--_gap-main);border-inline-start:var(--_line-w) solid var(--_line)}:host(.mk-org-chart--top) .mk-org-chart__item{flex-direction:column;padding:var(--_gap-main) calc(var(--_gap-cross) / 2) 0}:host(.mk-org-chart--top) .mk-org-chart__item:before,:host(.mk-org-chart--top) .mk-org-chart__item:after{content:\"\";position:absolute;top:0;width:50%;height:var(--_gap-main);border-block-start:var(--_line-w) solid var(--_line);pointer-events:none}:host(.mk-org-chart--top) .mk-org-chart__item:before{inset-inline-end:50%}:host(.mk-org-chart--top) .mk-org-chart__item:after{inset-inline-start:50%;border-inline-start:var(--_line-w) solid var(--_line)}:host(.mk-org-chart--top) .mk-org-chart__item:first-child:before{display:none}:host(.mk-org-chart--top) .mk-org-chart__item:last-child:after{border-block-start-color:transparent}:host(.mk-org-chart--top) .mk-org-chart__level--root>.mk-org-chart__item{padding-block-start:0}:host(.mk-org-chart--top) .mk-org-chart__level--root>.mk-org-chart__item:before,:host(.mk-org-chart--top) .mk-org-chart__level--root>.mk-org-chart__item:after{display:none}:host(.mk-org-chart--left) .mk-org-chart__level{flex-direction:column}:host(.mk-org-chart--left) .mk-org-chart__level--root{align-items:flex-start}:host(.mk-org-chart--left) .mk-org-chart__level--nested{padding-inline-start:var(--_gap-main)}:host(.mk-org-chart--left) .mk-org-chart__level--nested:before{content:\"\";position:absolute;top:50%;inset-inline-start:0;width:var(--_gap-main);height:0;border-block-start:var(--_line-w) solid var(--_line)}:host(.mk-org-chart--left) .mk-org-chart__item{flex-direction:row;padding-block:calc(var(--_gap-cross) / 2);padding-inline:var(--_gap-main) 0}:host(.mk-org-chart--left) .mk-org-chart__item:before,:host(.mk-org-chart--left) .mk-org-chart__item:after{content:\"\";position:absolute;inset-inline-start:0;width:var(--_gap-main);height:50%;border-inline-start:var(--_line-w) solid var(--_line);pointer-events:none}:host(.mk-org-chart--left) .mk-org-chart__item:before{top:0}:host(.mk-org-chart--left) .mk-org-chart__item:after{top:50%;border-block-start:var(--_line-w) solid var(--_line)}:host(.mk-org-chart--left) .mk-org-chart__item:first-child:before{display:none}:host(.mk-org-chart--left) .mk-org-chart__item:last-child:after{border-inline-start-color:transparent}:host(.mk-org-chart--left) .mk-org-chart__level--root>.mk-org-chart__item{padding-inline-start:0}:host(.mk-org-chart--left) .mk-org-chart__level--root>.mk-org-chart__item:before,:host(.mk-org-chart--left) .mk-org-chart__level--root>.mk-org-chart__item:after{display:none}.mk-org-chart__node{position:relative;z-index:1;display:flex;align-items:center;gap:var(--mk-space-3);box-sizing:border-box;min-width:var(--_card-w);max-width:calc(var(--_card-w) * 2);min-height:2.75rem;padding:var(--mk-space-2) var(--mk-space-3);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);background:var(--mk-surface);box-shadow:var(--mk-shadow-xs);text-align:start;transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),background-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}.mk-org-chart__node--custom{display:block;min-width:0;max-width:none;min-height:0;padding:0;border:0;background:transparent;box-shadow:none}:host(.mk-org-chart--selectable) .mk-org-chart__node{cursor:pointer;-webkit-user-select:none;user-select:none}:host(.mk-org-chart--selectable) .mk-org-chart__node:not(.mk-org-chart__node--custom):hover{border-color:var(--mk-border-strong);background-color:color-mix(in srgb,var(--mk-surface),var(--mk-hover-overlay))}.mk-org-chart__node--selected:not(.mk-org-chart__node--custom),:host(.mk-org-chart--selectable) .mk-org-chart__node--selected:not(.mk-org-chart__node--custom):hover{border-color:var(--mk-primary);background-color:var(--mk-primary-subtle);box-shadow:0 0 0 1px var(--mk-primary)}.mk-org-chart__item:focus-visible>.mk-org-chart__node{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-radius:var(--mk-radius-md)}.mk-org-chart__avatar{flex:none}.mk-org-chart__text{display:flex;flex-direction:column;min-width:0;line-height:var(--mk-line-height-snug)}.mk-org-chart__label{font-size:var(--mk-font-size-sm);font-weight:var(--mk-font-weight-semibold);color:var(--mk-text);overflow-wrap:anywhere}.mk-org-chart__title{font-size:var(--mk-font-size-xs);color:var(--mk-text-muted);overflow-wrap:anywhere}.mk-org-chart__node--selected .mk-org-chart__label{color:var(--mk-primary-subtle-text)}.mk-org-chart__toggle{position:absolute;z-index:2;display:grid;place-items:center;width:1.25rem;height:1.25rem;padding:0;margin:0;border:var(--mk-border-width) solid var(--mk-border-strong);border-radius:var(--mk-radius-circle);background:var(--mk-surface);color:var(--mk-text-muted);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard),color var(--mk-duration-fast) var(--mk-ease-standard)}@media(pointer:coarse){.mk-org-chart__toggle:after{content:\"\";position:absolute;inset:min(0px,(100% - 24px) / 2)}}.mk-org-chart__toggle:hover{background:var(--mk-surface-2);color:var(--mk-text)}.mk-org-chart__toggle:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}:host(.mk-org-chart--top) .mk-org-chart__toggle{bottom:0;left:50%;transform:translate(-50%,50%)}:host(.mk-org-chart--left) .mk-org-chart__toggle{top:50%;inset-inline-end:0;transform:translateY(-50%);margin-inline-end:-.625rem}.mk-org-chart__caret{width:.35rem;height:.35rem;border-inline-end:2px solid currentColor;border-block-end:2px solid currentColor;transition:transform var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-org-chart--top) .mk-org-chart__caret{transform:translateY(-1px) rotate(45deg)}:host(.mk-org-chart--top) .mk-org-chart__item--expanded>.mk-org-chart__node>.mk-org-chart__toggle .mk-org-chart__caret{transform:translateY(1px) rotate(-135deg)}:host(.mk-org-chart--left) .mk-org-chart__caret{transform:translate(-1px) rotate(-45deg)}:host(.mk-org-chart--left) .mk-org-chart__item--expanded>.mk-org-chart__node>.mk-org-chart__toggle .mk-org-chart__caret{transform:translate(1px) rotate(135deg)}:host(.mk-org-chart--left:dir(rtl)) .mk-org-chart__caret{transform:translate(1px) rotate(135deg)}:host(.mk-org-chart--left:dir(rtl)) .mk-org-chart__item--expanded>.mk-org-chart__node>.mk-org-chart__toggle .mk-org-chart__caret{transform:translate(-1px) rotate(-45deg)}@media(prefers-reduced-motion:reduce){.mk-org-chart__node,.mk-org-chart__toggle,.mk-org-chart__caret{transition:none}}@media print{.mk-org-chart__scroller{overflow:visible}.mk-org-chart__canvas{display:block;zoom:1!important}.mk-org-chart__node{box-shadow:none;break-inside:avoid}.mk-org-chart__toggle{display:none}}\n"] }]
|
|
1097
|
+
}], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], nodeClick: [{ type: i0.Output, args: ["nodeClick"] }], nodeToggle: [{ type: i0.Output, args: ["nodeToggle"] }], nodeDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => MkOrgChartNodeDef), { isSignal: true }] }] } });
|
|
1098
|
+
function walk(list, fn) {
|
|
1099
|
+
for (const node of list) {
|
|
1100
|
+
fn(node);
|
|
1101
|
+
if (node.children?.length)
|
|
1102
|
+
walk(node.children, fn);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
function collectIds(list) {
|
|
1106
|
+
const ids = new Set();
|
|
1107
|
+
walk(list, (n) => ids.add(n.id));
|
|
1108
|
+
return ids;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
503
1111
|
/**
|
|
504
1112
|
* Stat card — a compact KPI tile for dashboards. Shows a label, a primary
|
|
505
1113
|
* value and an optional trend delta. The trend is conveyed by an arrow glyph
|
|
@@ -1165,6 +1773,15 @@ function mkChartTapPin(apply) {
|
|
|
1165
1773
|
*/
|
|
1166
1774
|
const CHAR_PX = 6.2;
|
|
1167
1775
|
const estimateLabelPx = (label) => label.length * CHAR_PX;
|
|
1776
|
+
/**
|
|
1777
|
+
* Fraction of its band a label may fill before `auto` tilts the axis.
|
|
1778
|
+
*
|
|
1779
|
+
* Not 1.0: labels that *technically* fit still read as a cramped run of words
|
|
1780
|
+
* with no gap between them ("mar" "kwi" "maj" touching each other), which is
|
|
1781
|
+
* the look tilting exists to avoid. Leaving a fifth of the band as breathing
|
|
1782
|
+
* room tilts the tight cases and leaves genuinely roomy axes alone.
|
|
1783
|
+
*/
|
|
1784
|
+
const TILT_THRESHOLD = 0.8;
|
|
1168
1785
|
/**
|
|
1169
1786
|
* BarChart — compares a measure across categories with one or more series
|
|
1170
1787
|
* (grouped or `stacked`), `vertical` (default) or `horizontal`. Dependency-free
|
|
@@ -1257,7 +1874,7 @@ class MkBarChart {
|
|
|
1257
1874
|
if (!count)
|
|
1258
1875
|
return 0;
|
|
1259
1876
|
const band = Math.max(this.drawWidth() - 58, 1) / count;
|
|
1260
|
-
return this.widestLabelPx() <= band ? 0 : 45;
|
|
1877
|
+
return this.widestLabelPx() <= band * TILT_THRESHOLD ? 0 : 45;
|
|
1261
1878
|
}, /* @ts-ignore */
|
|
1262
1879
|
...(ngDevMode ? [{ debugName: "resolvedAngle" }] : /* istanbul ignore next */ []));
|
|
1263
1880
|
/**
|
|
@@ -2222,18 +2839,105 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
2222
2839
|
}, template: "<div class=\"mk-chart__frame\">\n <svg\n class=\"mk-chart__svg\"\n [attr.viewBox]=\"'0 0 ' + width() + ' ' + height()\"\n preserveAspectRatio=\"xMidYMid meet\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <!-- Horizontal gridlines + y-axis labels -->\n @for (row of yGrid(); track row.value) {\n @if (showGrid()) {\n <line\n class=\"mk-chart__grid\"\n [attr.x1]=\"row.x1\"\n [attr.x2]=\"row.x2\"\n [attr.y1]=\"row.y\"\n [attr.y2]=\"row.y\"\n />\n }\n <text\n class=\"mk-chart__axis-label mk-chart__axis-label--y\"\n [attr.x]=\"plot().x - 8\"\n [attr.y]=\"row.y\"\n >\n {{ row.label }}\n </text>\n }\n\n <!-- x-axis labels -->\n @for (col of xGrid(); track col.value) {\n <text\n class=\"mk-chart__axis-label mk-chart__axis-label--x\"\n [attr.x]=\"col.x\"\n [attr.y]=\"height() - 12\"\n >\n {{ col.label }}\n </text>\n }\n\n <!-- Axis titles -->\n @if (xLabel()) {\n <text\n class=\"mk-chart__axis-title mk-chart__axis-title--x\"\n [attr.x]=\"plot().x + plot().w / 2\"\n [attr.y]=\"height() - 1\"\n >\n {{ xLabel() }}\n </text>\n }\n @if (yLabel()) {\n <text\n class=\"mk-chart__axis-title mk-chart__axis-title--y\"\n [attr.transform]=\"'rotate(-90 10 ' + (plot().y + plot().h / 2) + ')'\"\n [attr.x]=\"10\"\n [attr.y]=\"plot().y + plot().h / 2\"\n >\n {{ yLabel() }}\n </text>\n }\n\n <!-- Points -->\n @for (p of points(); track $index; let i = $index) {\n <circle\n class=\"mk-chart__point\"\n [class.mk-chart__point--active]=\"hoverKey() === keyOf(i)\"\n [attr.cx]=\"p.cx\"\n [attr.cy]=\"p.cy\"\n [attr.r]=\"p.r\"\n [style.fill]=\"p.color\"\n (pointerenter)=\"setHover(keyOf(i))\"\n (pointerleave)=\"setHover(null)\"\n (pointerdown)=\"pinHover(keyOf(i), $event)\"\n />\n }\n </svg>\n\n @if (tooltip(); as t) {\n <div class=\"mk-chart__tooltip\" [style.left.%]=\"t.left\" [style.top.%]=\"t.top\">\n <span class=\"mk-chart__tooltip-title\">\n <span class=\"mk-chart__swatch\" [style.background]=\"t.color\"></span>\n {{ t.title }}\n </span>\n <span class=\"mk-chart__tooltip-row\">\n <span class=\"mk-chart__tooltip-name\">{{ xLabel() || 'x' }}</span>\n <span class=\"mk-chart__tooltip-value\">{{ t.x }}</span>\n </span>\n <span class=\"mk-chart__tooltip-row\">\n <span class=\"mk-chart__tooltip-name\">{{ yLabel() || 'y' }}</span>\n <span class=\"mk-chart__tooltip-value\">{{ t.y }}</span>\n </span>\n </div>\n }\n</div>\n\n@if (legendVisible()) {\n <ul class=\"mk-chart__legend\">\n @for (item of legend(); track item.name) {\n <li class=\"mk-chart__legend-item\">\n <span class=\"mk-chart__swatch\" [style.background]=\"item.color\"></span>\n {{ item.name }}\n </li>\n }\n </ul>\n}\n\n<table class=\"mk-chart__sr-table\">\n <caption>{{ resolvedLabel() }}</caption>\n <thead>\n <tr>\n <th scope=\"col\">{{ i18n.chartSeries }}</th>\n <th scope=\"col\">{{ xLabel() || 'x' }}</th>\n <th scope=\"col\">{{ yLabel() || 'y' }}</th>\n </tr>\n </thead>\n <tbody>\n @for (s of series(); track s.name) {\n @for (p of s.points; track $index) {\n <tr>\n <th scope=\"row\">{{ s.name }}</th>\n <td>{{ p.x }}</td>\n <td>{{ p.y }}</td>\n </tr>\n }\n }\n </tbody>\n</table>\n", styles: ["@charset \"UTF-8\";:host{display:block;font-family:var(--mk-font-sans);color:var(--mk-text)}.mk-chart__frame{position:relative}.mk-chart__svg{display:block;width:100%;height:auto;overflow:visible}.mk-chart__grid{stroke:var(--mk-chart-grid);stroke-width:1;vector-effect:non-scaling-stroke}.mk-chart__axis-label{fill:var(--mk-chart-axis);font-size:11px}.mk-chart__axis-label--y{text-anchor:end;dominant-baseline:middle}.mk-chart__axis-label--x{text-anchor:middle;dominant-baseline:hanging}.mk-chart__axis-label--tilted{text-anchor:end;dominant-baseline:middle}.mk-chart__bar{transition:opacity var(--mk-duration-fast) var(--mk-ease-standard),filter var(--mk-duration-fast) var(--mk-ease-standard)}.mk-chart__bar:hover{filter:brightness(1.08)}.mk-chart__bar--dim{opacity:.4}.mk-chart__area{stroke:none;opacity:.14}.mk-chart__line{fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke}.mk-chart__dot{stroke:var(--mk-surface);stroke-width:1.5;vector-effect:non-scaling-stroke}.mk-chart__crosshair{stroke:var(--mk-chart-axis);stroke-width:1;stroke-dasharray:3 3;vector-effect:non-scaling-stroke;opacity:.6}.mk-chart__point{fill-opacity:.72;stroke:var(--mk-surface);stroke-width:1;vector-effect:non-scaling-stroke;cursor:pointer;transition:fill-opacity var(--mk-duration-fast) var(--mk-ease-standard)}.mk-chart__point:hover,.mk-chart__point--active{fill-opacity:1}.mk-radar__ring{fill:none}.mk-radar__area{fill-opacity:.16;stroke-width:2;stroke-linejoin:round;vector-effect:non-scaling-stroke}.mk-radar__axis-label{dominant-baseline:middle}.mk-chart__axis-title{fill:var(--mk-chart-axis);font-size:11px;font-weight:var(--mk-font-weight-medium)}.mk-chart__axis-title--x,.mk-chart__axis-title--y{text-anchor:middle}.mk-chart__slice{stroke:var(--mk-surface);stroke-width:2;transition:opacity var(--mk-duration-fast) var(--mk-ease-standard)}.mk-chart__slice--dim{opacity:.45}.mk-chart__tooltip{position:absolute;z-index:1;transform:translate(-50%,calc(-100% - 8px));pointer-events:none;display:flex;flex-direction:column;gap:2px;padding:var(--mk-space-2) var(--mk-space-3);background-color:var(--mk-surface);color:var(--mk-text);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);box-shadow:var(--mk-shadow-lg);font-size:var(--mk-font-size-sm);white-space:nowrap}.mk-chart__tooltip-title{font-weight:var(--mk-font-weight-semibold)}.mk-chart__tooltip-row{display:flex;align-items:center;gap:var(--mk-space-2)}.mk-chart__tooltip-name{color:var(--mk-text-muted)}.mk-chart__tooltip-value{margin-inline-start:auto;font-variant-numeric:tabular-nums;font-weight:var(--mk-font-weight-medium)}.mk-chart__swatch{flex:none;width:.75rem;height:.75rem;border-radius:var(--mk-radius-xs)}.mk-chart__legend{display:flex;flex-wrap:wrap;gap:var(--mk-space-2) var(--mk-space-4);margin:var(--mk-space-3) 0 0;padding:0;padding-inline-start:var(--mk-space-2);list-style:none}.mk-chart__legend-item{display:inline-flex;align-items:center;gap:var(--mk-space-2);font-size:var(--mk-font-size-sm);color:var(--mk-text-muted)}.mk-donut__frame{margin:0 auto}.mk-donut__center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;pointer-events:none;text-align:center}.mk-donut__center-label{font-size:var(--mk-font-size-2xl);font-weight:var(--mk-font-weight-bold);line-height:1;color:var(--mk-text);font-variant-numeric:tabular-nums}.mk-donut__center-sublabel{font-size:var(--mk-font-size-xs);color:var(--mk-text-subtle);text-transform:uppercase;letter-spacing:.04em}.mk-donut__legend{flex-direction:column;gap:var(--mk-space-2)}.mk-donut__legend .mk-chart__legend-item{width:100%}.mk-donut__legend-name{color:var(--mk-text)}.mk-donut__legend-value{margin-inline-start:auto;color:var(--mk-text-muted);font-variant-numeric:tabular-nums}.mk-chart__sr-table{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(prefers-reduced-motion:reduce){.mk-chart__bar,.mk-chart__slice{transition:none}}.mk-chart__slice--interactive,.mk-chart__bar--interactive,.mk-chart__band--interactive,.mk-chart__legend-item--interactive{cursor:pointer}.mk-chart__slice--interactive:focus-visible,.mk-chart__bar--interactive:focus-visible,.mk-chart__band--interactive:focus-visible{outline:2px solid var(--mk-primary);outline-offset:2px}.mk-chart__legend-button{display:inline-flex;align-items:center;gap:inherit;padding:0;margin:0;border:0;background:transparent;color:inherit;font:inherit;cursor:pointer;border-radius:var(--mk-radius-sm)}.mk-chart__legend-button:focus-visible{outline:2px solid var(--mk-primary);outline-offset:2px}\n"] }]
|
|
2223
2840
|
}], propDecorators: { series: [{ type: i0.Input, args: [{ isSignal: true, alias: "series", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], pointRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "pointRadius", required: false }] }], bubble: [{ type: i0.Input, args: [{ isSignal: true, alias: "bubble", required: false }] }], maxRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRadius", required: false }] }], showGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGrid", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], xLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "xLabel", required: false }] }], yLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "yLabel", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
2224
2841
|
|
|
2842
|
+
/** WCAG AA minimum contrast for normal text — what cell values are held to. */
|
|
2843
|
+
const MK_HEATMAP_MIN_CONTRAST = 4.5;
|
|
2844
|
+
/** WCAG 2.x relative luminance of an sRGB colour. */
|
|
2845
|
+
function mkRelativeLuminance([r, g, b]) {
|
|
2846
|
+
const lin = (c) => {
|
|
2847
|
+
const s = c / 255;
|
|
2848
|
+
return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
2849
|
+
};
|
|
2850
|
+
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
|
|
2851
|
+
}
|
|
2852
|
+
/** WCAG 2.x contrast ratio (1–21) between two colours. */
|
|
2853
|
+
function mkContrastRatio(a, b) {
|
|
2854
|
+
const la = mkRelativeLuminance(a);
|
|
2855
|
+
const lb = mkRelativeLuminance(b);
|
|
2856
|
+
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
2857
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
2858
|
+
}
|
|
2859
|
+
/** What `color-mix(in srgb, a t%, b)` yields: a linear blend in gamma-encoded sRGB. */
|
|
2860
|
+
function mkMixRgb(a, b, t) {
|
|
2861
|
+
const mix = (x, y) => Math.round(x * t + y * (1 - t));
|
|
2862
|
+
return [mix(a[0], b[0]), mix(a[1], b[1]), mix(a[2], b[2])];
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Parse a CSS colour as the CSSOM serialises it — `rgb(r, g, b)` /
|
|
2866
|
+
* `rgba(r, g, b, a)` — or a `#rgb` / `#rrggbb` literal. `null` for anything
|
|
2867
|
+
* else (an unresolved `var()`, `color(...)`, …).
|
|
2868
|
+
*/
|
|
2869
|
+
function mkParseRgb(text) {
|
|
2870
|
+
const s = (text ?? '').trim();
|
|
2871
|
+
const fn = /^rgba?\(\s*(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)/i.exec(s);
|
|
2872
|
+
if (fn)
|
|
2873
|
+
return [Number(fn[1]), Number(fn[2]), Number(fn[3])];
|
|
2874
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(s);
|
|
2875
|
+
if (!hex)
|
|
2876
|
+
return null;
|
|
2877
|
+
const h = hex[1].length === 3 ? [...hex[1]].map((c) => c + c).join('') : hex[1];
|
|
2878
|
+
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
|
|
2879
|
+
}
|
|
2880
|
+
/** Best of the two text tokens against the cell painted at intensity `t`. */
|
|
2881
|
+
function pickText(t, palette) {
|
|
2882
|
+
const bg = mkMixRgb(palette.accent, palette.surface, t);
|
|
2883
|
+
const onText = mkContrastRatio(palette.text, bg);
|
|
2884
|
+
const onInverse = mkContrastRatio(palette.textInverse, bg);
|
|
2885
|
+
return onInverse > onText
|
|
2886
|
+
? { intensity: t, inverse: true, contrast: onInverse }
|
|
2887
|
+
: { intensity: t, inverse: false, contrast: onText };
|
|
2888
|
+
}
|
|
2889
|
+
/**
|
|
2890
|
+
* Choose the text token for a cell at intensity `t` (0–1) — light text on
|
|
2891
|
+
* dark cells, dark text on light cells, decided by contrast against the
|
|
2892
|
+
* actual blend rather than a fixed threshold.
|
|
2893
|
+
*
|
|
2894
|
+
* Every accent ramp has a crossover band where neither `--mk-text` nor
|
|
2895
|
+
* `--mk-text-inverse` reaches 4.5:1 (the cell is mid-luminance). When values
|
|
2896
|
+
* are printed in the cells, a cell in that band is painted at the nearest
|
|
2897
|
+
* intensity just outside it, so its value stays readable; the shift is a few
|
|
2898
|
+
* percent of the ramp and the cell keeps its order relative to its
|
|
2899
|
+
* neighbours. Without a resolved palette (server rendering, tests) a plain
|
|
2900
|
+
* threshold is used.
|
|
2901
|
+
*/
|
|
2902
|
+
function mkHeatmapCellColors(t, palette, showValues) {
|
|
2903
|
+
const clamped = Math.max(0, Math.min(1, t));
|
|
2904
|
+
if (!palette)
|
|
2905
|
+
return { intensity: clamped, inverse: clamped >= 0.55, contrast: NaN };
|
|
2906
|
+
const best = pickText(clamped, palette);
|
|
2907
|
+
if (!showValues || best.contrast >= MK_HEATMAP_MIN_CONTRAST)
|
|
2908
|
+
return best;
|
|
2909
|
+
// In the crossover band: step outward (down and up alternately, 1 % at a
|
|
2910
|
+
// time) to the closest intensity at which one of the tokens passes.
|
|
2911
|
+
for (let step = 1; step <= 100; step++) {
|
|
2912
|
+
const delta = step / 100;
|
|
2913
|
+
for (const candidate of [clamped - delta, clamped + delta]) {
|
|
2914
|
+
if (candidate < 0 || candidate > 1)
|
|
2915
|
+
continue;
|
|
2916
|
+
const pick = pickText(candidate, palette);
|
|
2917
|
+
if (pick.contrast >= MK_HEATMAP_MIN_CONTRAST)
|
|
2918
|
+
return pick;
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
return best;
|
|
2922
|
+
}
|
|
2225
2923
|
/**
|
|
2226
2924
|
* Heatmap — a matrix of cells shaded by value intensity, for correlation
|
|
2227
2925
|
* grids, activity-by-time tables and the like. Rendered as a semantic
|
|
2228
2926
|
* `<table>` (so it is inherently screen-reader friendly) with cell colours
|
|
2229
2927
|
* mixed from `accent` over the surface via `color-mix`, so it tracks the theme.
|
|
2230
2928
|
*
|
|
2929
|
+
* Printed values (`showValues`) pick `--mk-text` or `--mk-text-inverse` per
|
|
2930
|
+
* cell by contrast against that cell's actual colour, in both themes — see
|
|
2931
|
+
* {@link mkHeatmapCellColors}.
|
|
2932
|
+
*
|
|
2231
2933
|
* ```html
|
|
2232
2934
|
* <mk-heatmap
|
|
2233
2935
|
* [xLabels]="hours" [yLabels]="days" [data]="matrix()" showValues />
|
|
2234
2936
|
* ```
|
|
2235
2937
|
*/
|
|
2236
2938
|
class MkHeatmap {
|
|
2939
|
+
doc = inject(DOCUMENT$1);
|
|
2940
|
+
host = inject(ElementRef).nativeElement;
|
|
2237
2941
|
/** Column labels (x axis). */
|
|
2238
2942
|
xLabels = input([], /* @ts-ignore */
|
|
2239
2943
|
...(ngDevMode ? [{ debugName: "xLabels" }] : /* istanbul ignore next */ []));
|
|
@@ -2262,6 +2966,25 @@ class MkHeatmap {
|
|
|
2262
2966
|
/** Accessible summary; generated from the data when omitted. */
|
|
2263
2967
|
label = input('', /* @ts-ignore */
|
|
2264
2968
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
2969
|
+
/**
|
|
2970
|
+
* The accent and the text/surface tokens resolved through the CSSOM, so
|
|
2971
|
+
* each cell can pick a text colour that actually contrasts with its blend.
|
|
2972
|
+
* `null` until resolved (server, jsdom) — the cells then fall back to a
|
|
2973
|
+
* threshold.
|
|
2974
|
+
*/
|
|
2975
|
+
palette = signal(null, /* @ts-ignore */
|
|
2976
|
+
...(ngDevMode ? [{ debugName: "palette" }] : /* istanbul ignore next */ []));
|
|
2977
|
+
/** Bumped when the document theme changes so the palette is re-resolved. */
|
|
2978
|
+
themeTick = signal(0, /* @ts-ignore */
|
|
2979
|
+
...(ngDevMode ? [{ debugName: "themeTick" }] : /* istanbul ignore next */ []));
|
|
2980
|
+
constructor() {
|
|
2981
|
+
afterRenderEffect(() => {
|
|
2982
|
+
this.accent();
|
|
2983
|
+
this.themeTick();
|
|
2984
|
+
untracked(() => this.palette.set(this.resolvePalette()));
|
|
2985
|
+
});
|
|
2986
|
+
this.watchTheme();
|
|
2987
|
+
}
|
|
2265
2988
|
domain = computed(() => {
|
|
2266
2989
|
const flat = this.data()
|
|
2267
2990
|
.flat()
|
|
@@ -2277,26 +3000,32 @@ class MkHeatmap {
|
|
|
2277
3000
|
const span = hi - lo || 1;
|
|
2278
3001
|
return Math.max(0, Math.min(1, (value - lo) / span));
|
|
2279
3002
|
}
|
|
3003
|
+
/** The `color-mix()` expression painting a cell at intensity `t`. */
|
|
3004
|
+
cellBackground(t) {
|
|
3005
|
+
return `color-mix(in srgb, ${this.accent()} ${Math.round(t * 100)}%, var(--mk-surface-2))`;
|
|
3006
|
+
}
|
|
2280
3007
|
rows = computed(() => {
|
|
2281
3008
|
const xs = this.xLabels();
|
|
2282
3009
|
const ys = this.yLabels();
|
|
2283
3010
|
const data = this.data();
|
|
2284
3011
|
const fmt = this.format();
|
|
3012
|
+
const palette = this.palette();
|
|
3013
|
+
const showValues = this.showValues();
|
|
2285
3014
|
return ys.map((yLabel, r) => ({
|
|
2286
3015
|
label: yLabel,
|
|
2287
3016
|
cells: xs.map((xLabel, c) => {
|
|
2288
3017
|
const value = data[r]?.[c] ?? null;
|
|
2289
3018
|
if (value == null) {
|
|
2290
|
-
return { value: null, display: '', xLabel, bg: 'transparent',
|
|
3019
|
+
return { value: null, display: '', xLabel, bg: 'transparent', inverse: false };
|
|
2291
3020
|
}
|
|
2292
|
-
const
|
|
3021
|
+
const colors = mkHeatmapCellColors(this.ratio(value), palette, showValues);
|
|
2293
3022
|
return {
|
|
2294
3023
|
value,
|
|
2295
3024
|
display: fmt(value),
|
|
2296
3025
|
xLabel,
|
|
2297
3026
|
// Mix the accent over the surface; more intense = more accent.
|
|
2298
|
-
bg:
|
|
2299
|
-
|
|
3027
|
+
bg: this.cellBackground(colors.intensity),
|
|
3028
|
+
inverse: colors.inverse,
|
|
2300
3029
|
};
|
|
2301
3030
|
}),
|
|
2302
3031
|
}));
|
|
@@ -2309,7 +3038,7 @@ class MkHeatmap {
|
|
|
2309
3038
|
return {
|
|
2310
3039
|
min: fmt(lo),
|
|
2311
3040
|
max: fmt(hi),
|
|
2312
|
-
swatches: [0, 0.25, 0.5, 0.75, 1].map((t) =>
|
|
3041
|
+
swatches: [0, 0.25, 0.5, 0.75, 1].map((t) => this.cellBackground(t)),
|
|
2313
3042
|
};
|
|
2314
3043
|
}, /* @ts-ignore */
|
|
2315
3044
|
...(ngDevMode ? [{ debugName: "scaleStops" }] : /* istanbul ignore next */ []));
|
|
@@ -2321,8 +3050,62 @@ class MkHeatmap {
|
|
|
2321
3050
|
return `Heatmap, ${rows} rows by ${cols} columns`;
|
|
2322
3051
|
}, /* @ts-ignore */
|
|
2323
3052
|
...(ngDevMode ? [{ debugName: "resolvedLabel" }] : /* istanbul ignore next */ []));
|
|
3053
|
+
/**
|
|
3054
|
+
* Resolve the accent and the tokens to sRGB through a hidden probe: the
|
|
3055
|
+
* probe's computed `color` is what the browser would paint, `var()`s and
|
|
3056
|
+
* `color-mix()` included. `null` when the environment cannot resolve them.
|
|
3057
|
+
*/
|
|
3058
|
+
resolvePalette() {
|
|
3059
|
+
const win = this.doc.defaultView;
|
|
3060
|
+
if (!win?.getComputedStyle)
|
|
3061
|
+
return null;
|
|
3062
|
+
const probe = this.doc.createElement('span');
|
|
3063
|
+
probe.setAttribute('aria-hidden', 'true');
|
|
3064
|
+
probe.style.position = 'absolute';
|
|
3065
|
+
probe.style.visibility = 'hidden';
|
|
3066
|
+
this.host.appendChild(probe);
|
|
3067
|
+
try {
|
|
3068
|
+
const read = (expr) => {
|
|
3069
|
+
probe.style.color = '';
|
|
3070
|
+
probe.style.color = expr;
|
|
3071
|
+
return mkParseRgb(win.getComputedStyle(probe).color);
|
|
3072
|
+
};
|
|
3073
|
+
const accent = read(this.accent());
|
|
3074
|
+
const surface = read('var(--mk-surface-2)');
|
|
3075
|
+
const text = read('var(--mk-text)');
|
|
3076
|
+
const textInverse = read('var(--mk-text-inverse)');
|
|
3077
|
+
return accent && surface && text && textInverse
|
|
3078
|
+
? { accent, surface, text, textInverse }
|
|
3079
|
+
: null;
|
|
3080
|
+
}
|
|
3081
|
+
finally {
|
|
3082
|
+
probe.remove();
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
/**
|
|
3086
|
+
* Re-resolve when the theme flips: `data-theme` / class changes on the root
|
|
3087
|
+
* element (MkThemeService and most hand-rolled toggles) and the OS scheme.
|
|
3088
|
+
*/
|
|
3089
|
+
watchTheme() {
|
|
3090
|
+
const win = this.doc.defaultView;
|
|
3091
|
+
const root = this.doc.documentElement;
|
|
3092
|
+
if (!win || !root)
|
|
3093
|
+
return;
|
|
3094
|
+
const bump = () => this.themeTick.update((n) => n + 1);
|
|
3095
|
+
const destroyRef = inject(DestroyRef);
|
|
3096
|
+
if (typeof win.MutationObserver === 'function') {
|
|
3097
|
+
const observer = new win.MutationObserver(bump);
|
|
3098
|
+
observer.observe(root, { attributes: true, attributeFilter: ['data-theme', 'class', 'style'] });
|
|
3099
|
+
destroyRef.onDestroy(() => observer.disconnect());
|
|
3100
|
+
}
|
|
3101
|
+
const mql = win.matchMedia?.('(prefers-color-scheme: dark)');
|
|
3102
|
+
if (mql?.addEventListener) {
|
|
3103
|
+
mql.addEventListener('change', bump);
|
|
3104
|
+
destroyRef.onDestroy(() => mql.removeEventListener('change', bump));
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
2324
3107
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkHeatmap, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2325
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkHeatmap, isStandalone: true, selector: "mk-heatmap", inputs: { xLabels: { classPropertyName: "xLabels", publicName: "xLabels", isSignal: true, isRequired: false, transformFunction: null }, yLabels: { classPropertyName: "yLabels", publicName: "yLabels", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, accent: { classPropertyName: "accent", publicName: "accent", isSignal: true, isRequired: false, transformFunction: null }, showValues: { classPropertyName: "showValues", publicName: "showValues", isSignal: true, isRequired: false, transformFunction: null }, showScale: { classPropertyName: "showScale", publicName: "showScale", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "group" }, properties: { "attr.aria-label": "resolvedLabel()" }, classAttribute: "mk-chart mk-heatmap" }, ngImport: i0, template: "<!-- Focusable so keyboard users can scroll the overflowing grid (WCAG 2.1.1). -->\n<div\n class=\"mk-chart__frame mk-heatmap__scroll\"\n role=\"region\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedLabel()\"\n>\n <table class=\"mk-heatmap__grid\">\n <caption class=\"mk-visually-hidden\">{{ resolvedLabel() }}</caption>\n <thead>\n <tr>\n <td class=\"mk-heatmap__corner\"></td>\n @for (x of xLabels(); track $index) {\n <th scope=\"col\" class=\"mk-heatmap__col-head\">{{ x }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.label) {\n <tr>\n <th scope=\"row\" class=\"mk-heatmap__row-head\">{{ row.label }}</th>\n @for (cell of row.cells; track $index) {\n <td\n class=\"mk-heatmap__cell\"\n [class.mk-heatmap__cell--
|
|
3108
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkHeatmap, isStandalone: true, selector: "mk-heatmap", inputs: { xLabels: { classPropertyName: "xLabels", publicName: "xLabels", isSignal: true, isRequired: false, transformFunction: null }, yLabels: { classPropertyName: "yLabels", publicName: "yLabels", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, accent: { classPropertyName: "accent", publicName: "accent", isSignal: true, isRequired: false, transformFunction: null }, showValues: { classPropertyName: "showValues", publicName: "showValues", isSignal: true, isRequired: false, transformFunction: null }, showScale: { classPropertyName: "showScale", publicName: "showScale", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "group" }, properties: { "attr.aria-label": "resolvedLabel()" }, classAttribute: "mk-chart mk-heatmap" }, ngImport: i0, template: "<!-- Focusable so keyboard users can scroll the overflowing grid (WCAG 2.1.1). -->\n<div\n class=\"mk-chart__frame mk-heatmap__scroll\"\n role=\"region\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedLabel()\"\n>\n <table class=\"mk-heatmap__grid\">\n <caption class=\"mk-visually-hidden\">{{ resolvedLabel() }}</caption>\n <thead>\n <tr>\n <td class=\"mk-heatmap__corner\"></td>\n @for (x of xLabels(); track $index) {\n <th scope=\"col\" class=\"mk-heatmap__col-head\">{{ x }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.label) {\n <tr>\n <th scope=\"row\" class=\"mk-heatmap__row-head\">{{ row.label }}</th>\n @for (cell of row.cells; track $index) {\n <td\n class=\"mk-heatmap__cell\"\n [class.mk-heatmap__cell--inverse]=\"cell.inverse\"\n [class.mk-heatmap__cell--empty]=\"cell.value === null\"\n [style.background]=\"cell.bg\"\n [attr.title]=\"\n cell.value === null\n ? null\n : row.label + ' \u00B7 ' + cell.xLabel + ': ' + cell.display\n \"\n >\n @if (cell.value !== null) {\n @if (showValues()) {\n <span class=\"mk-heatmap__value\">{{ cell.display }}</span>\n } @else {\n <!-- Colour must not be the only signal: expose the value to AT. -->\n <span class=\"mk-visually-hidden\">{{ cell.display }}</span>\n }\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n</div>\n\n@if (showScale()) {\n <div class=\"mk-heatmap__legend\" aria-hidden=\"true\">\n <span class=\"mk-heatmap__legend-label\">{{ scaleStops().min }}</span>\n <span class=\"mk-heatmap__legend-ramp\">\n @for (sw of scaleStops().swatches; track $index) {\n <span class=\"mk-heatmap__legend-swatch\" [style.background]=\"sw\"></span>\n }\n </span>\n <span class=\"mk-heatmap__legend-label\">{{ scaleStops().max }}</span>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;font-family:var(--mk-font-sans)}.mk-heatmap__scroll{width:100%;overflow-x:auto}.mk-heatmap__scroll:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:calc(-1 * var(--mk-focus-ring-width))}.mk-heatmap__grid{border-collapse:separate;border-spacing:3px;font-size:var(--mk-font-size-sm);color:var(--mk-text)}.mk-heatmap__corner{border:0}.mk-heatmap__col-head,.mk-heatmap__row-head{padding:var(--mk-space-1) var(--mk-space-2);font-weight:var(--mk-font-weight-medium);color:var(--mk-text-muted);white-space:nowrap}.mk-heatmap__col-head{text-align:center;vertical-align:bottom}.mk-heatmap__row-head{text-align:end}.mk-heatmap__cell{width:2.25rem;height:2.25rem;padding:0;text-align:center;vertical-align:middle;border-radius:var(--mk-radius-sm);color:var(--mk-text);transition:outline-color var(--mk-duration-fast) var(--mk-ease-standard);outline:2px solid transparent;outline-offset:-2px}.mk-heatmap__cell:hover{outline-color:var(--mk-border-strong)}.mk-heatmap__cell--inverse{color:var(--mk-text-inverse)}.mk-heatmap__cell--empty{outline:none}.mk-heatmap__cell--empty:hover{outline-color:transparent}.mk-heatmap__value{font-size:var(--mk-font-size-xs);font-variant-numeric:tabular-nums}.mk-heatmap__legend{display:flex;align-items:center;gap:var(--mk-space-2);margin-top:var(--mk-space-3);font-size:var(--mk-font-size-xs);color:var(--mk-text-muted)}.mk-heatmap__legend-ramp{display:inline-flex;gap:2px}.mk-heatmap__legend-swatch{width:1.1rem;height:.75rem;border-radius:2px;box-shadow:inset 0 0 0 1px var(--mk-border-subtle)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2326
3109
|
}
|
|
2327
3110
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkHeatmap, decorators: [{
|
|
2328
3111
|
type: Component,
|
|
@@ -2330,8 +3113,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
2330
3113
|
class: 'mk-chart mk-heatmap',
|
|
2331
3114
|
role: 'group',
|
|
2332
3115
|
'[attr.aria-label]': 'resolvedLabel()',
|
|
2333
|
-
}, template: "<!-- Focusable so keyboard users can scroll the overflowing grid (WCAG 2.1.1). -->\n<div\n class=\"mk-chart__frame mk-heatmap__scroll\"\n role=\"region\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedLabel()\"\n>\n <table class=\"mk-heatmap__grid\">\n <caption class=\"mk-visually-hidden\">{{ resolvedLabel() }}</caption>\n <thead>\n <tr>\n <td class=\"mk-heatmap__corner\"></td>\n @for (x of xLabels(); track $index) {\n <th scope=\"col\" class=\"mk-heatmap__col-head\">{{ x }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.label) {\n <tr>\n <th scope=\"row\" class=\"mk-heatmap__row-head\">{{ row.label }}</th>\n @for (cell of row.cells; track $index) {\n <td\n class=\"mk-heatmap__cell\"\n [class.mk-heatmap__cell--
|
|
2334
|
-
}], propDecorators: { xLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "xLabels", required: false }] }], yLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "yLabels", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], accent: [{ type: i0.Input, args: [{ isSignal: true, alias: "accent", required: false }] }], showValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValues", required: false }] }], showScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "showScale", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
3116
|
+
}, template: "<!-- Focusable so keyboard users can scroll the overflowing grid (WCAG 2.1.1). -->\n<div\n class=\"mk-chart__frame mk-heatmap__scroll\"\n role=\"region\"\n tabindex=\"0\"\n [attr.aria-label]=\"resolvedLabel()\"\n>\n <table class=\"mk-heatmap__grid\">\n <caption class=\"mk-visually-hidden\">{{ resolvedLabel() }}</caption>\n <thead>\n <tr>\n <td class=\"mk-heatmap__corner\"></td>\n @for (x of xLabels(); track $index) {\n <th scope=\"col\" class=\"mk-heatmap__col-head\">{{ x }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.label) {\n <tr>\n <th scope=\"row\" class=\"mk-heatmap__row-head\">{{ row.label }}</th>\n @for (cell of row.cells; track $index) {\n <td\n class=\"mk-heatmap__cell\"\n [class.mk-heatmap__cell--inverse]=\"cell.inverse\"\n [class.mk-heatmap__cell--empty]=\"cell.value === null\"\n [style.background]=\"cell.bg\"\n [attr.title]=\"\n cell.value === null\n ? null\n : row.label + ' \u00B7 ' + cell.xLabel + ': ' + cell.display\n \"\n >\n @if (cell.value !== null) {\n @if (showValues()) {\n <span class=\"mk-heatmap__value\">{{ cell.display }}</span>\n } @else {\n <!-- Colour must not be the only signal: expose the value to AT. -->\n <span class=\"mk-visually-hidden\">{{ cell.display }}</span>\n }\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n</div>\n\n@if (showScale()) {\n <div class=\"mk-heatmap__legend\" aria-hidden=\"true\">\n <span class=\"mk-heatmap__legend-label\">{{ scaleStops().min }}</span>\n <span class=\"mk-heatmap__legend-ramp\">\n @for (sw of scaleStops().swatches; track $index) {\n <span class=\"mk-heatmap__legend-swatch\" [style.background]=\"sw\"></span>\n }\n </span>\n <span class=\"mk-heatmap__legend-label\">{{ scaleStops().max }}</span>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;font-family:var(--mk-font-sans)}.mk-heatmap__scroll{width:100%;overflow-x:auto}.mk-heatmap__scroll:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:calc(-1 * var(--mk-focus-ring-width))}.mk-heatmap__grid{border-collapse:separate;border-spacing:3px;font-size:var(--mk-font-size-sm);color:var(--mk-text)}.mk-heatmap__corner{border:0}.mk-heatmap__col-head,.mk-heatmap__row-head{padding:var(--mk-space-1) var(--mk-space-2);font-weight:var(--mk-font-weight-medium);color:var(--mk-text-muted);white-space:nowrap}.mk-heatmap__col-head{text-align:center;vertical-align:bottom}.mk-heatmap__row-head{text-align:end}.mk-heatmap__cell{width:2.25rem;height:2.25rem;padding:0;text-align:center;vertical-align:middle;border-radius:var(--mk-radius-sm);color:var(--mk-text);transition:outline-color var(--mk-duration-fast) var(--mk-ease-standard);outline:2px solid transparent;outline-offset:-2px}.mk-heatmap__cell:hover{outline-color:var(--mk-border-strong)}.mk-heatmap__cell--inverse{color:var(--mk-text-inverse)}.mk-heatmap__cell--empty{outline:none}.mk-heatmap__cell--empty:hover{outline-color:transparent}.mk-heatmap__value{font-size:var(--mk-font-size-xs);font-variant-numeric:tabular-nums}.mk-heatmap__legend{display:flex;align-items:center;gap:var(--mk-space-2);margin-top:var(--mk-space-3);font-size:var(--mk-font-size-xs);color:var(--mk-text-muted)}.mk-heatmap__legend-ramp{display:inline-flex;gap:2px}.mk-heatmap__legend-swatch{width:1.1rem;height:.75rem;border-radius:2px;box-shadow:inset 0 0 0 1px var(--mk-border-subtle)}\n"] }]
|
|
3117
|
+
}], ctorParameters: () => [], propDecorators: { xLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "xLabels", required: false }] }], yLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "yLabels", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], accent: [{ type: i0.Input, args: [{ isSignal: true, alias: "accent", required: false }] }], showValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValues", required: false }] }], showScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "showScale", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
2335
3118
|
|
|
2336
3119
|
/** Local midnight for `d` (drops the time-of-day). */
|
|
2337
3120
|
function stripTime(d) {
|
|
@@ -3135,6 +3918,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
3135
3918
|
class MkInlineEdit {
|
|
3136
3919
|
injector = inject(Injector);
|
|
3137
3920
|
field = inject(MkFieldContext, { optional: true });
|
|
3921
|
+
/** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
|
|
3922
|
+
fieldTouched = mkInjectFieldTouched();
|
|
3138
3923
|
i18n = inject(MK_I18N);
|
|
3139
3924
|
displayRef = viewChild('display', /* @ts-ignore */
|
|
3140
3925
|
...(ngDevMode ? [{ debugName: "displayRef" }] : /* istanbul ignore next */ []));
|
|
@@ -3177,7 +3962,7 @@ class MkInlineEdit {
|
|
|
3177
3962
|
...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
|
|
3178
3963
|
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
3179
3964
|
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
3180
|
-
isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
|
|
3965
|
+
isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
|
|
3181
3966
|
...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
|
|
3182
3967
|
labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
|
|
3183
3968
|
...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
|
|
@@ -3989,11 +4774,11 @@ class MkCarousel {
|
|
|
3989
4774
|
this.io = undefined;
|
|
3990
4775
|
}
|
|
3991
4776
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkCarousel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3992
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkCarousel, isStandalone: true, selector: "mk-carousel", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, showDots: { classPropertyName: "showDots", publicName: "showDots", isSignal: true, isRequired: false, transformFunction: null }, showArrows: { classPropertyName: "showArrows", publicName: "showArrows", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null }, interval: { classPropertyName: "interval", publicName: "interval", isSignal: true, isRequired: false, transformFunction: null }, userPaused: { classPropertyName: "userPaused", publicName: "userPaused", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { index: "indexChange", userPaused: "userPausedChange" }, host: { classAttribute: "mk-carousel" }, queries: [{ propertyName: "slides", predicate: MkCarouselSlide, isSignal: true }], ngImport: i0, template: "<div\n class=\"mk-carousel__viewport\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n [attr.aria-label]=\"ariaLabel()\"\n tabindex=\"0\"\n (keydown)=\"onKeydown($event)\"\n (mouseenter)=\"pause()\"\n (mouseleave)=\"resume()\"\n (focusin)=\"pause()\"\n (focusout)=\"resume()\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n>\n @if (autoplay()) {\n <button\n type=\"button\"\n class=\"mk-carousel__playpause\"\n [attr.aria-label]=\"userPaused() ? i18n.playSlideshow : i18n.pauseSlideshow\"\n [attr.aria-pressed]=\"userPaused()\"\n (click)=\"toggleUserPause()\"\n >\n <span aria-hidden=\"true\">{{ userPaused() ? '\u25B6' : '\u23F8' }}</span>\n </button>\n }\n <div\n class=\"mk-carousel__track\"\n [class.mk-carousel__track--dragging]=\"dragging()\"\n [style.transform]=\"trackTransform()\"\n >\n <ng-content />\n </div>\n\n @if (showArrows() && count() > 1) {\n <button\n type=\"button\"\n class=\"mk-carousel__arrow mk-carousel__arrow--prev\"\n [attr.aria-label]=\"i18n.previousSlide\"\n (click)=\"prev()\"\n >\n <span aria-hidden=\"true\">\u2039</span>\n </button>\n <button\n type=\"button\"\n class=\"mk-carousel__arrow mk-carousel__arrow--next\"\n [attr.aria-label]=\"i18n.nextSlide\"\n (click)=\"next()\"\n >\n <span aria-hidden=\"true\">\u203A</span>\n </button>\n }\n</div>\n\n@if (showDots() && count() > 1) {\n <div class=\"mk-carousel__dots\">\n @for (dot of dots(); track dot) {\n <button\n type=\"button\"\n class=\"mk-carousel__dot\"\n [class.mk-carousel__dot--active]=\"dot === index()\"\n [attr.aria-current]=\"dot === index() || null\"\n [attr.aria-label]=\"i18n.goToSlide(dot + 1)\"\n (click)=\"goTo(dot)\"\n ></button>\n }\n </div>\n}\n\n<div class=\"mk-visually-hidden\" [attr.aria-live]=\"liveMode()\">{{ liveText() }}</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;font-family:var(--mk-font-sans)}.mk-carousel__viewport{position:relative;overflow:hidden;border-radius:var(--mk-radius-md);touch-action:pan-y}.mk-carousel__viewport:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__track{display:flex;transition:transform var(--mk-duration-normal, .24s) var(--mk-ease-standard)}.mk-carousel__track--dragging{transition:none}.mk-carousel__slide{flex:0 0 100%;min-width:0}.mk-carousel__playpause{position:absolute;top:var(--mk-space-2);inset-inline-start:var(--mk-space-2);z-index:1;display:grid;place-items:center;width:2rem;height:2rem;padding:0;font-size:var(--mk-font-size-sm);line-height:1;color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__playpause:hover{background-color:var(--mk-surface-2)}.mk-carousel__playpause:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);display:grid;place-items:center;width:2rem;height:2rem;font-size:var(--mk-font-size-lg);line-height:1;color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__arrow:hover{background-color:var(--mk-surface-2)}.mk-carousel__arrow:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__arrow--prev{inset-inline-start:var(--mk-space-2)}.mk-carousel__arrow--next{inset-inline-end:var(--mk-space-2)}.mk-carousel__arrow:dir(rtl) span{display:inline-block;transform:scaleX(-1)}.mk-carousel__dots{display:flex;justify-content:center;gap:var(--mk-space-2);margin-top:var(--mk-space-2)}.mk-carousel__dot{width:.5rem;height:.5rem;padding:0;background-color:var(--mk-border-strong);border:none;border-radius:var(--mk-radius-circle);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard),transform var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__dot:hover{background-color:var(--mk-text-muted)}.mk-carousel__dot:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__dot--active{background-color:var(--mk-primary);transform:scale(1.25)}@media(prefers-reduced-motion:reduce){.mk-carousel__track,.mk-carousel__arrow,.mk-carousel__dot{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4777
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkCarousel, isStandalone: true, selector: "mk-carousel", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, showDots: { classPropertyName: "showDots", publicName: "showDots", isSignal: true, isRequired: false, transformFunction: null }, showArrows: { classPropertyName: "showArrows", publicName: "showArrows", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null }, interval: { classPropertyName: "interval", publicName: "interval", isSignal: true, isRequired: false, transformFunction: null }, userPaused: { classPropertyName: "userPaused", publicName: "userPaused", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { index: "indexChange", userPaused: "userPausedChange" }, host: { classAttribute: "mk-carousel" }, queries: [{ propertyName: "slides", predicate: MkCarouselSlide, isSignal: true }], ngImport: i0, template: "<div\n class=\"mk-carousel__viewport\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n [attr.aria-label]=\"ariaLabel()\"\n tabindex=\"0\"\n (keydown)=\"onKeydown($event)\"\n (mouseenter)=\"pause()\"\n (mouseleave)=\"resume()\"\n (focusin)=\"pause()\"\n (focusout)=\"resume()\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n>\n @if (autoplay()) {\n <button\n type=\"button\"\n class=\"mk-carousel__playpause\"\n [attr.aria-label]=\"userPaused() ? i18n.playSlideshow : i18n.pauseSlideshow\"\n [attr.aria-pressed]=\"userPaused()\"\n (click)=\"toggleUserPause()\"\n >\n <span aria-hidden=\"true\">{{ userPaused() ? '\u25B6' : '\u23F8' }}</span>\n </button>\n }\n <div\n class=\"mk-carousel__track\"\n [class.mk-carousel__track--dragging]=\"dragging()\"\n [style.transform]=\"trackTransform()\"\n >\n <ng-content />\n </div>\n\n @if (showArrows() && count() > 1) {\n <button\n type=\"button\"\n class=\"mk-carousel__arrow mk-carousel__arrow--prev\"\n [attr.aria-label]=\"i18n.previousSlide\"\n (click)=\"prev()\"\n >\n <span aria-hidden=\"true\">\u2039</span>\n </button>\n <button\n type=\"button\"\n class=\"mk-carousel__arrow mk-carousel__arrow--next\"\n [attr.aria-label]=\"i18n.nextSlide\"\n (click)=\"next()\"\n >\n <span aria-hidden=\"true\">\u203A</span>\n </button>\n }\n</div>\n\n@if (showDots() && count() > 1) {\n <div class=\"mk-carousel__dots\">\n @for (dot of dots(); track dot) {\n <button\n type=\"button\"\n class=\"mk-carousel__dot\"\n [class.mk-carousel__dot--active]=\"dot === index()\"\n [attr.aria-current]=\"dot === index() || null\"\n [attr.aria-label]=\"i18n.goToSlide(dot + 1)\"\n (click)=\"goTo(dot)\"\n ></button>\n }\n </div>\n}\n\n<div class=\"mk-visually-hidden\" [attr.aria-live]=\"liveMode()\">{{ liveText() }}</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;font-family:var(--mk-font-sans)}.mk-carousel__viewport{position:relative;overflow:hidden;border-radius:var(--mk-radius-md);touch-action:pan-y}.mk-carousel__viewport:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__track{display:flex;transition:transform var(--mk-duration-normal, .24s) var(--mk-ease-standard)}.mk-carousel__track--dragging{transition:none}:host ::ng-deep .mk-carousel__slide{flex:0 0 100%;min-width:0;box-sizing:border-box}.mk-carousel__playpause{position:absolute;top:var(--mk-space-2);inset-inline-start:var(--mk-space-2);z-index:1;display:grid;place-items:center;width:2rem;height:2rem;padding:0;font-size:var(--mk-font-size-sm);line-height:1;color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__playpause:hover{background-color:var(--mk-surface-2)}.mk-carousel__playpause:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);display:grid;place-items:center;width:2rem;height:2rem;font-size:var(--mk-font-size-lg);line-height:1;color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__arrow:hover{background-color:var(--mk-surface-2)}.mk-carousel__arrow:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__arrow--prev{inset-inline-start:var(--mk-space-2)}.mk-carousel__arrow--next{inset-inline-end:var(--mk-space-2)}.mk-carousel__arrow:dir(rtl) span{display:inline-block;transform:scaleX(-1)}.mk-carousel__dots{display:flex;justify-content:center;gap:var(--mk-space-2);margin-top:var(--mk-space-2)}.mk-carousel__dot{width:.5rem;height:.5rem;padding:0;background-color:var(--mk-border-strong);border:none;border-radius:var(--mk-radius-circle);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard),transform var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__dot:hover{background-color:var(--mk-text-muted)}.mk-carousel__dot:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__dot--active{background-color:var(--mk-primary);transform:scale(1.25)}@media(prefers-reduced-motion:reduce){.mk-carousel__track,.mk-carousel__arrow,.mk-carousel__dot{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3993
4778
|
}
|
|
3994
4779
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkCarousel, decorators: [{
|
|
3995
4780
|
type: Component,
|
|
3996
|
-
args: [{ selector: 'mk-carousel', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'mk-carousel' }, template: "<div\n class=\"mk-carousel__viewport\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n [attr.aria-label]=\"ariaLabel()\"\n tabindex=\"0\"\n (keydown)=\"onKeydown($event)\"\n (mouseenter)=\"pause()\"\n (mouseleave)=\"resume()\"\n (focusin)=\"pause()\"\n (focusout)=\"resume()\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n>\n @if (autoplay()) {\n <button\n type=\"button\"\n class=\"mk-carousel__playpause\"\n [attr.aria-label]=\"userPaused() ? i18n.playSlideshow : i18n.pauseSlideshow\"\n [attr.aria-pressed]=\"userPaused()\"\n (click)=\"toggleUserPause()\"\n >\n <span aria-hidden=\"true\">{{ userPaused() ? '\u25B6' : '\u23F8' }}</span>\n </button>\n }\n <div\n class=\"mk-carousel__track\"\n [class.mk-carousel__track--dragging]=\"dragging()\"\n [style.transform]=\"trackTransform()\"\n >\n <ng-content />\n </div>\n\n @if (showArrows() && count() > 1) {\n <button\n type=\"button\"\n class=\"mk-carousel__arrow mk-carousel__arrow--prev\"\n [attr.aria-label]=\"i18n.previousSlide\"\n (click)=\"prev()\"\n >\n <span aria-hidden=\"true\">\u2039</span>\n </button>\n <button\n type=\"button\"\n class=\"mk-carousel__arrow mk-carousel__arrow--next\"\n [attr.aria-label]=\"i18n.nextSlide\"\n (click)=\"next()\"\n >\n <span aria-hidden=\"true\">\u203A</span>\n </button>\n }\n</div>\n\n@if (showDots() && count() > 1) {\n <div class=\"mk-carousel__dots\">\n @for (dot of dots(); track dot) {\n <button\n type=\"button\"\n class=\"mk-carousel__dot\"\n [class.mk-carousel__dot--active]=\"dot === index()\"\n [attr.aria-current]=\"dot === index() || null\"\n [attr.aria-label]=\"i18n.goToSlide(dot + 1)\"\n (click)=\"goTo(dot)\"\n ></button>\n }\n </div>\n}\n\n<div class=\"mk-visually-hidden\" [attr.aria-live]=\"liveMode()\">{{ liveText() }}</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;font-family:var(--mk-font-sans)}.mk-carousel__viewport{position:relative;overflow:hidden;border-radius:var(--mk-radius-md);touch-action:pan-y}.mk-carousel__viewport:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__track{display:flex;transition:transform var(--mk-duration-normal, .24s) var(--mk-ease-standard)}.mk-carousel__track--dragging{transition:none}.mk-carousel__slide{flex:0 0 100%;min-width:0}.mk-carousel__playpause{position:absolute;top:var(--mk-space-2);inset-inline-start:var(--mk-space-2);z-index:1;display:grid;place-items:center;width:2rem;height:2rem;padding:0;font-size:var(--mk-font-size-sm);line-height:1;color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__playpause:hover{background-color:var(--mk-surface-2)}.mk-carousel__playpause:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);display:grid;place-items:center;width:2rem;height:2rem;font-size:var(--mk-font-size-lg);line-height:1;color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__arrow:hover{background-color:var(--mk-surface-2)}.mk-carousel__arrow:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__arrow--prev{inset-inline-start:var(--mk-space-2)}.mk-carousel__arrow--next{inset-inline-end:var(--mk-space-2)}.mk-carousel__arrow:dir(rtl) span{display:inline-block;transform:scaleX(-1)}.mk-carousel__dots{display:flex;justify-content:center;gap:var(--mk-space-2);margin-top:var(--mk-space-2)}.mk-carousel__dot{width:.5rem;height:.5rem;padding:0;background-color:var(--mk-border-strong);border:none;border-radius:var(--mk-radius-circle);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard),transform var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__dot:hover{background-color:var(--mk-text-muted)}.mk-carousel__dot:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__dot--active{background-color:var(--mk-primary);transform:scale(1.25)}@media(prefers-reduced-motion:reduce){.mk-carousel__track,.mk-carousel__arrow,.mk-carousel__dot{transition:none}}\n"] }]
|
|
4781
|
+
args: [{ selector: 'mk-carousel', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'mk-carousel' }, template: "<div\n class=\"mk-carousel__viewport\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n [attr.aria-label]=\"ariaLabel()\"\n tabindex=\"0\"\n (keydown)=\"onKeydown($event)\"\n (mouseenter)=\"pause()\"\n (mouseleave)=\"resume()\"\n (focusin)=\"pause()\"\n (focusout)=\"resume()\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n>\n @if (autoplay()) {\n <button\n type=\"button\"\n class=\"mk-carousel__playpause\"\n [attr.aria-label]=\"userPaused() ? i18n.playSlideshow : i18n.pauseSlideshow\"\n [attr.aria-pressed]=\"userPaused()\"\n (click)=\"toggleUserPause()\"\n >\n <span aria-hidden=\"true\">{{ userPaused() ? '\u25B6' : '\u23F8' }}</span>\n </button>\n }\n <div\n class=\"mk-carousel__track\"\n [class.mk-carousel__track--dragging]=\"dragging()\"\n [style.transform]=\"trackTransform()\"\n >\n <ng-content />\n </div>\n\n @if (showArrows() && count() > 1) {\n <button\n type=\"button\"\n class=\"mk-carousel__arrow mk-carousel__arrow--prev\"\n [attr.aria-label]=\"i18n.previousSlide\"\n (click)=\"prev()\"\n >\n <span aria-hidden=\"true\">\u2039</span>\n </button>\n <button\n type=\"button\"\n class=\"mk-carousel__arrow mk-carousel__arrow--next\"\n [attr.aria-label]=\"i18n.nextSlide\"\n (click)=\"next()\"\n >\n <span aria-hidden=\"true\">\u203A</span>\n </button>\n }\n</div>\n\n@if (showDots() && count() > 1) {\n <div class=\"mk-carousel__dots\">\n @for (dot of dots(); track dot) {\n <button\n type=\"button\"\n class=\"mk-carousel__dot\"\n [class.mk-carousel__dot--active]=\"dot === index()\"\n [attr.aria-current]=\"dot === index() || null\"\n [attr.aria-label]=\"i18n.goToSlide(dot + 1)\"\n (click)=\"goTo(dot)\"\n ></button>\n }\n </div>\n}\n\n<div class=\"mk-visually-hidden\" [attr.aria-live]=\"liveMode()\">{{ liveText() }}</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;font-family:var(--mk-font-sans)}.mk-carousel__viewport{position:relative;overflow:hidden;border-radius:var(--mk-radius-md);touch-action:pan-y}.mk-carousel__viewport:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__track{display:flex;transition:transform var(--mk-duration-normal, .24s) var(--mk-ease-standard)}.mk-carousel__track--dragging{transition:none}:host ::ng-deep .mk-carousel__slide{flex:0 0 100%;min-width:0;box-sizing:border-box}.mk-carousel__playpause{position:absolute;top:var(--mk-space-2);inset-inline-start:var(--mk-space-2);z-index:1;display:grid;place-items:center;width:2rem;height:2rem;padding:0;font-size:var(--mk-font-size-sm);line-height:1;color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__playpause:hover{background-color:var(--mk-surface-2)}.mk-carousel__playpause:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);display:grid;place-items:center;width:2rem;height:2rem;font-size:var(--mk-font-size-lg);line-height:1;color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__arrow:hover{background-color:var(--mk-surface-2)}.mk-carousel__arrow:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__arrow--prev{inset-inline-start:var(--mk-space-2)}.mk-carousel__arrow--next{inset-inline-end:var(--mk-space-2)}.mk-carousel__arrow:dir(rtl) span{display:inline-block;transform:scaleX(-1)}.mk-carousel__dots{display:flex;justify-content:center;gap:var(--mk-space-2);margin-top:var(--mk-space-2)}.mk-carousel__dot{width:.5rem;height:.5rem;padding:0;background-color:var(--mk-border-strong);border:none;border-radius:var(--mk-radius-circle);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard),transform var(--mk-duration-fast) var(--mk-ease-standard)}.mk-carousel__dot:hover{background-color:var(--mk-text-muted)}.mk-carousel__dot:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-carousel__dot--active{background-color:var(--mk-primary);transform:scale(1.25)}@media(prefers-reduced-motion:reduce){.mk-carousel__track,.mk-carousel__arrow,.mk-carousel__dot{transition:none}}\n"] }]
|
|
3997
4782
|
}], ctorParameters: () => [], propDecorators: { slides: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MkCarouselSlide), { isSignal: true }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: false }] }, { type: i0.Output, args: ["indexChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], showDots: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDots", required: false }] }], showArrows: [{ type: i0.Input, args: [{ isSignal: true, alias: "showArrows", required: false }] }], autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], interval: [{ type: i0.Input, args: [{ isSignal: true, alias: "interval", required: false }] }], userPaused: [{ type: i0.Input, args: [{ isSignal: true, alias: "userPaused", required: false }] }, { type: i0.Output, args: ["userPausedChange"] }] } });
|
|
3998
4783
|
|
|
3999
4784
|
/**
|
|
@@ -6137,5 +6922,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
6137
6922
|
* Generated bundle index. Do not edit.
|
|
6138
6923
|
*/
|
|
6139
6924
|
|
|
6140
|
-
export { MK_CHART_PALETTE_SIZE, MkAvatar, MkAvatarGroup, MkBadge, MkBarChart, MkCalendarHeatmap, MkCard, MkCardFooter, MkCardHeader, MkCardTitle, MkCarousel, MkCarouselSlide, MkCode, MkCountdown, MkDescItem, MkDescriptionList, MkDiff, MkDivider, MkDonutChart, MkEmptyState, MkFunnelChart, MkGauge, MkHeatmap, MkInlineEdit, MkJsonViewer, MkKanban, MkLineChart, MkList, MkListItem, MkLogViewer, MkMarkdown, MkProfileActions, MkProfileCard, MkProfileMeta, MkProgressBar, MkProgressRing, MkQrCode, MkRadarChart, MkScatterChart, MkSkeleton, MkSkeletonPreset, MkSparkline, MkSpinner, MkStatCard, MkTag, MkTimeline, MkTimelineItem, MkTreemap, MkVirtualScroll, mkArcPath, mkAreaPath, mkBuildJsonTree, mkChartColor, mkComputeDiff, mkDiffStats, mkEncodeQr, mkFormatCompact, mkLinePath, mkLinearScale, mkNiceTicks, mkParseAnsi, mkParseMarkdown, mkRenderMarkdown, mkSplitDuration, mkSquarify, mkStripAnsi };
|
|
6925
|
+
export { MK_CHART_PALETTE_SIZE, MK_HEATMAP_MIN_CONTRAST, MkAvatar, MkAvatarGroup, MkBadge, MkBadgeOverlay, MkBarChart, MkCalendarHeatmap, MkCard, MkCardFooter, MkCardHeader, MkCardTitle, MkCarousel, MkCarouselSlide, MkCode, MkCountdown, MkDescItem, MkDescriptionList, MkDiff, MkDivider, MkDonutChart, MkEmptyState, MkFunnelChart, MkGauge, MkHeatmap, MkInlineEdit, MkJsonViewer, MkKanban, MkLineChart, MkList, MkListItem, MkLogViewer, MkMarkdown, MkOrgChart, MkOrgChartNodeDef, MkProfileActions, MkProfileCard, MkProfileMeta, MkProgressBar, MkProgressRing, MkQrCode, MkRadarChart, MkScatterChart, MkSkeleton, MkSkeletonPreset, MkSparkline, MkSpinner, MkStatCard, MkTag, MkTimeline, MkTimelineItem, MkTreemap, MkVirtualScroll, mkArcPath, mkAreaPath, mkBuildJsonTree, mkChartColor, mkComputeDiff, mkContrastRatio, mkDiffStats, mkEncodeQr, mkFormatCompact, mkHeatmapCellColors, mkLinePath, mkLinearScale, mkMixRgb, mkNiceTicks, mkOrgChartFromFlat, mkParseAnsi, mkParseMarkdown, mkParseRgb, mkRelativeLuminance, mkRenderMarkdown, mkSplitDuration, mkSquarify, mkStripAnsi };
|
|
6141
6926
|
//# sourceMappingURL=mk-kit-ui-data.mjs.map
|