@frame-kit/ui-ng-patterns 0.1.5 → 0.1.7
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.
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, computed, HostBinding, inject, TemplateRef, Directive, ViewContainerRef, DestroyRef, Injector, viewChild, contentChild, signal, effect, afterNextRender } from '@angular/core';
|
|
2
|
+
import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, computed, HostBinding, inject, TemplateRef, Directive, ViewContainerRef, DestroyRef, Injector, viewChild, contentChild, signal, effect, afterNextRender, output, ElementRef, linkedSignal } from '@angular/core';
|
|
3
3
|
import { HeadlineComponent } from '@frame-kit/ui-ng/core/headline';
|
|
4
4
|
import { TextComponent } from '@frame-kit/ui-ng/core/text';
|
|
5
5
|
import { IconComponent } from '@frame-kit/ui-ng/core/icon';
|
|
6
6
|
import { Overlay } from '@angular/cdk/overlay';
|
|
7
7
|
import { TemplatePortal } from '@angular/cdk/portal';
|
|
8
8
|
import { NgTemplateOutlet } from '@angular/common';
|
|
9
|
-
import { toSignal } from '@angular/core/rxjs-interop';
|
|
9
|
+
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
10
10
|
import { Router, ActivatedRoute, NavigationEnd, RouterLink } from '@angular/router';
|
|
11
11
|
import { filter, map, Subscription } from 'rxjs';
|
|
12
12
|
import { TooltipDirective } from '@frame-kit/ui-ng/directives/tooltip';
|
|
13
13
|
import { AppShellComponent } from '@frame-kit/ui-ng/layouts/app-shell';
|
|
14
|
+
import { ButtonComponent } from '@frame-kit/ui-ng/ui/button';
|
|
15
|
+
import { IconBadgeComponent } from '@frame-kit/ui-ng/ui/icon-badge';
|
|
16
|
+
import * as i1 from '@angular/cdk/drag-drop';
|
|
17
|
+
import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* Page-level dashboard header: title + optional description on the left, up to
|
|
@@ -645,9 +649,569 @@ function stripUrl(url) {
|
|
|
645
649
|
return url.split('?')[0].split('#')[0];
|
|
646
650
|
}
|
|
647
651
|
|
|
652
|
+
/**
|
|
653
|
+
* The fixed footer chrome for {@link DialogShellComponent} — an optional muted
|
|
654
|
+
* note on the left, then the projected action buttons pushed to the right,
|
|
655
|
+
* above a top divider. Paired with {@link DialogHeaderComponent} so every
|
|
656
|
+
* dialog shares the same chrome.
|
|
657
|
+
*
|
|
658
|
+
* The note icon is optional and degrades gracefully — an empty `noteIcon`
|
|
659
|
+
* simply renders no glyph.
|
|
660
|
+
*
|
|
661
|
+
* ```html
|
|
662
|
+
* <fk-dialog-footer note="Takes effect immediately" noteIcon="power">
|
|
663
|
+
* <fk-button variant="outline" (click)="cancel()">Cancel</fk-button>
|
|
664
|
+
* <fk-button variant="primary" type="submit">Save</fk-button>
|
|
665
|
+
* </fk-dialog-footer>
|
|
666
|
+
* ```
|
|
667
|
+
*/
|
|
668
|
+
class DialogFooterComponent {
|
|
669
|
+
// ===== INPUTS =====
|
|
670
|
+
/** Optional muted note shown on the left (e.g. "Takes effect immediately"). */
|
|
671
|
+
note = input('', ...(ngDevMode ? [{ debugName: "note" }] : /* istanbul ignore next */ []));
|
|
672
|
+
/** Optional icon glyph rendered before the note. Empty renders no glyph. */
|
|
673
|
+
noteIcon = input('', ...(ngDevMode ? [{ debugName: "noteIcon" }] : /* istanbul ignore next */ []));
|
|
674
|
+
// ===== BASE PROPS =====
|
|
675
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
676
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
677
|
+
ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
678
|
+
// ===== COMPUTED =====
|
|
679
|
+
classes = computed(() => ['fk-dialog-footer', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
680
|
+
get hostClass() {
|
|
681
|
+
return this.classes();
|
|
682
|
+
}
|
|
683
|
+
get hostId() {
|
|
684
|
+
return this.id();
|
|
685
|
+
}
|
|
686
|
+
get hostAriaLabel() {
|
|
687
|
+
return this.ariaLabel();
|
|
688
|
+
}
|
|
689
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
690
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DialogFooterComponent, isStandalone: true, selector: "fk-dialog-footer", inputs: { note: { classPropertyName: "note", publicName: "note", isSignal: true, isRequired: false, transformFunction: null }, noteIcon: { classPropertyName: "noteIcon", publicName: "noteIcon", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId", "attr.aria-label": "this.hostAriaLabel" } }, ngImport: i0, template: "@if (note()) {\n <span class=\"fk-dialog-footer__note\">\n @if (noteIcon()) {\n <fk-icon\n class=\"fk-dialog-footer__note-icon\"\n [name]=\"noteIcon()\"\n size=\"sm\"\n />\n }\n {{ note() }}\n </span>\n}\n\n<span class=\"fk-dialog-footer__spacer\"></span>\n\n<ng-content />\n", styles: [":host{display:flex;align-items:center;gap:var(--fk-dialog-footer-gap, var(--fk-rhythm-3, .75rem));margin-top:var(--fk-dialog-footer-margin-top, var(--fk-rhythm-5, 1.5rem));padding-top:var(--fk-dialog-footer-padding-top, var(--fk-rhythm-5, 1.5rem));border-top:1px solid var(--fk-dialog-footer-divider-color, var(--fk-color-surface-dim, #f1f5f9))}.fk-dialog-footer__note{display:inline-flex;align-items:center;gap:.375rem;font-size:var(--fk-dialog-footer-note-font-size, .71875rem);color:var(--fk-dialog-footer-note-color, var(--fk-color-muted, #64748b))}.fk-dialog-footer__note-icon{flex:none;color:var(--fk-dialog-footer-note-color, var(--fk-color-muted, #64748b))}.fk-dialog-footer__spacer{flex:1}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
691
|
+
}
|
|
692
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogFooterComponent, decorators: [{
|
|
693
|
+
type: Component,
|
|
694
|
+
args: [{ selector: 'fk-dialog-footer', standalone: true, imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (note()) {\n <span class=\"fk-dialog-footer__note\">\n @if (noteIcon()) {\n <fk-icon\n class=\"fk-dialog-footer__note-icon\"\n [name]=\"noteIcon()\"\n size=\"sm\"\n />\n }\n {{ note() }}\n </span>\n}\n\n<span class=\"fk-dialog-footer__spacer\"></span>\n\n<ng-content />\n", styles: [":host{display:flex;align-items:center;gap:var(--fk-dialog-footer-gap, var(--fk-rhythm-3, .75rem));margin-top:var(--fk-dialog-footer-margin-top, var(--fk-rhythm-5, 1.5rem));padding-top:var(--fk-dialog-footer-padding-top, var(--fk-rhythm-5, 1.5rem));border-top:1px solid var(--fk-dialog-footer-divider-color, var(--fk-color-surface-dim, #f1f5f9))}.fk-dialog-footer__note{display:inline-flex;align-items:center;gap:.375rem;font-size:var(--fk-dialog-footer-note-font-size, .71875rem);color:var(--fk-dialog-footer-note-color, var(--fk-color-muted, #64748b))}.fk-dialog-footer__note-icon{flex:none;color:var(--fk-dialog-footer-note-color, var(--fk-color-muted, #64748b))}.fk-dialog-footer__spacer{flex:1}\n"] }]
|
|
695
|
+
}], propDecorators: { note: [{ type: i0.Input, args: [{ isSignal: true, alias: "note", required: false }] }], noteIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "noteIcon", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hostClass: [{
|
|
696
|
+
type: HostBinding,
|
|
697
|
+
args: ['class']
|
|
698
|
+
}], hostId: [{
|
|
699
|
+
type: HostBinding,
|
|
700
|
+
args: ['attr.id']
|
|
701
|
+
}], hostAriaLabel: [{
|
|
702
|
+
type: HostBinding,
|
|
703
|
+
args: ['attr.aria-label']
|
|
704
|
+
}] } });
|
|
705
|
+
|
|
706
|
+
/** The tint tone → the CSS-variable pair driving the icon tile + title accent.
|
|
707
|
+
* Each reference is two-tier (component token → semantic token) so the header
|
|
708
|
+
* renders correctly with no theme loaded and can be retinted per theme. */
|
|
709
|
+
const TINTS = {
|
|
710
|
+
brand: {
|
|
711
|
+
color: 'var(--fk-dialog-header-tint-brand-color, var(--fk-color-primary, #2563eb))',
|
|
712
|
+
background: 'var(--fk-dialog-header-tint-brand-bg, var(--fk-color-primary-light, #eff6ff))',
|
|
713
|
+
},
|
|
714
|
+
warning: {
|
|
715
|
+
color: 'var(--fk-dialog-header-tint-warning-color, var(--fk-color-warning, #d97706))',
|
|
716
|
+
background: 'var(--fk-dialog-header-tint-warning-bg, var(--fk-color-warning-subtle, #fffbeb))',
|
|
717
|
+
},
|
|
718
|
+
success: {
|
|
719
|
+
color: 'var(--fk-dialog-header-tint-success-color, var(--fk-color-success, #16a34a))',
|
|
720
|
+
background: 'var(--fk-dialog-header-tint-success-bg, var(--fk-color-success-subtle, #f0fdf4))',
|
|
721
|
+
},
|
|
722
|
+
danger: {
|
|
723
|
+
color: 'var(--fk-dialog-header-tint-danger-color, var(--fk-color-danger, #dc2626))',
|
|
724
|
+
background: 'var(--fk-dialog-header-tint-danger-bg, var(--fk-color-danger-light, #fee2e2))',
|
|
725
|
+
},
|
|
726
|
+
accent: {
|
|
727
|
+
color: 'var(--fk-dialog-header-tint-accent-color, #7c3aed)',
|
|
728
|
+
background: 'var(--fk-dialog-header-tint-accent-bg, #f5f3ff)',
|
|
729
|
+
},
|
|
730
|
+
};
|
|
731
|
+
/**
|
|
732
|
+
* The fixed header chrome for {@link DialogShellComponent} — a tinted icon
|
|
733
|
+
* tile, a title with an optional trailing accent, an optional mono subtitle,
|
|
734
|
+
* and a close button. Every dialog wears the same chrome; only the icon, tint,
|
|
735
|
+
* and text differ, so this centralises it instead of each dialog hand-rolling
|
|
736
|
+
* the markup.
|
|
737
|
+
*
|
|
738
|
+
* Text is passed in as plain inputs (no i18n dependency), and the icon is
|
|
739
|
+
* optional — an empty `icon` simply renders no tile, so the header degrades
|
|
740
|
+
* gracefully rather than showing a broken glyph.
|
|
741
|
+
*
|
|
742
|
+
* ```html
|
|
743
|
+
* <fk-dialog-header
|
|
744
|
+
* icon="user-ban"
|
|
745
|
+
* tint="warning"
|
|
746
|
+
* title="Deactivate identity"
|
|
747
|
+
* subtitle="Acme Inc. · Members"
|
|
748
|
+
* (dismiss)="close()"
|
|
749
|
+
* />
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
class DialogHeaderComponent {
|
|
753
|
+
// ===== INPUTS =====
|
|
754
|
+
/** Icon glyph name for the tile. Empty renders no tile. */
|
|
755
|
+
icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
756
|
+
/** Colour accent for the icon tile + title accent. */
|
|
757
|
+
tint = input('brand', ...(ngDevMode ? [{ debugName: "tint" }] : /* istanbul ignore next */ []));
|
|
758
|
+
/** Dialog title. */
|
|
759
|
+
title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
760
|
+
/** Optional trailing accent appended to the title, rendered in the tint colour. */
|
|
761
|
+
titleAccent = input('', ...(ngDevMode ? [{ debugName: "titleAccent" }] : /* istanbul ignore next */ []));
|
|
762
|
+
/** Optional mono subtitle under the title. */
|
|
763
|
+
subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
764
|
+
/** Accessible label for the close button. */
|
|
765
|
+
closeLabel = input('Close', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
|
|
766
|
+
// ===== OUTPUTS =====
|
|
767
|
+
/** Fires when the close button is clicked. */
|
|
768
|
+
dismiss = output();
|
|
769
|
+
// ===== BASE PROPS =====
|
|
770
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
771
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
772
|
+
ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
773
|
+
// ===== COMPUTED =====
|
|
774
|
+
/** The tint's `{ color, background }` CSS-variable pair. */
|
|
775
|
+
tintVars = computed(() => TINTS[this.tint()], ...(ngDevMode ? [{ debugName: "tintVars" }] : /* istanbul ignore next */ []));
|
|
776
|
+
classes = computed(() => ['fk-dialog-header', `fk-dialog-header--${this.tint()}`, this.className()]
|
|
777
|
+
.filter(Boolean)
|
|
778
|
+
.join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
779
|
+
get hostClass() {
|
|
780
|
+
return this.classes();
|
|
781
|
+
}
|
|
782
|
+
get hostId() {
|
|
783
|
+
return this.id();
|
|
784
|
+
}
|
|
785
|
+
get hostAriaLabel() {
|
|
786
|
+
return this.ariaLabel();
|
|
787
|
+
}
|
|
788
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
789
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DialogHeaderComponent, isStandalone: true, selector: "fk-dialog-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, tint: { classPropertyName: "tint", publicName: "tint", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, titleAccent: { classPropertyName: "titleAccent", publicName: "titleAccent", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId", "attr.aria-label": "this.hostAriaLabel" } }, ngImport: i0, template: "@if (icon()) {\n <fk-icon-badge\n class=\"fk-dialog-header__icon\"\n [name]=\"icon()\"\n [color]=\"tintVars().color\"\n [backgroundColor]=\"tintVars().background\"\n shape=\"rounded\"\n size=\"md\"\n />\n}\n\n<div class=\"fk-dialog-header__titles\">\n <fk-headline\n class=\"fk-dialog-header__title\"\n [level]=\"2\"\n fontSize=\"var(--fk-dialog-header-title-font-size, 1.25rem)\"\n >\n {{ title() }}\n @if (titleAccent()) {\n <span class=\"fk-dialog-header__accent\" [style.color]=\"tintVars().color\">\n {{ titleAccent() }}</span\n >\n }\n </fk-headline>\n @if (subtitle()) {\n <fk-text class=\"fk-dialog-header__subtitle\" variant=\"small\">\n {{ subtitle() }}\n </fk-text>\n }\n</div>\n\n<button\n type=\"button\"\n class=\"fk-dialog-header__close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"dismiss.emit()\"\n>\n <fk-icon name=\"close\" size=\"sm\" />\n</button>\n", styles: [":host{display:flex;align-items:flex-start;gap:var(--fk-dialog-header-gap, var(--fk-rhythm-3, .75rem))}.fk-dialog-header__icon{flex:none}.fk-dialog-header__titles{flex:1;min-width:0}.fk-dialog-header__title{--fk-headline-margin-block-start: 0}.fk-dialog-header__subtitle{--fk-text-margin-block-start: .1875rem;--fk-text-color-default: var( --fk-dialog-header-subtitle-color, var(--fk-color-muted, #64748b) );--fk-typography-small-font-size: .78125rem;--fk-typography-small-letter-spacing: .02em;font-family:var(--fk-dialog-header-subtitle-font-family, var(--fk-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace))}.fk-dialog-header__close{display:grid;place-items:center;flex:none;width:2rem;height:2rem;margin:-.25rem -.25rem 0 0;border:1px solid transparent;border-radius:var(--fk-dialog-header-close-radius, var(--fk-radius-sm, .25rem));background:transparent;color:var(--fk-dialog-header-close-color, var(--fk-color-muted, #64748b));cursor:pointer;transition:.15s}.fk-dialog-header__close:hover{border-color:var(--fk-dialog-header-close-border-hover, var(--fk-color-border, #e2e8f0));background:var(--fk-dialog-header-close-bg-hover, var(--fk-color-surface-muted, #f8fafc));color:var(--fk-dialog-header-close-color-hover, var(--fk-color-text, #1e293b))}\n"], dependencies: [{ kind: "component", type: HeadlineComponent, selector: "fk-headline", inputs: ["ariaLabel", "className", "fontSize", "id", "level", "variant", "visuallyHidden"] }, { kind: "component", type: IconBadgeComponent, selector: "fk-icon-badge", inputs: ["name", "size", "badgeSize", "shape", "color", "borderColor", "backgroundColor", "className", "id", "ariaLabel", "ariaHidden"] }, { kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }, { kind: "component", type: TextComponent, selector: "fk-text", inputs: ["as", "variant", "tone", "align", "weight", "truncate", "maxLines", "gutterBottom", "italic", "visuallyHidden", "className", "id", "ariaLabel", "ariaDescribedBy", "role", "htmlFor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
790
|
+
}
|
|
791
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogHeaderComponent, decorators: [{
|
|
792
|
+
type: Component,
|
|
793
|
+
args: [{ selector: 'fk-dialog-header', standalone: true, imports: [
|
|
794
|
+
HeadlineComponent,
|
|
795
|
+
IconBadgeComponent,
|
|
796
|
+
IconComponent,
|
|
797
|
+
TextComponent,
|
|
798
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (icon()) {\n <fk-icon-badge\n class=\"fk-dialog-header__icon\"\n [name]=\"icon()\"\n [color]=\"tintVars().color\"\n [backgroundColor]=\"tintVars().background\"\n shape=\"rounded\"\n size=\"md\"\n />\n}\n\n<div class=\"fk-dialog-header__titles\">\n <fk-headline\n class=\"fk-dialog-header__title\"\n [level]=\"2\"\n fontSize=\"var(--fk-dialog-header-title-font-size, 1.25rem)\"\n >\n {{ title() }}\n @if (titleAccent()) {\n <span class=\"fk-dialog-header__accent\" [style.color]=\"tintVars().color\">\n {{ titleAccent() }}</span\n >\n }\n </fk-headline>\n @if (subtitle()) {\n <fk-text class=\"fk-dialog-header__subtitle\" variant=\"small\">\n {{ subtitle() }}\n </fk-text>\n }\n</div>\n\n<button\n type=\"button\"\n class=\"fk-dialog-header__close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"dismiss.emit()\"\n>\n <fk-icon name=\"close\" size=\"sm\" />\n</button>\n", styles: [":host{display:flex;align-items:flex-start;gap:var(--fk-dialog-header-gap, var(--fk-rhythm-3, .75rem))}.fk-dialog-header__icon{flex:none}.fk-dialog-header__titles{flex:1;min-width:0}.fk-dialog-header__title{--fk-headline-margin-block-start: 0}.fk-dialog-header__subtitle{--fk-text-margin-block-start: .1875rem;--fk-text-color-default: var( --fk-dialog-header-subtitle-color, var(--fk-color-muted, #64748b) );--fk-typography-small-font-size: .78125rem;--fk-typography-small-letter-spacing: .02em;font-family:var(--fk-dialog-header-subtitle-font-family, var(--fk-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace))}.fk-dialog-header__close{display:grid;place-items:center;flex:none;width:2rem;height:2rem;margin:-.25rem -.25rem 0 0;border:1px solid transparent;border-radius:var(--fk-dialog-header-close-radius, var(--fk-radius-sm, .25rem));background:transparent;color:var(--fk-dialog-header-close-color, var(--fk-color-muted, #64748b));cursor:pointer;transition:.15s}.fk-dialog-header__close:hover{border-color:var(--fk-dialog-header-close-border-hover, var(--fk-color-border, #e2e8f0));background:var(--fk-dialog-header-close-bg-hover, var(--fk-color-surface-muted, #f8fafc));color:var(--fk-dialog-header-close-color-hover, var(--fk-color-text, #1e293b))}\n"] }]
|
|
799
|
+
}], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], tint: [{ type: i0.Input, args: [{ isSignal: true, alias: "tint", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], titleAccent: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleAccent", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], hostClass: [{
|
|
800
|
+
type: HostBinding,
|
|
801
|
+
args: ['class']
|
|
802
|
+
}], hostId: [{
|
|
803
|
+
type: HostBinding,
|
|
804
|
+
args: ['attr.id']
|
|
805
|
+
}], hostAriaLabel: [{
|
|
806
|
+
type: HostBinding,
|
|
807
|
+
args: ['attr.aria-label']
|
|
808
|
+
}] } });
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* The one dialog shell every dashboard dialog is built from. It owns the fixed
|
|
812
|
+
* chrome — the tinted icon-tile header (title / accent / subtitle + close) and
|
|
813
|
+
* the footer (muted note + the standard Cancel/confirm buttons) — so individual
|
|
814
|
+
* dialogs stop re-declaring any of it. Everything unique to a dialog lives in
|
|
815
|
+
* the projected **content region**.
|
|
816
|
+
*
|
|
817
|
+
* The shell carries no i18n dependency and no domain data: every label is a
|
|
818
|
+
* plain input (with English defaults for the built-in Cancel button), and the
|
|
819
|
+
* icons are inputs that degrade gracefully when empty. Render it inside the
|
|
820
|
+
* consuming app's own `fk-dialog` (opened bare — `header: null, closable:
|
|
821
|
+
* false` — with an `ariaLabel` for an accessible name).
|
|
822
|
+
*
|
|
823
|
+
* The common case needs zero footer markup — set the confirm label/variant and
|
|
824
|
+
* handle the outputs:
|
|
825
|
+
*
|
|
826
|
+
* ```html
|
|
827
|
+
* <fk-dialog-shell
|
|
828
|
+
* icon="user-ban"
|
|
829
|
+
* tint="warning"
|
|
830
|
+
* title="Deactivate identity"
|
|
831
|
+
* [subtitle]="subtitle"
|
|
832
|
+
* footerNote="Sign-in blocked"
|
|
833
|
+
* footerNoteIcon="power"
|
|
834
|
+
* confirmLabel="Deactivate"
|
|
835
|
+
* confirmIcon="user-ban"
|
|
836
|
+
* confirmVariant="warning"
|
|
837
|
+
* [confirmDisabled]="!ready()"
|
|
838
|
+
* (confirm)="onConfirm()"
|
|
839
|
+
* (dismiss)="onCancel()"
|
|
840
|
+
* >
|
|
841
|
+
* <!-- content region: lead, description, cards, callouts … -->
|
|
842
|
+
* </fk-dialog-shell>
|
|
843
|
+
* ```
|
|
844
|
+
*
|
|
845
|
+
* Escape hatches for the exceptions:
|
|
846
|
+
* - leave `confirmLabel` empty and project buttons into the `dialogFooter` slot
|
|
847
|
+
* to keep the footer chrome but use custom buttons (e.g. a form submit);
|
|
848
|
+
* - set `[customFooter]="true"` and project into the `footer` slot to replace
|
|
849
|
+
* the whole footer.
|
|
850
|
+
*/
|
|
851
|
+
class DialogShellComponent {
|
|
852
|
+
// ===== INPUTS =====
|
|
853
|
+
/** Icon glyph for the header tile. Empty renders no tile. */
|
|
854
|
+
icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
855
|
+
/** Colour accent for the header tile. */
|
|
856
|
+
tint = input('brand', ...(ngDevMode ? [{ debugName: "tint" }] : /* istanbul ignore next */ []));
|
|
857
|
+
/** Header title. */
|
|
858
|
+
title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
859
|
+
/** Optional trailing accent appended to the title in the tint colour. */
|
|
860
|
+
titleAccent = input('', ...(ngDevMode ? [{ debugName: "titleAccent" }] : /* istanbul ignore next */ []));
|
|
861
|
+
/** Optional mono subtitle under the title. */
|
|
862
|
+
subtitle = input('', ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
863
|
+
/** Optional muted footer note on the left (e.g. "Permanent"). */
|
|
864
|
+
footerNote = input('', ...(ngDevMode ? [{ debugName: "footerNote" }] : /* istanbul ignore next */ []));
|
|
865
|
+
/** Optional icon rendered before the footer note. */
|
|
866
|
+
footerNoteIcon = input('', ...(ngDevMode ? [{ debugName: "footerNoteIcon" }] : /* istanbul ignore next */ []));
|
|
867
|
+
/**
|
|
868
|
+
* Confirm-button label. When set, the shell renders the standard
|
|
869
|
+
* Cancel + confirm pair; leave empty to project custom buttons into the
|
|
870
|
+
* `dialogFooter` slot instead.
|
|
871
|
+
*/
|
|
872
|
+
confirmLabel = input('', ...(ngDevMode ? [{ debugName: "confirmLabel" }] : /* istanbul ignore next */ []));
|
|
873
|
+
/** Optional leading icon on the confirm button. */
|
|
874
|
+
confirmIcon = input('', ...(ngDevMode ? [{ debugName: "confirmIcon" }] : /* istanbul ignore next */ []));
|
|
875
|
+
/** Confirm-button colour — `primary` (default), `danger`, or `warning`. */
|
|
876
|
+
confirmVariant = input('primary', ...(ngDevMode ? [{ debugName: "confirmVariant" }] : /* istanbul ignore next */ []));
|
|
877
|
+
/** Disables the confirm button (e.g. while a type-to-confirm is unmet). */
|
|
878
|
+
confirmDisabled = input(false, ...(ngDevMode ? [{ debugName: "confirmDisabled" }] : /* istanbul ignore next */ []));
|
|
879
|
+
/** Cancel-button label. Defaults to `Cancel`. */
|
|
880
|
+
cancelLabel = input('Cancel', ...(ngDevMode ? [{ debugName: "cancelLabel" }] : /* istanbul ignore next */ []));
|
|
881
|
+
/** Accessible label for the header's close button. */
|
|
882
|
+
closeLabel = input('Close', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
|
|
883
|
+
/**
|
|
884
|
+
* Escape hatch for the rare dialog that needs a bespoke footer. When true,
|
|
885
|
+
* the default `fk-dialog-footer` (note + buttons) is suppressed and whatever
|
|
886
|
+
* is projected into the `footer` slot is rendered raw instead.
|
|
887
|
+
*/
|
|
888
|
+
customFooter = input(false, ...(ngDevMode ? [{ debugName: "customFooter" }] : /* istanbul ignore next */ []));
|
|
889
|
+
// ===== OUTPUTS =====
|
|
890
|
+
/** Fires when the header close button OR the Cancel button is clicked. */
|
|
891
|
+
dismiss = output();
|
|
892
|
+
/** Fires when the confirm button is clicked. */
|
|
893
|
+
confirm = output();
|
|
894
|
+
// ===== BASE PROPS =====
|
|
895
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
896
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
897
|
+
// ===== COMPUTED =====
|
|
898
|
+
/** `warning` renders as a retinted `danger` fk-button; others map directly. */
|
|
899
|
+
confirmButtonVariant = computed(() => {
|
|
900
|
+
const variant = this.confirmVariant();
|
|
901
|
+
return variant === 'warning' ? 'danger' : variant;
|
|
902
|
+
}, ...(ngDevMode ? [{ debugName: "confirmButtonVariant" }] : /* istanbul ignore next */ []));
|
|
903
|
+
classes = computed(() => ['fk-dialog-shell', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
904
|
+
get hostClass() {
|
|
905
|
+
return this.classes();
|
|
906
|
+
}
|
|
907
|
+
get hostId() {
|
|
908
|
+
return this.id();
|
|
909
|
+
}
|
|
910
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
911
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DialogShellComponent, isStandalone: true, selector: "fk-dialog-shell", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, tint: { classPropertyName: "tint", publicName: "tint", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, titleAccent: { classPropertyName: "titleAccent", publicName: "titleAccent", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, footerNote: { classPropertyName: "footerNote", publicName: "footerNote", isSignal: true, isRequired: false, transformFunction: null }, footerNoteIcon: { classPropertyName: "footerNoteIcon", publicName: "footerNoteIcon", isSignal: true, isRequired: false, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, confirmIcon: { classPropertyName: "confirmIcon", publicName: "confirmIcon", isSignal: true, isRequired: false, transformFunction: null }, confirmVariant: { classPropertyName: "confirmVariant", publicName: "confirmVariant", isSignal: true, isRequired: false, transformFunction: null }, confirmDisabled: { classPropertyName: "confirmDisabled", publicName: "confirmDisabled", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, customFooter: { classPropertyName: "customFooter", publicName: "customFooter", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss", confirm: "confirm" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, ngImport: i0, template: "<fk-dialog-header\n class=\"fk-dialog-shell__header\"\n [icon]=\"icon()\"\n [tint]=\"tint()\"\n [title]=\"title()\"\n [titleAccent]=\"titleAccent()\"\n [subtitle]=\"subtitle()\"\n [closeLabel]=\"closeLabel()\"\n (dismiss)=\"dismiss.emit()\"\n/>\n\n<div class=\"fk-dialog-shell__body\">\n <ng-content />\n</div>\n\n@if (customFooter()) {\n <ng-content select=\"[footer]\" />\n} @else {\n <fk-dialog-footer [note]=\"footerNote()\" [noteIcon]=\"footerNoteIcon()\">\n @if (confirmLabel()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"dismiss.emit()\">\n {{ cancelLabel() }}\n </fk-button>\n <fk-button\n class=\"fk-dialog-shell__confirm\"\n [class.fk-dialog-shell__confirm--danger]=\"confirmVariant() === 'danger'\"\n [class.fk-dialog-shell__confirm--warning]=\"\n confirmVariant() === 'warning'\n \"\n [variant]=\"confirmButtonVariant()\"\n type=\"button\"\n [disabled]=\"confirmDisabled()\"\n (click)=\"confirm.emit()\"\n >\n @if (confirmIcon()) {\n <fk-icon fkButtonIconStart [name]=\"confirmIcon()\" size=\"sm\" />\n }\n {{ confirmLabel() }}\n </fk-button>\n } @else {\n <ng-content select=\"[dialogFooter]\" />\n }\n </fk-dialog-footer>\n}\n", styles: [":host{display:flex;flex-direction:column}.fk-dialog-shell__body{margin-top:var(--fk-dialog-shell-body-margin-top, var(--fk-rhythm-4, 1rem))}.fk-dialog-shell__confirm--warning{--fk-button-danger-bg: var( --fk-dialog-confirm-warning-bg, var(--fk-color-warning, #d97706) );--fk-button-danger-bg-hover: var( --fk-dialog-confirm-warning-bg-hover, var(--fk-color-warning-strong, #92400e) );--fk-button-danger-bg-active: var( --fk-dialog-confirm-warning-bg-active, var(--fk-color-warning-strong, #92400e) );--fk-button-danger-color: var( --fk-dialog-confirm-warning-color, var(--fk-color-surface, #ffffff) );--fk-button-danger-border-color: transparent}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "fk-button", inputs: ["variant", "size", "loading", "disabled", "type", "fullWidth", "id", "className", "ariaLabel", "ariaDescribedBy"] }, { kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }, { kind: "component", type: DialogFooterComponent, selector: "fk-dialog-footer", inputs: ["note", "noteIcon", "className", "id", "ariaLabel"] }, { kind: "component", type: DialogHeaderComponent, selector: "fk-dialog-header", inputs: ["icon", "tint", "title", "titleAccent", "subtitle", "closeLabel", "className", "id", "ariaLabel"], outputs: ["dismiss"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
912
|
+
}
|
|
913
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DialogShellComponent, decorators: [{
|
|
914
|
+
type: Component,
|
|
915
|
+
args: [{ selector: 'fk-dialog-shell', standalone: true, imports: [
|
|
916
|
+
ButtonComponent,
|
|
917
|
+
IconComponent,
|
|
918
|
+
DialogFooterComponent,
|
|
919
|
+
DialogHeaderComponent,
|
|
920
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fk-dialog-header\n class=\"fk-dialog-shell__header\"\n [icon]=\"icon()\"\n [tint]=\"tint()\"\n [title]=\"title()\"\n [titleAccent]=\"titleAccent()\"\n [subtitle]=\"subtitle()\"\n [closeLabel]=\"closeLabel()\"\n (dismiss)=\"dismiss.emit()\"\n/>\n\n<div class=\"fk-dialog-shell__body\">\n <ng-content />\n</div>\n\n@if (customFooter()) {\n <ng-content select=\"[footer]\" />\n} @else {\n <fk-dialog-footer [note]=\"footerNote()\" [noteIcon]=\"footerNoteIcon()\">\n @if (confirmLabel()) {\n <fk-button variant=\"outline\" type=\"button\" (click)=\"dismiss.emit()\">\n {{ cancelLabel() }}\n </fk-button>\n <fk-button\n class=\"fk-dialog-shell__confirm\"\n [class.fk-dialog-shell__confirm--danger]=\"confirmVariant() === 'danger'\"\n [class.fk-dialog-shell__confirm--warning]=\"\n confirmVariant() === 'warning'\n \"\n [variant]=\"confirmButtonVariant()\"\n type=\"button\"\n [disabled]=\"confirmDisabled()\"\n (click)=\"confirm.emit()\"\n >\n @if (confirmIcon()) {\n <fk-icon fkButtonIconStart [name]=\"confirmIcon()\" size=\"sm\" />\n }\n {{ confirmLabel() }}\n </fk-button>\n } @else {\n <ng-content select=\"[dialogFooter]\" />\n }\n </fk-dialog-footer>\n}\n", styles: [":host{display:flex;flex-direction:column}.fk-dialog-shell__body{margin-top:var(--fk-dialog-shell-body-margin-top, var(--fk-rhythm-4, 1rem))}.fk-dialog-shell__confirm--warning{--fk-button-danger-bg: var( --fk-dialog-confirm-warning-bg, var(--fk-color-warning, #d97706) );--fk-button-danger-bg-hover: var( --fk-dialog-confirm-warning-bg-hover, var(--fk-color-warning-strong, #92400e) );--fk-button-danger-bg-active: var( --fk-dialog-confirm-warning-bg-active, var(--fk-color-warning-strong, #92400e) );--fk-button-danger-color: var( --fk-dialog-confirm-warning-color, var(--fk-color-surface, #ffffff) );--fk-button-danger-border-color: transparent}\n"] }]
|
|
921
|
+
}], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], tint: [{ type: i0.Input, args: [{ isSignal: true, alias: "tint", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], titleAccent: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleAccent", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], footerNote: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerNote", required: false }] }], footerNoteIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerNoteIcon", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], confirmIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmIcon", required: false }] }], confirmVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmVariant", required: false }] }], confirmDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmDisabled", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], customFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "customFooter", required: false }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], confirm: [{ type: i0.Output, args: ["confirm"] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], hostClass: [{
|
|
922
|
+
type: HostBinding,
|
|
923
|
+
args: ['class']
|
|
924
|
+
}], hostId: [{
|
|
925
|
+
type: HostBinding,
|
|
926
|
+
args: ['attr.id']
|
|
927
|
+
}] } });
|
|
928
|
+
|
|
929
|
+
const VIEWPORT_MARGIN = 24;
|
|
930
|
+
/**
|
|
931
|
+
* A floating, draggable onboarding-progress widget. It presents a checklist of
|
|
932
|
+
* steps with a progress bar, collapses to a compact pill with a progress ring,
|
|
933
|
+
* and can be dragged anywhere on screen (CDK drag). It renders over the app in
|
|
934
|
+
* a fixed layer — drop it once near the app root.
|
|
935
|
+
*
|
|
936
|
+
* **Controlled model, no domain data.** The `steps` model is a required,
|
|
937
|
+
* reactive input: the widget is a pure view of it and emits events when the
|
|
938
|
+
* user acts (`stepAction`, `stepMarkDone`, `substepToggle`, `goLive`). The
|
|
939
|
+
* consumer owns the step state — so if it is keyed by, say, the active account
|
|
940
|
+
* or environment, swapping the input re-renders the widget instantly. The
|
|
941
|
+
* widget never persists step state.
|
|
942
|
+
*
|
|
943
|
+
* **Presentation state is internal + persisted.** Collapsed and drag position
|
|
944
|
+
* live in the widget and persist to `localStorage` under `storageKey` — these
|
|
945
|
+
* are UI preferences, not part of the model.
|
|
946
|
+
*
|
|
947
|
+
* **Appearance-agnostic.** All colour flows through `--fk-*` tokens with neutral
|
|
948
|
+
* (near-black / grey) raw fallbacks, so with no theme it renders monochrome;
|
|
949
|
+
* point the tokens at a brand scale to colour it.
|
|
950
|
+
*
|
|
951
|
+
* ```html
|
|
952
|
+
* <fk-onboarding-guide
|
|
953
|
+
* [steps]="steps()"
|
|
954
|
+
* title="Setup guide"
|
|
955
|
+
* (stepAction)="onStart($event)"
|
|
956
|
+
* (substepToggle)="onToggle($event)"
|
|
957
|
+
* (goLive)="onGoLive()"
|
|
958
|
+
* (dismiss)="onDismiss()"
|
|
959
|
+
* />
|
|
960
|
+
* ```
|
|
961
|
+
*/
|
|
962
|
+
class OnboardingGuideComponent {
|
|
963
|
+
host = inject(ElementRef);
|
|
964
|
+
drag = inject(CdkDrag);
|
|
965
|
+
destroyRef = inject(DestroyRef);
|
|
966
|
+
// ===== INPUTS =====
|
|
967
|
+
/** The step model. Controlled by the consumer; the widget is a view of it. */
|
|
968
|
+
steps = input.required(...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
|
|
969
|
+
/** Widget title. */
|
|
970
|
+
title = input('Setup guide', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
971
|
+
/** `localStorage` key for the collapsed + drag-position preferences. */
|
|
972
|
+
storageKey = input('fk-onboarding-guide', ...(ngDevMode ? [{ debugName: "storageKey" }] : /* istanbul ignore next */ []));
|
|
973
|
+
/** Initial collapsed state, used only when nothing is persisted yet. */
|
|
974
|
+
startCollapsed = input(false, ...(ngDevMode ? [{ debugName: "startCollapsed" }] : /* istanbul ignore next */ []));
|
|
975
|
+
// --- Text (English defaults; pass translated strings as needed) ---
|
|
976
|
+
/** Default CTA label when a step doesn't supply its own `actionLabel`. */
|
|
977
|
+
startLabel = input('Start', ...(ngDevMode ? [{ debugName: "startLabel" }] : /* istanbul ignore next */ []));
|
|
978
|
+
/** Secondary "mark this step done" action label. */
|
|
979
|
+
markDoneLabel = input('Mark done', ...(ngDevMode ? [{ debugName: "markDoneLabel" }] : /* istanbul ignore next */ []));
|
|
980
|
+
/** Footer / final go-live action label. */
|
|
981
|
+
goLiveLabel = input('Go live', ...(ngDevMode ? [{ debugName: "goLiveLabel" }] : /* istanbul ignore next */ []));
|
|
982
|
+
/** Footer noun for the "{done} of {total} {stepsWord} {doneWord}" summary. */
|
|
983
|
+
stepsWord = input('steps', ...(ngDevMode ? [{ debugName: "stepsWord" }] : /* istanbul ignore next */ []));
|
|
984
|
+
/** Footer verb for the summary. */
|
|
985
|
+
doneWord = input('done', ...(ngDevMode ? [{ debugName: "doneWord" }] : /* istanbul ignore next */ []));
|
|
986
|
+
/** Collapsed-pill suffix, e.g. "3 steps left". */
|
|
987
|
+
stepsLeftWord = input('left', ...(ngDevMode ? [{ debugName: "stepsLeftWord" }] : /* istanbul ignore next */ []));
|
|
988
|
+
/** Collapsed-pill copy shown when everything is complete. */
|
|
989
|
+
allDoneLabel = input('All set', ...(ngDevMode ? [{ debugName: "allDoneLabel" }] : /* istanbul ignore next */ []));
|
|
990
|
+
/** ARIA labels for the icon-only header controls. */
|
|
991
|
+
renameLabel = input('Rename', ...(ngDevMode ? [{ debugName: "renameLabel" }] : /* istanbul ignore next */ []));
|
|
992
|
+
collapseLabel = input('Collapse', ...(ngDevMode ? [{ debugName: "collapseLabel" }] : /* istanbul ignore next */ []));
|
|
993
|
+
dismissLabel = input('Dismiss', ...(ngDevMode ? [{ debugName: "dismissLabel" }] : /* istanbul ignore next */ []));
|
|
994
|
+
expandLabel = input('Expand', ...(ngDevMode ? [{ debugName: "expandLabel" }] : /* istanbul ignore next */ []));
|
|
995
|
+
// --- Icon glyph names (resolved by the app's icon registry) ---
|
|
996
|
+
checkIcon = input('check', ...(ngDevMode ? [{ debugName: "checkIcon" }] : /* istanbul ignore next */ []));
|
|
997
|
+
caretIcon = input('chevron-down', ...(ngDevMode ? [{ debugName: "caretIcon" }] : /* istanbul ignore next */ []));
|
|
998
|
+
editIcon = input('pen', ...(ngDevMode ? [{ debugName: "editIcon" }] : /* istanbul ignore next */ []));
|
|
999
|
+
collapseIcon = input('collapse', ...(ngDevMode ? [{ debugName: "collapseIcon" }] : /* istanbul ignore next */ []));
|
|
1000
|
+
dismissIcon = input('close', ...(ngDevMode ? [{ debugName: "dismissIcon" }] : /* istanbul ignore next */ []));
|
|
1001
|
+
expandIcon = input('expand', ...(ngDevMode ? [{ debugName: "expandIcon" }] : /* istanbul ignore next */ []));
|
|
1002
|
+
actionIcon = input('arrow-right', ...(ngDevMode ? [{ debugName: "actionIcon" }] : /* istanbul ignore next */ []));
|
|
1003
|
+
// ===== OUTPUTS =====
|
|
1004
|
+
/** A step's primary CTA was clicked. Emits the step id. */
|
|
1005
|
+
stepAction = output();
|
|
1006
|
+
/** A step's "mark done" was clicked. Emits the step id. */
|
|
1007
|
+
stepMarkDone = output();
|
|
1008
|
+
/** A substep checkbox was toggled. */
|
|
1009
|
+
substepToggle = output();
|
|
1010
|
+
/** The final go-live action was invoked (footer or the go-live step CTA). */
|
|
1011
|
+
goLive = output();
|
|
1012
|
+
/** The rename (edit) affordance was clicked; the consumer owns renaming. */
|
|
1013
|
+
renameClick = output();
|
|
1014
|
+
/** The dismiss control was clicked; the consumer decides whether to hide it. */
|
|
1015
|
+
dismiss = output();
|
|
1016
|
+
/** Fires when the collapsed state changes. */
|
|
1017
|
+
collapsedChange = output();
|
|
1018
|
+
// ===== BASE PROPS =====
|
|
1019
|
+
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
1020
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
1021
|
+
// ===== PRESENTATION STATE =====
|
|
1022
|
+
collapsed = signal(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
1023
|
+
dragPosition = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "dragPosition" }] : /* istanbul ignore next */ []));
|
|
1024
|
+
/** True immediately after a real drag, so the drag-handle's click (e.g. the
|
|
1025
|
+
* pill's expand) is suppressed rather than firing at the drop point. */
|
|
1026
|
+
justDragged = false;
|
|
1027
|
+
/**
|
|
1028
|
+
* The expanded step. A `linkedSignal` over `steps` so that whenever the model
|
|
1029
|
+
* changes (e.g. the consumer swaps in another account/environment's steps) it
|
|
1030
|
+
* resets to the first incomplete step; user toggles override until the next
|
|
1031
|
+
* model change.
|
|
1032
|
+
*/
|
|
1033
|
+
openStepId = linkedSignal({ ...(ngDevMode ? { debugName: "openStepId" } : /* istanbul ignore next */ {}), source: this.steps,
|
|
1034
|
+
computation: (steps) => steps.find((s) => !this.isComplete(s))?.id ?? null });
|
|
1035
|
+
// ===== DERIVED =====
|
|
1036
|
+
total = computed(() => this.steps().length, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
|
|
1037
|
+
doneCount = computed(() => this.steps().filter((s) => this.isComplete(s)).length, ...(ngDevMode ? [{ debugName: "doneCount" }] : /* istanbul ignore next */ []));
|
|
1038
|
+
percent = computed(() => {
|
|
1039
|
+
const total = this.total();
|
|
1040
|
+
return total === 0 ? 0 : Math.round((this.doneCount() / total) * 100);
|
|
1041
|
+
}, ...(ngDevMode ? [{ debugName: "percent" }] : /* istanbul ignore next */ []));
|
|
1042
|
+
stepsLeft = computed(() => this.total() - this.doneCount(), ...(ngDevMode ? [{ debugName: "stepsLeft" }] : /* istanbul ignore next */ []));
|
|
1043
|
+
complete = computed(() => this.total() > 0 && this.stepsLeft() === 0, ...(ngDevMode ? [{ debugName: "complete" }] : /* istanbul ignore next */ []));
|
|
1044
|
+
/** Circumference for the pill's progress ring (r = 16). */
|
|
1045
|
+
ringCircumference = 2 * Math.PI * 16;
|
|
1046
|
+
ringOffset = computed(() => {
|
|
1047
|
+
const total = this.total();
|
|
1048
|
+
const fraction = total === 0 ? 0 : this.doneCount() / total;
|
|
1049
|
+
return this.ringCircumference * (1 - fraction);
|
|
1050
|
+
}, ...(ngDevMode ? [{ debugName: "ringOffset" }] : /* istanbul ignore next */ []));
|
|
1051
|
+
classes = computed(() => [
|
|
1052
|
+
'fk-onboarding-guide',
|
|
1053
|
+
this.collapsed() ? 'fk-onboarding-guide--collapsed' : '',
|
|
1054
|
+
this.complete() ? 'fk-onboarding-guide--complete' : '',
|
|
1055
|
+
this.className(),
|
|
1056
|
+
]
|
|
1057
|
+
.filter(Boolean)
|
|
1058
|
+
.join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
1059
|
+
get hostClass() {
|
|
1060
|
+
return this.classes();
|
|
1061
|
+
}
|
|
1062
|
+
get hostId() {
|
|
1063
|
+
return this.id();
|
|
1064
|
+
}
|
|
1065
|
+
constructor() {
|
|
1066
|
+
this.drag.ended
|
|
1067
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
1068
|
+
.subscribe((event) => this.onDragEnded(event));
|
|
1069
|
+
afterNextRender(() => this.hydrate());
|
|
1070
|
+
}
|
|
1071
|
+
// ===== COMPLETION LOGIC =====
|
|
1072
|
+
isComplete(step) {
|
|
1073
|
+
if (step.substeps && step.substeps.length > 0) {
|
|
1074
|
+
return step.substeps.every((sub) => sub.done);
|
|
1075
|
+
}
|
|
1076
|
+
return step.done;
|
|
1077
|
+
}
|
|
1078
|
+
isActive(step) {
|
|
1079
|
+
return !this.isComplete(step) && this.openStepId() === step.id;
|
|
1080
|
+
}
|
|
1081
|
+
actionLabelFor(step) {
|
|
1082
|
+
return step.actionLabel || this.startLabel();
|
|
1083
|
+
}
|
|
1084
|
+
// ===== INTERACTIONS =====
|
|
1085
|
+
toggleStep(stepId) {
|
|
1086
|
+
this.openStepId.update((cur) => (cur === stepId ? null : stepId));
|
|
1087
|
+
}
|
|
1088
|
+
onAction(step) {
|
|
1089
|
+
this.stepAction.emit(step.id);
|
|
1090
|
+
}
|
|
1091
|
+
onMarkDone(step) {
|
|
1092
|
+
this.stepMarkDone.emit(step.id);
|
|
1093
|
+
}
|
|
1094
|
+
onSubstep(step, substep) {
|
|
1095
|
+
this.substepToggle.emit({ stepId: step.id, substepId: substep.id });
|
|
1096
|
+
}
|
|
1097
|
+
onCollapse() {
|
|
1098
|
+
this.setCollapsed(true);
|
|
1099
|
+
}
|
|
1100
|
+
onExpand() {
|
|
1101
|
+
if (this.justDragged) {
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
this.setCollapsed(false);
|
|
1105
|
+
}
|
|
1106
|
+
onRename() {
|
|
1107
|
+
this.renameClick.emit();
|
|
1108
|
+
}
|
|
1109
|
+
onDismiss() {
|
|
1110
|
+
this.dismiss.emit();
|
|
1111
|
+
}
|
|
1112
|
+
onGoLive() {
|
|
1113
|
+
this.goLive.emit();
|
|
1114
|
+
}
|
|
1115
|
+
setCollapsed(value) {
|
|
1116
|
+
this.collapsed.set(value);
|
|
1117
|
+
this.collapsedChange.emit(value);
|
|
1118
|
+
this.persist();
|
|
1119
|
+
}
|
|
1120
|
+
// ===== DRAG =====
|
|
1121
|
+
onDragEnded(event) {
|
|
1122
|
+
const clamped = this.clamp(this.drag.getFreeDragPosition());
|
|
1123
|
+
this.dragPosition.set(clamped);
|
|
1124
|
+
this.drag.setFreeDragPosition(clamped);
|
|
1125
|
+
this.persist();
|
|
1126
|
+
const moved = Math.abs(event.distance.x) > 3 || Math.abs(event.distance.y) > 3;
|
|
1127
|
+
if (moved) {
|
|
1128
|
+
this.justDragged = true;
|
|
1129
|
+
setTimeout(() => (this.justDragged = false));
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
clamp(point) {
|
|
1133
|
+
if (typeof window === 'undefined') {
|
|
1134
|
+
return point;
|
|
1135
|
+
}
|
|
1136
|
+
const rect = this.host.nativeElement.getBoundingClientRect();
|
|
1137
|
+
const maxX = window.innerWidth - rect.width - VIEWPORT_MARGIN;
|
|
1138
|
+
const maxY = window.innerHeight - rect.height - VIEWPORT_MARGIN;
|
|
1139
|
+
return {
|
|
1140
|
+
x: Math.max(VIEWPORT_MARGIN, Math.min(point.x, maxX)),
|
|
1141
|
+
y: Math.max(VIEWPORT_MARGIN, Math.min(point.y, maxY)),
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
// ===== PERSISTENCE =====
|
|
1145
|
+
hydrate() {
|
|
1146
|
+
const saved = this.read();
|
|
1147
|
+
this.collapsed.set(saved.collapsed ?? this.startCollapsed());
|
|
1148
|
+
const pos = saved.pos ? this.clamp(saved.pos) : this.defaultBottomRight();
|
|
1149
|
+
this.dragPosition.set(pos);
|
|
1150
|
+
this.drag.setFreeDragPosition(pos);
|
|
1151
|
+
}
|
|
1152
|
+
defaultBottomRight() {
|
|
1153
|
+
if (typeof window === 'undefined') {
|
|
1154
|
+
return { x: 0, y: 0 };
|
|
1155
|
+
}
|
|
1156
|
+
const rect = this.host.nativeElement.getBoundingClientRect();
|
|
1157
|
+
return {
|
|
1158
|
+
x: Math.max(VIEWPORT_MARGIN, window.innerWidth - rect.width - VIEWPORT_MARGIN),
|
|
1159
|
+
y: Math.max(VIEWPORT_MARGIN, window.innerHeight - rect.height - VIEWPORT_MARGIN),
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
read() {
|
|
1163
|
+
const store = this.storage();
|
|
1164
|
+
if (!store) {
|
|
1165
|
+
return {};
|
|
1166
|
+
}
|
|
1167
|
+
try {
|
|
1168
|
+
return JSON.parse(store.getItem(this.storageKey()) || '{}');
|
|
1169
|
+
}
|
|
1170
|
+
catch {
|
|
1171
|
+
return {};
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
persist() {
|
|
1175
|
+
const store = this.storage();
|
|
1176
|
+
if (!store) {
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
const state = {
|
|
1180
|
+
collapsed: this.collapsed(),
|
|
1181
|
+
pos: this.dragPosition(),
|
|
1182
|
+
};
|
|
1183
|
+
try {
|
|
1184
|
+
store.setItem(this.storageKey(), JSON.stringify(state));
|
|
1185
|
+
}
|
|
1186
|
+
catch {
|
|
1187
|
+
// Storage unavailable (private mode / quota) — preferences just don't stick.
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
storage() {
|
|
1191
|
+
try {
|
|
1192
|
+
return typeof window === 'undefined' ? null : window.localStorage;
|
|
1193
|
+
}
|
|
1194
|
+
catch {
|
|
1195
|
+
return null;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OnboardingGuideComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1199
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OnboardingGuideComponent, isStandalone: true, selector: "fk-onboarding-guide", inputs: { steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, storageKey: { classPropertyName: "storageKey", publicName: "storageKey", isSignal: true, isRequired: false, transformFunction: null }, startCollapsed: { classPropertyName: "startCollapsed", publicName: "startCollapsed", isSignal: true, isRequired: false, transformFunction: null }, startLabel: { classPropertyName: "startLabel", publicName: "startLabel", isSignal: true, isRequired: false, transformFunction: null }, markDoneLabel: { classPropertyName: "markDoneLabel", publicName: "markDoneLabel", isSignal: true, isRequired: false, transformFunction: null }, goLiveLabel: { classPropertyName: "goLiveLabel", publicName: "goLiveLabel", isSignal: true, isRequired: false, transformFunction: null }, stepsWord: { classPropertyName: "stepsWord", publicName: "stepsWord", isSignal: true, isRequired: false, transformFunction: null }, doneWord: { classPropertyName: "doneWord", publicName: "doneWord", isSignal: true, isRequired: false, transformFunction: null }, stepsLeftWord: { classPropertyName: "stepsLeftWord", publicName: "stepsLeftWord", isSignal: true, isRequired: false, transformFunction: null }, allDoneLabel: { classPropertyName: "allDoneLabel", publicName: "allDoneLabel", isSignal: true, isRequired: false, transformFunction: null }, renameLabel: { classPropertyName: "renameLabel", publicName: "renameLabel", isSignal: true, isRequired: false, transformFunction: null }, collapseLabel: { classPropertyName: "collapseLabel", publicName: "collapseLabel", isSignal: true, isRequired: false, transformFunction: null }, dismissLabel: { classPropertyName: "dismissLabel", publicName: "dismissLabel", isSignal: true, isRequired: false, transformFunction: null }, expandLabel: { classPropertyName: "expandLabel", publicName: "expandLabel", isSignal: true, isRequired: false, transformFunction: null }, checkIcon: { classPropertyName: "checkIcon", publicName: "checkIcon", isSignal: true, isRequired: false, transformFunction: null }, caretIcon: { classPropertyName: "caretIcon", publicName: "caretIcon", isSignal: true, isRequired: false, transformFunction: null }, editIcon: { classPropertyName: "editIcon", publicName: "editIcon", isSignal: true, isRequired: false, transformFunction: null }, collapseIcon: { classPropertyName: "collapseIcon", publicName: "collapseIcon", isSignal: true, isRequired: false, transformFunction: null }, dismissIcon: { classPropertyName: "dismissIcon", publicName: "dismissIcon", isSignal: true, isRequired: false, transformFunction: null }, expandIcon: { classPropertyName: "expandIcon", publicName: "expandIcon", isSignal: true, isRequired: false, transformFunction: null }, actionIcon: { classPropertyName: "actionIcon", publicName: "actionIcon", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { stepAction: "stepAction", stepMarkDone: "stepMarkDone", substepToggle: "substepToggle", goLive: "goLive", renameClick: "renameClick", dismiss: "dismiss", collapsedChange: "collapsedChange" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, hostDirectives: [{ directive: i1.CdkDrag }], ngImport: i0, template: "<!-- ===================== EXPANDED CARD ===================== -->\n<div class=\"fk-onboarding-guide__card\">\n <div class=\"fk-onboarding-guide__head\">\n <div class=\"fk-onboarding-guide__head-top\">\n <span class=\"fk-onboarding-guide__grip\" cdkDragHandle aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"9\" cy=\"6\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"15\" cy=\"6\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"9\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"15\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"9\" cy=\"18\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"15\" cy=\"18\" r=\"1.5\" fill=\"currentColor\" />\n </svg>\n </span>\n\n <span class=\"fk-onboarding-guide__title\" cdkDragHandle>{{\n title()\n }}</span>\n\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__icon-btn fk-onboarding-guide__edit\"\n [attr.aria-label]=\"renameLabel()\"\n (click)=\"onRename()\"\n >\n <fk-icon [name]=\"editIcon()\" size=\"sm\" />\n </button>\n\n <div class=\"fk-onboarding-guide__head-actions\">\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__icon-btn\"\n [attr.aria-label]=\"collapseLabel()\"\n (click)=\"onCollapse()\"\n >\n <fk-icon [name]=\"collapseIcon()\" size=\"sm\" />\n </button>\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__icon-btn\"\n [attr.aria-label]=\"dismissLabel()\"\n (click)=\"onDismiss()\"\n >\n <fk-icon [name]=\"dismissIcon()\" size=\"sm\" />\n </button>\n </div>\n </div>\n\n <div class=\"fk-onboarding-guide__progress\">\n <div\n class=\"fk-onboarding-guide__track\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"percent()\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <div\n class=\"fk-onboarding-guide__fill\"\n [style.width.%]=\"percent()\"\n ></div>\n </div>\n <span class=\"fk-onboarding-guide__pct\">{{ percent() }}%</span>\n </div>\n </div>\n\n <div class=\"fk-onboarding-guide__steps\" role=\"list\">\n @for (step of steps(); track step.id) {\n @let done = isComplete(step);\n @let open = openStepId() === step.id;\n @let active = isActive(step);\n\n <div\n class=\"fk-onboarding-guide__step\"\n role=\"listitem\"\n [class.fk-onboarding-guide__step--done]=\"done\"\n [class.fk-onboarding-guide__step--open]=\"open\"\n >\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__step-row\"\n [attr.aria-expanded]=\"open\"\n (click)=\"toggleStep(step.id)\"\n >\n <span\n class=\"fk-onboarding-guide__check\"\n [class.fk-onboarding-guide__check--done]=\"done\"\n [class.fk-onboarding-guide__check--active]=\"active\"\n [class.fk-onboarding-guide__check--todo]=\"!done && !active\"\n aria-hidden=\"true\"\n >\n @if (done) {\n <fk-icon [name]=\"checkIcon()\" size=\"xs\" />\n } @else if (active) {\n <i class=\"fk-onboarding-guide__dot\"></i>\n }\n </span>\n\n <span class=\"fk-onboarding-guide__step-label\">{{ step.label }}</span>\n\n <span class=\"fk-onboarding-guide__caret\" aria-hidden=\"true\">\n <fk-icon [name]=\"caretIcon()\" size=\"sm\" />\n </span>\n </button>\n\n @if (open) {\n <div class=\"fk-onboarding-guide__step-body\">\n @if (step.substeps && step.substeps.length > 0) {\n @for (sub of step.substeps; track sub.id) {\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__sub\"\n [class.fk-onboarding-guide__sub--done]=\"sub.done\"\n (click)=\"onSubstep(step, sub)\"\n >\n <span\n class=\"fk-onboarding-guide__sub-check\"\n aria-hidden=\"true\"\n >\n @if (sub.done) {\n <fk-icon [name]=\"checkIcon()\" size=\"xs\" />\n }\n </span>\n <span class=\"fk-onboarding-guide__sub-label\">{{\n sub.label\n }}</span>\n </button>\n }\n } @else {\n @if (step.description) {\n <p class=\"fk-onboarding-guide__desc\">{{ step.description }}</p>\n }\n\n @if (!done) {\n <div class=\"fk-onboarding-guide__actions\">\n <fk-button\n variant=\"primary\"\n size=\"sm\"\n (click)=\"onAction(step)\"\n >\n {{ actionLabelFor(step) }}\n <fk-icon fkButtonIconEnd [name]=\"actionIcon()\" size=\"xs\" />\n </fk-button>\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__skip\"\n (click)=\"onMarkDone(step)\"\n >\n {{ markDoneLabel() }}\n </button>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"fk-onboarding-guide__foot\">\n <span class=\"fk-onboarding-guide__foot-text\">\n <b>{{ doneCount() }}</b> / {{ total() }} {{ stepsWord() }}\n {{ doneWord() }}\n </span>\n <button type=\"button\" class=\"fk-onboarding-guide__go\" (click)=\"onGoLive()\">\n {{ goLiveLabel() }}\n <fk-icon [name]=\"actionIcon()\" size=\"xs\" />\n </button>\n </div>\n</div>\n\n<!-- ===================== COLLAPSED PILL ===================== -->\n<button\n type=\"button\"\n class=\"fk-onboarding-guide__pill\"\n cdkDragHandle\n [attr.aria-label]=\"expandLabel()\"\n (click)=\"onExpand()\"\n>\n <span class=\"fk-onboarding-guide__ring\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 38 38\">\n <circle class=\"fk-onboarding-guide__ring-track\" cx=\"19\" cy=\"19\" r=\"16\" />\n <circle\n class=\"fk-onboarding-guide__ring-prog\"\n cx=\"19\"\n cy=\"19\"\n r=\"16\"\n [style.stroke-dasharray]=\"ringCircumference\"\n [style.stroke-dashoffset]=\"ringOffset()\"\n />\n </svg>\n <span class=\"fk-onboarding-guide__ring-num\">{{ percent() }}%</span>\n </span>\n\n <span class=\"fk-onboarding-guide__pill-meta\">\n <span class=\"fk-onboarding-guide__pill-title\">{{ title() }}</span>\n <span class=\"fk-onboarding-guide__pill-sub\">\n @if (complete()) {\n {{ allDoneLabel() }}\n } @else {\n <b>{{ stepsLeft() }}</b> {{ stepsWord() }} {{ stepsLeftWord() }}\n }\n </span>\n </span>\n\n <span class=\"fk-onboarding-guide__pill-expand\" aria-hidden=\"true\">\n <fk-icon [name]=\"expandIcon()\" size=\"sm\" />\n </span>\n</button>\n", styles: ["@keyframes fk-onboarding-guide-pop{0%{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}:host{position:fixed;top:0;left:0;z-index:var(--fk-onboarding-guide-z, 60);font-family:var(--fk-onboarding-guide-font-body, var(--fk-font-family-body, system-ui, sans-serif));color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b));touch-action:none}.fk-onboarding-guide__card,.fk-onboarding-guide__pill{--fk-button-primary-bg: var( --fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a) );--fk-button-primary-bg-hover: color-mix( in srgb, var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a)) 88%, #000 );--fk-button-primary-color: var( --fk-onboarding-guide-accent-contrast, var(--fk-color-surface, #fff) )}.fk-onboarding-guide__card{width:var(--fk-onboarding-guide-width, 360px);max-width:calc(100vw - 2 * var(--fk-rhythm-4, 1rem));background:var(--fk-onboarding-guide-surface, var(--fk-color-surface, #fff));border:1px solid var(--fk-onboarding-guide-border, var(--fk-color-border, #e2e8f0));border-radius:var(--fk-onboarding-guide-radius, var(--fk-radius-lg, 16px));box-shadow:var(--fk-onboarding-guide-shadow, 0 24px 54px -18px rgba(15, 23, 42, .28));overflow:hidden;transform-origin:bottom right;animation:fk-onboarding-guide-pop .24s cubic-bezier(.2,.9,.3,1.1)}.fk-onboarding-guide__head{padding:16px 18px 14px}.fk-onboarding-guide__head-top{display:flex;align-items:center;gap:9px}.fk-onboarding-guide__grip,.fk-onboarding-guide__title{cursor:grab}.fk-onboarding-guide__grip:active,.fk-onboarding-guide__title:active{cursor:grabbing}.fk-onboarding-guide__grip{display:inline-flex;flex:none;margin-left:-3px;color:var(--fk-onboarding-guide-text-faint, var(--fk-color-muted, #94a3b8))}.fk-onboarding-guide__grip svg{width:15px;height:15px}.fk-onboarding-guide__title{font-family:var(--fk-onboarding-guide-font-display, var(--fk-font-family-display, inherit));font-size:16px;font-weight:700;color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__head-actions{margin-left:auto;display:flex;gap:4px}.fk-onboarding-guide__icon-btn{display:grid;place-items:center;width:28px;height:28px;border:1px solid transparent;border-radius:7px;background:none;color:var(--fk-onboarding-guide-text-faint, var(--fk-color-muted, #94a3b8));cursor:pointer;transition:.14s}.fk-onboarding-guide__icon-btn:hover{background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f8fafc));border-color:var(--fk-onboarding-guide-border, var(--fk-color-border, #e2e8f0));color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__edit{width:24px;height:24px;border-radius:6px}.fk-onboarding-guide__progress{display:flex;align-items:center;gap:11px;margin-top:13px}.fk-onboarding-guide__track{flex:1;height:7px;border-radius:5px;background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f1f5f9));overflow:hidden}.fk-onboarding-guide__fill{height:100%;border-radius:5px;background:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a));transition:width .5s cubic-bezier(.4,0,.2,1)}.fk-onboarding-guide__pct{flex:none;font-family:var(--fk-onboarding-guide-font-mono, var(--fk-font-family-mono, ui-monospace, monospace));font-size:11px;font-weight:600;color:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__steps{padding:6px 8px 10px;max-height:400px;overflow-y:auto}.fk-onboarding-guide__step{border-radius:12px;overflow:hidden}.fk-onboarding-guide__step--open{background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f8fafc))}.fk-onboarding-guide__step-row{display:flex;align-items:center;gap:12px;width:100%;padding:13px 12px;border:none;border-radius:12px;background:none;text-align:left;cursor:pointer;transition:.14s}.fk-onboarding-guide__step-row:hover{background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f8fafc))}.fk-onboarding-guide__step--open .fk-onboarding-guide__step-row:hover{background:transparent}.fk-onboarding-guide__check{display:grid;place-items:center;flex:none;width:20px;height:20px;border-radius:50%;transition:.16s;color:var(--fk-onboarding-guide-accent-contrast, #fff)}.fk-onboarding-guide__check--done{background:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__check--todo{border:2px solid color-mix(in srgb,var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b)) 45%,transparent)}.fk-onboarding-guide__check--active{border:2px solid var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a));background:color-mix(in srgb,var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a)) 10%,transparent)}.fk-onboarding-guide__dot{width:7px;height:7px;border-radius:50%;background:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__step-label{flex:1;min-width:0;font-size:14.5px;font-weight:600;color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__step--done .fk-onboarding-guide__step-label{color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b));font-weight:500;text-decoration:line-through;text-decoration-color:color-mix(in srgb,var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b)) 55%,transparent)}.fk-onboarding-guide__caret{display:grid;place-items:center;flex:none;color:var(--fk-onboarding-guide-text-faint, var(--fk-color-muted, #b6bfce));transition:transform .18s}.fk-onboarding-guide__step--open .fk-onboarding-guide__caret{transform:rotate(180deg);color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__step-body{padding:0 14px 14px 44px}.fk-onboarding-guide__desc{font-size:13px;line-height:1.55;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b));margin-bottom:12px}.fk-onboarding-guide__actions{display:flex;align-items:center;gap:8px}.fk-onboarding-guide__skip{font-size:13px;font-weight:600;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b));background:none;border:none;padding:6px;cursor:pointer}.fk-onboarding-guide__skip:hover{color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__sub{display:flex;align-items:center;gap:11px;width:100%;padding:8px 0;border:none;background:none;text-align:left;cursor:pointer}.fk-onboarding-guide__sub-check{display:grid;place-items:center;flex:none;width:18px;height:18px;border-radius:50%;color:var(--fk-onboarding-guide-accent-contrast, #fff);background:color-mix(in srgb,var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b)) 22%,transparent)}.fk-onboarding-guide__sub--done .fk-onboarding-guide__sub-check{background:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__sub-label{font-size:14px;color:var(--fk-onboarding-guide-text, var(--fk-color-text, #36405a))}.fk-onboarding-guide__sub--done .fk-onboarding-guide__sub-label{color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b))}.fk-onboarding-guide__foot{display:flex;align-items:center;gap:10px;padding:12px 18px 16px;border-top:1px solid var(--fk-onboarding-guide-divider, var(--fk-color-surface-dim, #f1f5f9))}.fk-onboarding-guide__foot-text{font-size:12.5px;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b))}.fk-onboarding-guide__foot-text b{color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b));font-weight:600}.fk-onboarding-guide__go{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;background:none;border:none;cursor:pointer;color:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__pill{display:none;align-items:center;gap:12px;padding:9px 15px 9px 10px;border:1px solid var(--fk-onboarding-guide-border, var(--fk-color-border, #e2e8f0));border-radius:999px;background:var(--fk-onboarding-guide-surface, var(--fk-color-surface, #fff));box-shadow:var(--fk-onboarding-guide-shadow, 0 24px 54px -18px rgba(15, 23, 42, .28));cursor:grab;animation:fk-onboarding-guide-pop .22s cubic-bezier(.2,.9,.3,1.1)}.fk-onboarding-guide__pill:active{cursor:grabbing}.fk-onboarding-guide__ring{position:relative;width:38px;height:38px;flex:none}.fk-onboarding-guide__ring svg{width:38px;height:38px;transform:rotate(-90deg)}.fk-onboarding-guide__ring-track{fill:none;stroke:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f1f5f9));stroke-width:4}.fk-onboarding-guide__ring-prog{fill:none;stroke:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a));stroke-width:4;stroke-linecap:round;transition:stroke-dashoffset .5s cubic-bezier(.4,0,.2,1)}.fk-onboarding-guide__ring-num{position:absolute;inset:0;display:grid;place-items:center;font-family:var(--fk-onboarding-guide-font-mono, var(--fk-font-family-mono, ui-monospace, monospace));font-size:10.5px;font-weight:600;color:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__pill-meta{display:flex;flex-direction:column;line-height:1.2;text-align:left}.fk-onboarding-guide__pill-title{font-family:var(--fk-onboarding-guide-font-display, var(--fk-font-family-display, inherit));font-size:13.5px;font-weight:700;white-space:nowrap;color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__pill-sub{margin-top:2px;font-size:11.5px;white-space:nowrap;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b))}.fk-onboarding-guide__pill-sub b{color:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a));font-weight:600}.fk-onboarding-guide__pill-expand{display:grid;place-items:center;width:28px;height:28px;flex:none;margin-left:2px;border-radius:50%;background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f8fafc));color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b))}.fk-onboarding-guide--complete .fk-onboarding-guide__ring-prog{stroke:var(--fk-onboarding-guide-complete, var(--fk-color-success, #16a34a))}.fk-onboarding-guide--complete .fk-onboarding-guide__ring-num{color:var(--fk-onboarding-guide-complete, var(--fk-color-success, #16a34a))}:host(.fk-onboarding-guide--collapsed) .fk-onboarding-guide__card{display:none}:host(.fk-onboarding-guide--collapsed) .fk-onboarding-guide__pill{display:flex}\n"], dependencies: [{ kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: ButtonComponent, selector: "fk-button", inputs: ["variant", "size", "loading", "disabled", "type", "fullWidth", "id", "className", "ariaLabel", "ariaDescribedBy"] }, { kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1200
|
+
}
|
|
1201
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OnboardingGuideComponent, decorators: [{
|
|
1202
|
+
type: Component,
|
|
1203
|
+
args: [{ selector: 'fk-onboarding-guide', standalone: true, imports: [CdkDragHandle, ButtonComponent, IconComponent], hostDirectives: [CdkDrag], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- ===================== EXPANDED CARD ===================== -->\n<div class=\"fk-onboarding-guide__card\">\n <div class=\"fk-onboarding-guide__head\">\n <div class=\"fk-onboarding-guide__head-top\">\n <span class=\"fk-onboarding-guide__grip\" cdkDragHandle aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"9\" cy=\"6\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"15\" cy=\"6\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"9\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"15\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"9\" cy=\"18\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"15\" cy=\"18\" r=\"1.5\" fill=\"currentColor\" />\n </svg>\n </span>\n\n <span class=\"fk-onboarding-guide__title\" cdkDragHandle>{{\n title()\n }}</span>\n\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__icon-btn fk-onboarding-guide__edit\"\n [attr.aria-label]=\"renameLabel()\"\n (click)=\"onRename()\"\n >\n <fk-icon [name]=\"editIcon()\" size=\"sm\" />\n </button>\n\n <div class=\"fk-onboarding-guide__head-actions\">\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__icon-btn\"\n [attr.aria-label]=\"collapseLabel()\"\n (click)=\"onCollapse()\"\n >\n <fk-icon [name]=\"collapseIcon()\" size=\"sm\" />\n </button>\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__icon-btn\"\n [attr.aria-label]=\"dismissLabel()\"\n (click)=\"onDismiss()\"\n >\n <fk-icon [name]=\"dismissIcon()\" size=\"sm\" />\n </button>\n </div>\n </div>\n\n <div class=\"fk-onboarding-guide__progress\">\n <div\n class=\"fk-onboarding-guide__track\"\n role=\"progressbar\"\n [attr.aria-valuenow]=\"percent()\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n >\n <div\n class=\"fk-onboarding-guide__fill\"\n [style.width.%]=\"percent()\"\n ></div>\n </div>\n <span class=\"fk-onboarding-guide__pct\">{{ percent() }}%</span>\n </div>\n </div>\n\n <div class=\"fk-onboarding-guide__steps\" role=\"list\">\n @for (step of steps(); track step.id) {\n @let done = isComplete(step);\n @let open = openStepId() === step.id;\n @let active = isActive(step);\n\n <div\n class=\"fk-onboarding-guide__step\"\n role=\"listitem\"\n [class.fk-onboarding-guide__step--done]=\"done\"\n [class.fk-onboarding-guide__step--open]=\"open\"\n >\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__step-row\"\n [attr.aria-expanded]=\"open\"\n (click)=\"toggleStep(step.id)\"\n >\n <span\n class=\"fk-onboarding-guide__check\"\n [class.fk-onboarding-guide__check--done]=\"done\"\n [class.fk-onboarding-guide__check--active]=\"active\"\n [class.fk-onboarding-guide__check--todo]=\"!done && !active\"\n aria-hidden=\"true\"\n >\n @if (done) {\n <fk-icon [name]=\"checkIcon()\" size=\"xs\" />\n } @else if (active) {\n <i class=\"fk-onboarding-guide__dot\"></i>\n }\n </span>\n\n <span class=\"fk-onboarding-guide__step-label\">{{ step.label }}</span>\n\n <span class=\"fk-onboarding-guide__caret\" aria-hidden=\"true\">\n <fk-icon [name]=\"caretIcon()\" size=\"sm\" />\n </span>\n </button>\n\n @if (open) {\n <div class=\"fk-onboarding-guide__step-body\">\n @if (step.substeps && step.substeps.length > 0) {\n @for (sub of step.substeps; track sub.id) {\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__sub\"\n [class.fk-onboarding-guide__sub--done]=\"sub.done\"\n (click)=\"onSubstep(step, sub)\"\n >\n <span\n class=\"fk-onboarding-guide__sub-check\"\n aria-hidden=\"true\"\n >\n @if (sub.done) {\n <fk-icon [name]=\"checkIcon()\" size=\"xs\" />\n }\n </span>\n <span class=\"fk-onboarding-guide__sub-label\">{{\n sub.label\n }}</span>\n </button>\n }\n } @else {\n @if (step.description) {\n <p class=\"fk-onboarding-guide__desc\">{{ step.description }}</p>\n }\n\n @if (!done) {\n <div class=\"fk-onboarding-guide__actions\">\n <fk-button\n variant=\"primary\"\n size=\"sm\"\n (click)=\"onAction(step)\"\n >\n {{ actionLabelFor(step) }}\n <fk-icon fkButtonIconEnd [name]=\"actionIcon()\" size=\"xs\" />\n </fk-button>\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__skip\"\n (click)=\"onMarkDone(step)\"\n >\n {{ markDoneLabel() }}\n </button>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"fk-onboarding-guide__foot\">\n <span class=\"fk-onboarding-guide__foot-text\">\n <b>{{ doneCount() }}</b> / {{ total() }} {{ stepsWord() }}\n {{ doneWord() }}\n </span>\n <button type=\"button\" class=\"fk-onboarding-guide__go\" (click)=\"onGoLive()\">\n {{ goLiveLabel() }}\n <fk-icon [name]=\"actionIcon()\" size=\"xs\" />\n </button>\n </div>\n</div>\n\n<!-- ===================== COLLAPSED PILL ===================== -->\n<button\n type=\"button\"\n class=\"fk-onboarding-guide__pill\"\n cdkDragHandle\n [attr.aria-label]=\"expandLabel()\"\n (click)=\"onExpand()\"\n>\n <span class=\"fk-onboarding-guide__ring\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 38 38\">\n <circle class=\"fk-onboarding-guide__ring-track\" cx=\"19\" cy=\"19\" r=\"16\" />\n <circle\n class=\"fk-onboarding-guide__ring-prog\"\n cx=\"19\"\n cy=\"19\"\n r=\"16\"\n [style.stroke-dasharray]=\"ringCircumference\"\n [style.stroke-dashoffset]=\"ringOffset()\"\n />\n </svg>\n <span class=\"fk-onboarding-guide__ring-num\">{{ percent() }}%</span>\n </span>\n\n <span class=\"fk-onboarding-guide__pill-meta\">\n <span class=\"fk-onboarding-guide__pill-title\">{{ title() }}</span>\n <span class=\"fk-onboarding-guide__pill-sub\">\n @if (complete()) {\n {{ allDoneLabel() }}\n } @else {\n <b>{{ stepsLeft() }}</b> {{ stepsWord() }} {{ stepsLeftWord() }}\n }\n </span>\n </span>\n\n <span class=\"fk-onboarding-guide__pill-expand\" aria-hidden=\"true\">\n <fk-icon [name]=\"expandIcon()\" size=\"sm\" />\n </span>\n</button>\n", styles: ["@keyframes fk-onboarding-guide-pop{0%{opacity:0;transform:translateY(8px) scale(.97)}to{opacity:1;transform:none}}:host{position:fixed;top:0;left:0;z-index:var(--fk-onboarding-guide-z, 60);font-family:var(--fk-onboarding-guide-font-body, var(--fk-font-family-body, system-ui, sans-serif));color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b));touch-action:none}.fk-onboarding-guide__card,.fk-onboarding-guide__pill{--fk-button-primary-bg: var( --fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a) );--fk-button-primary-bg-hover: color-mix( in srgb, var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a)) 88%, #000 );--fk-button-primary-color: var( --fk-onboarding-guide-accent-contrast, var(--fk-color-surface, #fff) )}.fk-onboarding-guide__card{width:var(--fk-onboarding-guide-width, 360px);max-width:calc(100vw - 2 * var(--fk-rhythm-4, 1rem));background:var(--fk-onboarding-guide-surface, var(--fk-color-surface, #fff));border:1px solid var(--fk-onboarding-guide-border, var(--fk-color-border, #e2e8f0));border-radius:var(--fk-onboarding-guide-radius, var(--fk-radius-lg, 16px));box-shadow:var(--fk-onboarding-guide-shadow, 0 24px 54px -18px rgba(15, 23, 42, .28));overflow:hidden;transform-origin:bottom right;animation:fk-onboarding-guide-pop .24s cubic-bezier(.2,.9,.3,1.1)}.fk-onboarding-guide__head{padding:16px 18px 14px}.fk-onboarding-guide__head-top{display:flex;align-items:center;gap:9px}.fk-onboarding-guide__grip,.fk-onboarding-guide__title{cursor:grab}.fk-onboarding-guide__grip:active,.fk-onboarding-guide__title:active{cursor:grabbing}.fk-onboarding-guide__grip{display:inline-flex;flex:none;margin-left:-3px;color:var(--fk-onboarding-guide-text-faint, var(--fk-color-muted, #94a3b8))}.fk-onboarding-guide__grip svg{width:15px;height:15px}.fk-onboarding-guide__title{font-family:var(--fk-onboarding-guide-font-display, var(--fk-font-family-display, inherit));font-size:16px;font-weight:700;color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__head-actions{margin-left:auto;display:flex;gap:4px}.fk-onboarding-guide__icon-btn{display:grid;place-items:center;width:28px;height:28px;border:1px solid transparent;border-radius:7px;background:none;color:var(--fk-onboarding-guide-text-faint, var(--fk-color-muted, #94a3b8));cursor:pointer;transition:.14s}.fk-onboarding-guide__icon-btn:hover{background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f8fafc));border-color:var(--fk-onboarding-guide-border, var(--fk-color-border, #e2e8f0));color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__edit{width:24px;height:24px;border-radius:6px}.fk-onboarding-guide__progress{display:flex;align-items:center;gap:11px;margin-top:13px}.fk-onboarding-guide__track{flex:1;height:7px;border-radius:5px;background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f1f5f9));overflow:hidden}.fk-onboarding-guide__fill{height:100%;border-radius:5px;background:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a));transition:width .5s cubic-bezier(.4,0,.2,1)}.fk-onboarding-guide__pct{flex:none;font-family:var(--fk-onboarding-guide-font-mono, var(--fk-font-family-mono, ui-monospace, monospace));font-size:11px;font-weight:600;color:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__steps{padding:6px 8px 10px;max-height:400px;overflow-y:auto}.fk-onboarding-guide__step{border-radius:12px;overflow:hidden}.fk-onboarding-guide__step--open{background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f8fafc))}.fk-onboarding-guide__step-row{display:flex;align-items:center;gap:12px;width:100%;padding:13px 12px;border:none;border-radius:12px;background:none;text-align:left;cursor:pointer;transition:.14s}.fk-onboarding-guide__step-row:hover{background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f8fafc))}.fk-onboarding-guide__step--open .fk-onboarding-guide__step-row:hover{background:transparent}.fk-onboarding-guide__check{display:grid;place-items:center;flex:none;width:20px;height:20px;border-radius:50%;transition:.16s;color:var(--fk-onboarding-guide-accent-contrast, #fff)}.fk-onboarding-guide__check--done{background:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__check--todo{border:2px solid color-mix(in srgb,var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b)) 45%,transparent)}.fk-onboarding-guide__check--active{border:2px solid var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a));background:color-mix(in srgb,var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a)) 10%,transparent)}.fk-onboarding-guide__dot{width:7px;height:7px;border-radius:50%;background:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__step-label{flex:1;min-width:0;font-size:14.5px;font-weight:600;color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__step--done .fk-onboarding-guide__step-label{color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b));font-weight:500;text-decoration:line-through;text-decoration-color:color-mix(in srgb,var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b)) 55%,transparent)}.fk-onboarding-guide__caret{display:grid;place-items:center;flex:none;color:var(--fk-onboarding-guide-text-faint, var(--fk-color-muted, #b6bfce));transition:transform .18s}.fk-onboarding-guide__step--open .fk-onboarding-guide__caret{transform:rotate(180deg);color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__step-body{padding:0 14px 14px 44px}.fk-onboarding-guide__desc{font-size:13px;line-height:1.55;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b));margin-bottom:12px}.fk-onboarding-guide__actions{display:flex;align-items:center;gap:8px}.fk-onboarding-guide__skip{font-size:13px;font-weight:600;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b));background:none;border:none;padding:6px;cursor:pointer}.fk-onboarding-guide__skip:hover{color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__sub{display:flex;align-items:center;gap:11px;width:100%;padding:8px 0;border:none;background:none;text-align:left;cursor:pointer}.fk-onboarding-guide__sub-check{display:grid;place-items:center;flex:none;width:18px;height:18px;border-radius:50%;color:var(--fk-onboarding-guide-accent-contrast, #fff);background:color-mix(in srgb,var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b)) 22%,transparent)}.fk-onboarding-guide__sub--done .fk-onboarding-guide__sub-check{background:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__sub-label{font-size:14px;color:var(--fk-onboarding-guide-text, var(--fk-color-text, #36405a))}.fk-onboarding-guide__sub--done .fk-onboarding-guide__sub-label{color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b))}.fk-onboarding-guide__foot{display:flex;align-items:center;gap:10px;padding:12px 18px 16px;border-top:1px solid var(--fk-onboarding-guide-divider, var(--fk-color-surface-dim, #f1f5f9))}.fk-onboarding-guide__foot-text{font-size:12.5px;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b))}.fk-onboarding-guide__foot-text b{color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b));font-weight:600}.fk-onboarding-guide__go{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;background:none;border:none;cursor:pointer;color:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__pill{display:none;align-items:center;gap:12px;padding:9px 15px 9px 10px;border:1px solid var(--fk-onboarding-guide-border, var(--fk-color-border, #e2e8f0));border-radius:999px;background:var(--fk-onboarding-guide-surface, var(--fk-color-surface, #fff));box-shadow:var(--fk-onboarding-guide-shadow, 0 24px 54px -18px rgba(15, 23, 42, .28));cursor:grab;animation:fk-onboarding-guide-pop .22s cubic-bezier(.2,.9,.3,1.1)}.fk-onboarding-guide__pill:active{cursor:grabbing}.fk-onboarding-guide__ring{position:relative;width:38px;height:38px;flex:none}.fk-onboarding-guide__ring svg{width:38px;height:38px;transform:rotate(-90deg)}.fk-onboarding-guide__ring-track{fill:none;stroke:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f1f5f9));stroke-width:4}.fk-onboarding-guide__ring-prog{fill:none;stroke:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a));stroke-width:4;stroke-linecap:round;transition:stroke-dashoffset .5s cubic-bezier(.4,0,.2,1)}.fk-onboarding-guide__ring-num{position:absolute;inset:0;display:grid;place-items:center;font-family:var(--fk-onboarding-guide-font-mono, var(--fk-font-family-mono, ui-monospace, monospace));font-size:10.5px;font-weight:600;color:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a))}.fk-onboarding-guide__pill-meta{display:flex;flex-direction:column;line-height:1.2;text-align:left}.fk-onboarding-guide__pill-title{font-family:var(--fk-onboarding-guide-font-display, var(--fk-font-family-display, inherit));font-size:13.5px;font-weight:700;white-space:nowrap;color:var(--fk-onboarding-guide-text, var(--fk-color-text, #1e293b))}.fk-onboarding-guide__pill-sub{margin-top:2px;font-size:11.5px;white-space:nowrap;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b))}.fk-onboarding-guide__pill-sub b{color:var(--fk-onboarding-guide-accent, var(--fk-color-text, #1a1a1a));font-weight:600}.fk-onboarding-guide__pill-expand{display:grid;place-items:center;width:28px;height:28px;flex:none;margin-left:2px;border-radius:50%;background:var(--fk-onboarding-guide-surface-muted, var(--fk-color-surface-muted, #f8fafc));color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b))}.fk-onboarding-guide--complete .fk-onboarding-guide__ring-prog{stroke:var(--fk-onboarding-guide-complete, var(--fk-color-success, #16a34a))}.fk-onboarding-guide--complete .fk-onboarding-guide__ring-num{color:var(--fk-onboarding-guide-complete, var(--fk-color-success, #16a34a))}:host(.fk-onboarding-guide--collapsed) .fk-onboarding-guide__card{display:none}:host(.fk-onboarding-guide--collapsed) .fk-onboarding-guide__pill{display:flex}\n"] }]
|
|
1204
|
+
}], ctorParameters: () => [], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], storageKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "storageKey", required: false }] }], startCollapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "startCollapsed", required: false }] }], startLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "startLabel", required: false }] }], markDoneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "markDoneLabel", required: false }] }], goLiveLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "goLiveLabel", required: false }] }], stepsWord: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepsWord", required: false }] }], doneWord: [{ type: i0.Input, args: [{ isSignal: true, alias: "doneWord", required: false }] }], stepsLeftWord: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepsLeftWord", required: false }] }], allDoneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "allDoneLabel", required: false }] }], renameLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "renameLabel", required: false }] }], collapseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseLabel", required: false }] }], dismissLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissLabel", required: false }] }], expandLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandLabel", required: false }] }], checkIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkIcon", required: false }] }], caretIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "caretIcon", required: false }] }], editIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "editIcon", required: false }] }], collapseIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseIcon", required: false }] }], dismissIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissIcon", required: false }] }], expandIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandIcon", required: false }] }], actionIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionIcon", required: false }] }], stepAction: [{ type: i0.Output, args: ["stepAction"] }], stepMarkDone: [{ type: i0.Output, args: ["stepMarkDone"] }], substepToggle: [{ type: i0.Output, args: ["substepToggle"] }], goLive: [{ type: i0.Output, args: ["goLive"] }], renameClick: [{ type: i0.Output, args: ["renameClick"] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], collapsedChange: [{ type: i0.Output, args: ["collapsedChange"] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], hostClass: [{
|
|
1205
|
+
type: HostBinding,
|
|
1206
|
+
args: ['class']
|
|
1207
|
+
}], hostId: [{
|
|
1208
|
+
type: HostBinding,
|
|
1209
|
+
args: ['attr.id']
|
|
1210
|
+
}] } });
|
|
1211
|
+
|
|
648
1212
|
/**
|
|
649
1213
|
* Generated bundle index. Do not edit.
|
|
650
1214
|
*/
|
|
651
1215
|
|
|
652
|
-
export { ColumnsComponent, ContentSplitLayoutComponent, DetailPanelComponent, PageHeaderComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
|
|
1216
|
+
export { ColumnsComponent, ContentSplitLayoutComponent, DetailPanelComponent, DialogFooterComponent, DialogHeaderComponent, DialogShellComponent, OnboardingGuideComponent, PageHeaderComponent, SidebarNavComponent, SidebarNavItemDirective, TableEmptyStateComponent };
|
|
653
1217
|
//# sourceMappingURL=frame-kit-ui-ng-patterns-dashboard.mjs.map
|