@frame-kit/ui-ng-patterns 0.1.18 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frame-kit/ui-ng-patterns",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "Opinionated, dashboard-ready Angular component patterns composed from @frame-kit/ui-ng.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@angular/forms": "^21.2.0",
|
|
18
18
|
"@angular/platform-browser": "^21.2.0",
|
|
19
19
|
"@angular/router": "^21.2.0",
|
|
20
|
-
"@frame-kit/ui-ng": "0.1.
|
|
20
|
+
"@frame-kit/ui-ng": "0.1.20",
|
|
21
21
|
"rxjs": "^7.0.0"
|
|
22
22
|
},
|
|
23
23
|
"sideEffects": false,
|
|
@@ -618,8 +618,8 @@ interface OnboardingSubstep {
|
|
|
618
618
|
* - a **group** step — carries `substeps`, and is considered complete once every
|
|
619
619
|
* substep is `done` (the top-level `done` flag is ignored for group steps).
|
|
620
620
|
*
|
|
621
|
-
* All state (`done`) is owned by the consumer; the widget is a
|
|
622
|
-
* of the `steps` model and emits events when the user acts.
|
|
621
|
+
* All state (`done`, `pending`) is owned by the consumer; the widget is a
|
|
622
|
+
* controlled view of the `steps` model and emits events when the user acts.
|
|
623
623
|
*/
|
|
624
624
|
interface OnboardingStep {
|
|
625
625
|
/** Stable identifier, unique within the guide. */
|
|
@@ -628,10 +628,23 @@ interface OnboardingStep {
|
|
|
628
628
|
label: string;
|
|
629
629
|
/** Whether a leaf step is complete. Ignored when `substeps` is set. */
|
|
630
630
|
done: boolean;
|
|
631
|
+
/**
|
|
632
|
+
* The step is in flight — not complete, but expected to complete without
|
|
633
|
+
* further user action (e.g. an invite that auto-completes when accepted).
|
|
634
|
+
* Purely visual: a pending step still counts as incomplete for progress.
|
|
635
|
+
*/
|
|
636
|
+
pending?: boolean;
|
|
637
|
+
/** Optional supporting copy shown while the step is `pending`. */
|
|
638
|
+
pendingNote?: string;
|
|
631
639
|
/** Optional supporting copy shown when the step is expanded. */
|
|
632
640
|
description?: string;
|
|
633
641
|
/** Optional call-to-action label (defaults to a generic "Start"). */
|
|
634
642
|
actionLabel?: string;
|
|
643
|
+
/**
|
|
644
|
+
* Optional secondary text-action label (e.g. "Show help"). When set, the
|
|
645
|
+
* expanded body renders it and clicks emit `stepSecondaryAction`.
|
|
646
|
+
*/
|
|
647
|
+
secondaryActionLabel?: string;
|
|
635
648
|
/** When set, the step renders a sub-checklist and auto-completes when all are done. */
|
|
636
649
|
substeps?: OnboardingSubstep[];
|
|
637
650
|
}
|
|
@@ -648,19 +661,33 @@ interface Point {
|
|
|
648
661
|
/**
|
|
649
662
|
* A floating, draggable onboarding-progress widget. It presents a checklist of
|
|
650
663
|
* steps with a progress bar, collapses to a compact pill with a progress ring,
|
|
651
|
-
* and can be dragged
|
|
652
|
-
*
|
|
664
|
+
* and can be dragged around (CDK drag). It renders over the app in a fixed
|
|
665
|
+
* layer — drop it once near the app root.
|
|
653
666
|
*
|
|
654
667
|
* **Controlled model, no domain data.** The `steps` model is a required,
|
|
655
668
|
* reactive input: the widget is a pure view of it and emits events when the
|
|
656
|
-
* user acts (`stepAction`, `stepMarkDone`, `
|
|
657
|
-
* consumer owns the step state — so if
|
|
658
|
-
*
|
|
659
|
-
* widget never persists step state.
|
|
669
|
+
* user acts (`stepAction`, `stepMarkDone`, `stepSecondaryAction`,
|
|
670
|
+
* `substepToggle`, `footerAction`). The consumer owns the step state — so if
|
|
671
|
+
* it is keyed by, say, the active account or environment, swapping the input
|
|
672
|
+
* re-renders the widget instantly. The widget never persists step state.
|
|
673
|
+
*
|
|
674
|
+
* **Collapsed is a two-way model.** Bind `[(collapsed)]` (or listen to
|
|
675
|
+
* `collapsedChange`) to drive the card ↔ pill swap from outside — e.g. an
|
|
676
|
+
* auto-open policy. Left unbound, the widget manages it internally.
|
|
677
|
+
*
|
|
678
|
+
* **Container-aware.** `boundary` (a selector or element) confines both
|
|
679
|
+
* dragging and the widget's resting position to a region of the app — e.g. a
|
|
680
|
+
* dashboard's content area, so the widget can never cover navigation or a
|
|
681
|
+
* footer. The default resting spot is the boundary's bottom-right corner, and
|
|
682
|
+
* the widget re-clamps itself whenever its own footprint changes (pill ↔ card
|
|
683
|
+
* swap, a step expanding) or the viewport resizes — a corner-parked pill that
|
|
684
|
+
* expands into a large card pulls itself fully back into view. Until the user
|
|
685
|
+
* actually drags it, it stays anchored to the bottom-right corner.
|
|
660
686
|
*
|
|
661
|
-
* **Presentation state is internal + persisted.** Collapsed and
|
|
662
|
-
*
|
|
663
|
-
*
|
|
687
|
+
* **Presentation state is internal + optionally persisted.** Collapsed and
|
|
688
|
+
* drag position persist to `localStorage` under `storageKey`; pass
|
|
689
|
+
* `storageKey = null` for session-only behavior (e.g. when a consumer-side
|
|
690
|
+
* store owns presentation policy).
|
|
664
691
|
*
|
|
665
692
|
* **Opinionated but themeable.** It ships a house look out of the box; all
|
|
666
693
|
* colour flows through `--fk-onboarding-guide-*` tokens, so overriding those
|
|
@@ -670,9 +697,12 @@ interface Point {
|
|
|
670
697
|
* <fk-onboarding-guide
|
|
671
698
|
* [steps]="steps()"
|
|
672
699
|
* title="Setup guide"
|
|
700
|
+
* boundary=".app-content"
|
|
701
|
+
* [storageKey]="null"
|
|
702
|
+
* [(collapsed)]="collapsed"
|
|
673
703
|
* (stepAction)="onStart($event)"
|
|
674
704
|
* (substepToggle)="onToggle($event)"
|
|
675
|
-
* (
|
|
705
|
+
* (footerAction)="onGoLive()"
|
|
676
706
|
* (dismiss)="onDismiss()"
|
|
677
707
|
* />
|
|
678
708
|
* ```
|
|
@@ -685,16 +715,30 @@ declare class OnboardingGuideComponent {
|
|
|
685
715
|
readonly steps: _angular_core.InputSignal<OnboardingStep[]>;
|
|
686
716
|
/** Widget title. */
|
|
687
717
|
readonly title: _angular_core.InputSignal<string>;
|
|
688
|
-
/**
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
718
|
+
/**
|
|
719
|
+
* `localStorage` key for the collapsed + drag-position preferences.
|
|
720
|
+
* `null` disables persistence entirely — presentation state then lives and
|
|
721
|
+
* dies with the component instance (or with whatever the consumer binds).
|
|
722
|
+
*/
|
|
723
|
+
readonly storageKey: _angular_core.InputSignal<string | null>;
|
|
724
|
+
/**
|
|
725
|
+
* Region the widget is confined to: a CSS selector or an element. Both
|
|
726
|
+
* dragging and the widget's resting position are clamped inside it. `null`
|
|
727
|
+
* (the default) confines to the viewport.
|
|
728
|
+
*/
|
|
729
|
+
readonly boundary: _angular_core.InputSignal<string | HTMLElement | null>;
|
|
730
|
+
/** Breathing room kept between the widget and the boundary's edges. */
|
|
731
|
+
readonly boundaryMargin: _angular_core.InputSignal<number>;
|
|
732
|
+
/** Shows the rename (edit) affordance next to the title. */
|
|
733
|
+
readonly renamable: _angular_core.InputSignal<boolean>;
|
|
734
|
+
/** Shows the secondary "mark this step done" action on leaf steps. */
|
|
735
|
+
readonly showMarkDone: _angular_core.InputSignal<boolean>;
|
|
692
736
|
/** Default CTA label when a step doesn't supply its own `actionLabel`. */
|
|
693
737
|
readonly startLabel: _angular_core.InputSignal<string>;
|
|
694
738
|
/** Secondary "mark this step done" action label. */
|
|
695
739
|
readonly markDoneLabel: _angular_core.InputSignal<string>;
|
|
696
|
-
/** Footer
|
|
697
|
-
readonly
|
|
740
|
+
/** Footer action label (e.g. "Go live"). `null` hides the footer action. */
|
|
741
|
+
readonly footerActionLabel: _angular_core.InputSignal<string | null>;
|
|
698
742
|
/** Footer noun for the "{done} of {total} {stepsWord} {doneWord}" summary. */
|
|
699
743
|
readonly stepsWord: _angular_core.InputSignal<string>;
|
|
700
744
|
/** Footer verb for the summary. */
|
|
@@ -715,34 +759,61 @@ declare class OnboardingGuideComponent {
|
|
|
715
759
|
readonly dismissIcon: _angular_core.InputSignal<string>;
|
|
716
760
|
readonly expandIcon: _angular_core.InputSignal<string>;
|
|
717
761
|
readonly actionIcon: _angular_core.InputSignal<string>;
|
|
762
|
+
/**
|
|
763
|
+
* Whether the widget is collapsed to the pill. Two-way bindable so a
|
|
764
|
+
* consumer-side policy (auto-open on context switches, banner-triggered
|
|
765
|
+
* opens) can drive the card ↔ pill swap; unbound, the widget manages it.
|
|
766
|
+
*/
|
|
767
|
+
readonly collapsed: _angular_core.ModelSignal<boolean>;
|
|
718
768
|
/** A step's primary CTA was clicked. Emits the step id. */
|
|
719
769
|
readonly stepAction: _angular_core.OutputEmitterRef<string>;
|
|
720
770
|
/** A step's "mark done" was clicked. Emits the step id. */
|
|
721
771
|
readonly stepMarkDone: _angular_core.OutputEmitterRef<string>;
|
|
772
|
+
/** A step's secondary text-action was clicked. Emits the step id. */
|
|
773
|
+
readonly stepSecondaryAction: _angular_core.OutputEmitterRef<string>;
|
|
722
774
|
/** A substep checkbox was toggled. */
|
|
723
775
|
readonly substepToggle: _angular_core.OutputEmitterRef<OnboardingSubstepToggle>;
|
|
724
|
-
/** The
|
|
725
|
-
readonly
|
|
776
|
+
/** The footer action was invoked. */
|
|
777
|
+
readonly footerAction: _angular_core.OutputEmitterRef<void>;
|
|
726
778
|
/** The rename (edit) affordance was clicked; the consumer owns renaming. */
|
|
727
779
|
readonly renameClick: _angular_core.OutputEmitterRef<void>;
|
|
728
780
|
/** The dismiss control was clicked; the consumer decides whether to hide it. */
|
|
729
781
|
readonly dismiss: _angular_core.OutputEmitterRef<void>;
|
|
730
|
-
/** Fires when the collapsed state changes. */
|
|
731
|
-
readonly collapsedChange: _angular_core.OutputEmitterRef<boolean>;
|
|
732
782
|
readonly className: _angular_core.InputSignal<string>;
|
|
733
783
|
readonly id: _angular_core.InputSignal<string | null>;
|
|
734
|
-
protected readonly collapsed: _angular_core.WritableSignal<boolean>;
|
|
735
784
|
protected readonly dragPosition: _angular_core.WritableSignal<Point>;
|
|
736
785
|
/** True immediately after a real drag, so the drag-handle's click (e.g. the
|
|
737
786
|
* pill's expand) is suppressed rather than firing at the drop point. */
|
|
738
787
|
private justDragged;
|
|
739
788
|
/**
|
|
740
|
-
*
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
*
|
|
789
|
+
* True once the user has deliberately placed the widget (a real drag now,
|
|
790
|
+
* or a persisted position from a previous session). Until then the widget
|
|
791
|
+
* re-anchors to the boundary's bottom-right corner on every viewport or
|
|
792
|
+
* footprint change instead of merely clamping its current spot.
|
|
744
793
|
*/
|
|
745
|
-
|
|
794
|
+
private userMoved;
|
|
795
|
+
private footprintObserver;
|
|
796
|
+
/** Guards the one-time preference restore in `hydrateOnce`. */
|
|
797
|
+
private hydrated;
|
|
798
|
+
/** True between CDK's drag start and drop — `reposition` stands down. */
|
|
799
|
+
private dragging;
|
|
800
|
+
/** The boundary element currently under observation (if any). */
|
|
801
|
+
private observedBoundary;
|
|
802
|
+
/**
|
|
803
|
+
* Last seen completion signature (step ids + done states) — the accordion
|
|
804
|
+
* reset skips the first run and cosmetic-only model updates.
|
|
805
|
+
*/
|
|
806
|
+
private prevCompletionSignature;
|
|
807
|
+
/**
|
|
808
|
+
* The expanded step. Two-way bindable so a consumer can spotlight a
|
|
809
|
+
* specific step (e.g. a "complete this section" banner). `undefined` means
|
|
810
|
+
* "follow the default" — the first incomplete step. User toggles (and
|
|
811
|
+
* consumer writes) override the default until the `steps` model changes,
|
|
812
|
+
* which resets the choice so the guide re-focuses the next piece of work.
|
|
813
|
+
*/
|
|
814
|
+
readonly openStepId: _angular_core.ModelSignal<string | null | undefined>;
|
|
815
|
+
/** The step actually expanded: the explicit choice, or the default. */
|
|
816
|
+
protected readonly resolvedOpenStepId: _angular_core.Signal<string | null>;
|
|
746
817
|
protected readonly total: _angular_core.Signal<number>;
|
|
747
818
|
protected readonly doneCount: _angular_core.Signal<number>;
|
|
748
819
|
protected readonly percent: _angular_core.Signal<number>;
|
|
@@ -756,27 +827,67 @@ declare class OnboardingGuideComponent {
|
|
|
756
827
|
get hostId(): string | null;
|
|
757
828
|
constructor();
|
|
758
829
|
isComplete(step: OnboardingStep): boolean;
|
|
830
|
+
isPending(step: OnboardingStep): boolean;
|
|
759
831
|
protected isActive(step: OnboardingStep): boolean;
|
|
760
832
|
protected actionLabelFor(step: OnboardingStep): string;
|
|
761
833
|
protected toggleStep(stepId: string): void;
|
|
762
834
|
protected onAction(step: OnboardingStep): void;
|
|
763
835
|
protected onMarkDone(step: OnboardingStep): void;
|
|
836
|
+
protected onSecondaryAction(step: OnboardingStep): void;
|
|
764
837
|
protected onSubstep(step: OnboardingStep, substep: OnboardingSubstep): void;
|
|
765
838
|
protected onCollapse(): void;
|
|
766
839
|
protected onExpand(): void;
|
|
767
840
|
protected onRename(): void;
|
|
768
841
|
protected onDismiss(): void;
|
|
769
|
-
protected
|
|
842
|
+
protected onFooterAction(): void;
|
|
770
843
|
private setCollapsed;
|
|
771
844
|
private onDragEnded;
|
|
845
|
+
/**
|
|
846
|
+
* Keep the resting position valid as the world changes around it. Two
|
|
847
|
+
* signals feed the same repositioning: the viewport resizing, and the
|
|
848
|
+
* widget's own footprint changing (pill ↔ card swap, a step expanding) —
|
|
849
|
+
* the latter is what stops a corner-parked pill from reopening as a card
|
|
850
|
+
* that's mostly off-screen.
|
|
851
|
+
*/
|
|
852
|
+
private watchGeometry;
|
|
853
|
+
/**
|
|
854
|
+
* The boundary region can resize without a viewport resize — a side
|
|
855
|
+
* panel opening, chrome banners toggling — so the confining element is
|
|
856
|
+
* observed alongside the widget's own footprint. Re-synced whenever the
|
|
857
|
+
* `boundary` input changes.
|
|
858
|
+
*/
|
|
859
|
+
private syncBoundaryObservation;
|
|
860
|
+
private resolveBoundaryElement;
|
|
861
|
+
private readonly onViewportResize;
|
|
862
|
+
private reposition;
|
|
863
|
+
/** The confining region: the `boundary` element's rect, or the viewport. */
|
|
864
|
+
private boundaryRect;
|
|
865
|
+
/**
|
|
866
|
+
* Constrain one axis to `[start + margin, end - size - margin]`.
|
|
867
|
+
*
|
|
868
|
+
* When the region can't fit the widget plus both margins that range is
|
|
869
|
+
* inverted, so the margin is dropped and the widget is fitted to the
|
|
870
|
+
* region's own edges instead — keeping it inside the boundary rather
|
|
871
|
+
* than letting the inset push it out. A widget genuinely larger than
|
|
872
|
+
* its region still overflows (the card has a fixed width and something
|
|
873
|
+
* has to give); it pins to the start edge.
|
|
874
|
+
*/
|
|
875
|
+
private clampAxis;
|
|
772
876
|
private clamp;
|
|
773
|
-
private hydrate;
|
|
774
877
|
private defaultBottomRight;
|
|
878
|
+
/**
|
|
879
|
+
* One-time restore of the persisted preferences, run from the first
|
|
880
|
+
* execution of the boundary effect — inputs are set by then, storage
|
|
881
|
+
* reads are synchronous, and it lands during initial change detection
|
|
882
|
+
* so the restored state is what the first paint shows. The caller's
|
|
883
|
+
* `reposition()` clamps the raw restored position right afterwards.
|
|
884
|
+
*/
|
|
885
|
+
private hydrateOnce;
|
|
775
886
|
private read;
|
|
776
887
|
private persist;
|
|
777
888
|
private storage;
|
|
778
889
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<OnboardingGuideComponent, never>;
|
|
779
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<OnboardingGuideComponent, "fk-onboarding-guide", never, { "steps": { "alias": "steps"; "required": true; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "storageKey": { "alias": "storageKey"; "required": false; "isSignal": true; }; "
|
|
890
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<OnboardingGuideComponent, "fk-onboarding-guide", never, { "steps": { "alias": "steps"; "required": true; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "storageKey": { "alias": "storageKey"; "required": false; "isSignal": true; }; "boundary": { "alias": "boundary"; "required": false; "isSignal": true; }; "boundaryMargin": { "alias": "boundaryMargin"; "required": false; "isSignal": true; }; "renamable": { "alias": "renamable"; "required": false; "isSignal": true; }; "showMarkDone": { "alias": "showMarkDone"; "required": false; "isSignal": true; }; "startLabel": { "alias": "startLabel"; "required": false; "isSignal": true; }; "markDoneLabel": { "alias": "markDoneLabel"; "required": false; "isSignal": true; }; "footerActionLabel": { "alias": "footerActionLabel"; "required": false; "isSignal": true; }; "stepsWord": { "alias": "stepsWord"; "required": false; "isSignal": true; }; "doneWord": { "alias": "doneWord"; "required": false; "isSignal": true; }; "stepsLeftWord": { "alias": "stepsLeftWord"; "required": false; "isSignal": true; }; "allDoneLabel": { "alias": "allDoneLabel"; "required": false; "isSignal": true; }; "renameLabel": { "alias": "renameLabel"; "required": false; "isSignal": true; }; "collapseLabel": { "alias": "collapseLabel"; "required": false; "isSignal": true; }; "dismissLabel": { "alias": "dismissLabel"; "required": false; "isSignal": true; }; "expandLabel": { "alias": "expandLabel"; "required": false; "isSignal": true; }; "checkIcon": { "alias": "checkIcon"; "required": false; "isSignal": true; }; "caretIcon": { "alias": "caretIcon"; "required": false; "isSignal": true; }; "editIcon": { "alias": "editIcon"; "required": false; "isSignal": true; }; "collapseIcon": { "alias": "collapseIcon"; "required": false; "isSignal": true; }; "dismissIcon": { "alias": "dismissIcon"; "required": false; "isSignal": true; }; "expandIcon": { "alias": "expandIcon"; "required": false; "isSignal": true; }; "actionIcon": { "alias": "actionIcon"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "className": { "alias": "className"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "openStepId": { "alias": "openStepId"; "required": false; "isSignal": true; }; }, { "collapsed": "collapsedChange"; "stepAction": "stepAction"; "stepMarkDone": "stepMarkDone"; "stepSecondaryAction": "stepSecondaryAction"; "substepToggle": "substepToggle"; "footerAction": "footerAction"; "renameClick": "renameClick"; "dismiss": "dismiss"; "openStepId": "openStepIdChange"; }, never, never, true, [{ directive: typeof i1.CdkDrag; inputs: {}; outputs: {}; }]>;
|
|
780
891
|
}
|
|
781
892
|
|
|
782
893
|
/**
|