@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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, computed, HostBinding, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, TemplateRef, Directive, ViewContainerRef, DestroyRef, Injector, viewChild, contentChild, signal, effect, afterNextRender, output, ElementRef,
|
|
2
|
+
import { input, computed, HostBinding, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, TemplateRef, Directive, ViewContainerRef, DestroyRef, Injector, viewChild, contentChild, signal, effect, afterNextRender, output, ElementRef, model, untracked } 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';
|
|
@@ -967,23 +967,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
967
967
|
args: ['attr.id']
|
|
968
968
|
}] } });
|
|
969
969
|
|
|
970
|
-
|
|
970
|
+
/** Pointer travel below this is a click, not a drag. */
|
|
971
|
+
const DRAG_THRESHOLD = 3;
|
|
971
972
|
/**
|
|
972
973
|
* A floating, draggable onboarding-progress widget. It presents a checklist of
|
|
973
974
|
* steps with a progress bar, collapses to a compact pill with a progress ring,
|
|
974
|
-
* and can be dragged
|
|
975
|
-
*
|
|
975
|
+
* and can be dragged around (CDK drag). It renders over the app in a fixed
|
|
976
|
+
* layer — drop it once near the app root.
|
|
976
977
|
*
|
|
977
978
|
* **Controlled model, no domain data.** The `steps` model is a required,
|
|
978
979
|
* reactive input: the widget is a pure view of it and emits events when the
|
|
979
|
-
* user acts (`stepAction`, `stepMarkDone`, `
|
|
980
|
-
* consumer owns the step state — so if
|
|
981
|
-
*
|
|
982
|
-
* widget never persists step state.
|
|
980
|
+
* user acts (`stepAction`, `stepMarkDone`, `stepSecondaryAction`,
|
|
981
|
+
* `substepToggle`, `footerAction`). The consumer owns the step state — so if
|
|
982
|
+
* it is keyed by, say, the active account or environment, swapping the input
|
|
983
|
+
* re-renders the widget instantly. The widget never persists step state.
|
|
983
984
|
*
|
|
984
|
-
* **
|
|
985
|
-
*
|
|
986
|
-
*
|
|
985
|
+
* **Collapsed is a two-way model.** Bind `[(collapsed)]` (or listen to
|
|
986
|
+
* `collapsedChange`) to drive the card ↔ pill swap from outside — e.g. an
|
|
987
|
+
* auto-open policy. Left unbound, the widget manages it internally.
|
|
988
|
+
*
|
|
989
|
+
* **Container-aware.** `boundary` (a selector or element) confines both
|
|
990
|
+
* dragging and the widget's resting position to a region of the app — e.g. a
|
|
991
|
+
* dashboard's content area, so the widget can never cover navigation or a
|
|
992
|
+
* footer. The default resting spot is the boundary's bottom-right corner, and
|
|
993
|
+
* the widget re-clamps itself whenever its own footprint changes (pill ↔ card
|
|
994
|
+
* swap, a step expanding) or the viewport resizes — a corner-parked pill that
|
|
995
|
+
* expands into a large card pulls itself fully back into view. Until the user
|
|
996
|
+
* actually drags it, it stays anchored to the bottom-right corner.
|
|
997
|
+
*
|
|
998
|
+
* **Presentation state is internal + optionally persisted.** Collapsed and
|
|
999
|
+
* drag position persist to `localStorage` under `storageKey`; pass
|
|
1000
|
+
* `storageKey = null` for session-only behavior (e.g. when a consumer-side
|
|
1001
|
+
* store owns presentation policy).
|
|
987
1002
|
*
|
|
988
1003
|
* **Opinionated but themeable.** It ships a house look out of the box; all
|
|
989
1004
|
* colour flows through `--fk-onboarding-guide-*` tokens, so overriding those
|
|
@@ -993,9 +1008,12 @@ const VIEWPORT_MARGIN = 24;
|
|
|
993
1008
|
* <fk-onboarding-guide
|
|
994
1009
|
* [steps]="steps()"
|
|
995
1010
|
* title="Setup guide"
|
|
1011
|
+
* boundary=".app-content"
|
|
1012
|
+
* [storageKey]="null"
|
|
1013
|
+
* [(collapsed)]="collapsed"
|
|
996
1014
|
* (stepAction)="onStart($event)"
|
|
997
1015
|
* (substepToggle)="onToggle($event)"
|
|
998
|
-
* (
|
|
1016
|
+
* (footerAction)="onGoLive()"
|
|
999
1017
|
* (dismiss)="onDismiss()"
|
|
1000
1018
|
* />
|
|
1001
1019
|
* ```
|
|
@@ -1009,17 +1027,31 @@ class OnboardingGuideComponent {
|
|
|
1009
1027
|
steps = input.required(...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
|
|
1010
1028
|
/** Widget title. */
|
|
1011
1029
|
title = input('Setup guide', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
1012
|
-
/**
|
|
1030
|
+
/**
|
|
1031
|
+
* `localStorage` key for the collapsed + drag-position preferences.
|
|
1032
|
+
* `null` disables persistence entirely — presentation state then lives and
|
|
1033
|
+
* dies with the component instance (or with whatever the consumer binds).
|
|
1034
|
+
*/
|
|
1013
1035
|
storageKey = input('fk-onboarding-guide', ...(ngDevMode ? [{ debugName: "storageKey" }] : /* istanbul ignore next */ []));
|
|
1014
|
-
/**
|
|
1015
|
-
|
|
1036
|
+
/**
|
|
1037
|
+
* Region the widget is confined to: a CSS selector or an element. Both
|
|
1038
|
+
* dragging and the widget's resting position are clamped inside it. `null`
|
|
1039
|
+
* (the default) confines to the viewport.
|
|
1040
|
+
*/
|
|
1041
|
+
boundary = input(null, ...(ngDevMode ? [{ debugName: "boundary" }] : /* istanbul ignore next */ []));
|
|
1042
|
+
/** Breathing room kept between the widget and the boundary's edges. */
|
|
1043
|
+
boundaryMargin = input(24, ...(ngDevMode ? [{ debugName: "boundaryMargin" }] : /* istanbul ignore next */ []));
|
|
1044
|
+
/** Shows the rename (edit) affordance next to the title. */
|
|
1045
|
+
renamable = input(false, ...(ngDevMode ? [{ debugName: "renamable" }] : /* istanbul ignore next */ []));
|
|
1046
|
+
/** Shows the secondary "mark this step done" action on leaf steps. */
|
|
1047
|
+
showMarkDone = input(true, ...(ngDevMode ? [{ debugName: "showMarkDone" }] : /* istanbul ignore next */ []));
|
|
1016
1048
|
// --- Text (English defaults; pass translated strings as needed) ---
|
|
1017
1049
|
/** Default CTA label when a step doesn't supply its own `actionLabel`. */
|
|
1018
1050
|
startLabel = input('Start', ...(ngDevMode ? [{ debugName: "startLabel" }] : /* istanbul ignore next */ []));
|
|
1019
1051
|
/** Secondary "mark this step done" action label. */
|
|
1020
1052
|
markDoneLabel = input('Mark done', ...(ngDevMode ? [{ debugName: "markDoneLabel" }] : /* istanbul ignore next */ []));
|
|
1021
|
-
/** Footer
|
|
1022
|
-
|
|
1053
|
+
/** Footer action label (e.g. "Go live"). `null` hides the footer action. */
|
|
1054
|
+
footerActionLabel = input('Go live', ...(ngDevMode ? [{ debugName: "footerActionLabel" }] : /* istanbul ignore next */ []));
|
|
1023
1055
|
/** Footer noun for the "{done} of {total} {stepsWord} {doneWord}" summary. */
|
|
1024
1056
|
stepsWord = input('steps', ...(ngDevMode ? [{ debugName: "stepsWord" }] : /* istanbul ignore next */ []));
|
|
1025
1057
|
/** Footer verb for the summary. */
|
|
@@ -1041,38 +1073,71 @@ class OnboardingGuideComponent {
|
|
|
1041
1073
|
dismissIcon = input('close', ...(ngDevMode ? [{ debugName: "dismissIcon" }] : /* istanbul ignore next */ []));
|
|
1042
1074
|
expandIcon = input('expand', ...(ngDevMode ? [{ debugName: "expandIcon" }] : /* istanbul ignore next */ []));
|
|
1043
1075
|
actionIcon = input('arrow-right', ...(ngDevMode ? [{ debugName: "actionIcon" }] : /* istanbul ignore next */ []));
|
|
1076
|
+
// ===== MODEL =====
|
|
1077
|
+
/**
|
|
1078
|
+
* Whether the widget is collapsed to the pill. Two-way bindable so a
|
|
1079
|
+
* consumer-side policy (auto-open on context switches, banner-triggered
|
|
1080
|
+
* opens) can drive the card ↔ pill swap; unbound, the widget manages it.
|
|
1081
|
+
*/
|
|
1082
|
+
collapsed = model(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
1044
1083
|
// ===== OUTPUTS =====
|
|
1045
1084
|
/** A step's primary CTA was clicked. Emits the step id. */
|
|
1046
1085
|
stepAction = output();
|
|
1047
1086
|
/** A step's "mark done" was clicked. Emits the step id. */
|
|
1048
1087
|
stepMarkDone = output();
|
|
1088
|
+
/** A step's secondary text-action was clicked. Emits the step id. */
|
|
1089
|
+
stepSecondaryAction = output();
|
|
1049
1090
|
/** A substep checkbox was toggled. */
|
|
1050
1091
|
substepToggle = output();
|
|
1051
|
-
/** The
|
|
1052
|
-
|
|
1092
|
+
/** The footer action was invoked. */
|
|
1093
|
+
footerAction = output();
|
|
1053
1094
|
/** The rename (edit) affordance was clicked; the consumer owns renaming. */
|
|
1054
1095
|
renameClick = output();
|
|
1055
1096
|
/** The dismiss control was clicked; the consumer decides whether to hide it. */
|
|
1056
1097
|
dismiss = output();
|
|
1057
|
-
/** Fires when the collapsed state changes. */
|
|
1058
|
-
collapsedChange = output();
|
|
1059
1098
|
// ===== BASE PROPS =====
|
|
1060
1099
|
className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
|
|
1061
1100
|
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
1062
1101
|
// ===== PRESENTATION STATE =====
|
|
1063
|
-
collapsed = signal(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
1064
1102
|
dragPosition = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "dragPosition" }] : /* istanbul ignore next */ []));
|
|
1065
1103
|
/** True immediately after a real drag, so the drag-handle's click (e.g. the
|
|
1066
1104
|
* pill's expand) is suppressed rather than firing at the drop point. */
|
|
1067
1105
|
justDragged = false;
|
|
1068
1106
|
/**
|
|
1069
|
-
*
|
|
1070
|
-
*
|
|
1071
|
-
*
|
|
1072
|
-
*
|
|
1107
|
+
* True once the user has deliberately placed the widget (a real drag now,
|
|
1108
|
+
* or a persisted position from a previous session). Until then the widget
|
|
1109
|
+
* re-anchors to the boundary's bottom-right corner on every viewport or
|
|
1110
|
+
* footprint change instead of merely clamping its current spot.
|
|
1073
1111
|
*/
|
|
1074
|
-
|
|
1075
|
-
|
|
1112
|
+
userMoved = false;
|
|
1113
|
+
footprintObserver = null;
|
|
1114
|
+
/** Guards the one-time preference restore in `hydrateOnce`. */
|
|
1115
|
+
hydrated = false;
|
|
1116
|
+
/** True between CDK's drag start and drop — `reposition` stands down. */
|
|
1117
|
+
dragging = false;
|
|
1118
|
+
/** The boundary element currently under observation (if any). */
|
|
1119
|
+
observedBoundary = null;
|
|
1120
|
+
/**
|
|
1121
|
+
* Last seen completion signature (step ids + done states) — the accordion
|
|
1122
|
+
* reset skips the first run and cosmetic-only model updates.
|
|
1123
|
+
*/
|
|
1124
|
+
prevCompletionSignature = null;
|
|
1125
|
+
/**
|
|
1126
|
+
* The expanded step. Two-way bindable so a consumer can spotlight a
|
|
1127
|
+
* specific step (e.g. a "complete this section" banner). `undefined` means
|
|
1128
|
+
* "follow the default" — the first incomplete step. User toggles (and
|
|
1129
|
+
* consumer writes) override the default until the `steps` model changes,
|
|
1130
|
+
* which resets the choice so the guide re-focuses the next piece of work.
|
|
1131
|
+
*/
|
|
1132
|
+
openStepId = model(undefined, ...(ngDevMode ? [{ debugName: "openStepId" }] : /* istanbul ignore next */ []));
|
|
1133
|
+
/** The step actually expanded: the explicit choice, or the default. */
|
|
1134
|
+
resolvedOpenStepId = computed(() => {
|
|
1135
|
+
const explicit = this.openStepId();
|
|
1136
|
+
if (explicit !== undefined) {
|
|
1137
|
+
return explicit;
|
|
1138
|
+
}
|
|
1139
|
+
return this.steps().find((s) => !this.isComplete(s))?.id ?? null;
|
|
1140
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedOpenStepId" }] : /* istanbul ignore next */ []));
|
|
1076
1141
|
// ===== DERIVED =====
|
|
1077
1142
|
total = computed(() => this.steps().length, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
|
|
1078
1143
|
doneCount = computed(() => this.steps().filter((s) => this.isComplete(s)).length, ...(ngDevMode ? [{ debugName: "doneCount" }] : /* istanbul ignore next */ []));
|
|
@@ -1104,10 +1169,100 @@ class OnboardingGuideComponent {
|
|
|
1104
1169
|
return this.id();
|
|
1105
1170
|
}
|
|
1106
1171
|
constructor() {
|
|
1172
|
+
this.drag.started
|
|
1173
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
1174
|
+
.subscribe(() => (this.dragging = true));
|
|
1107
1175
|
this.drag.ended
|
|
1108
1176
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
1109
1177
|
.subscribe((event) => this.onDragEnded(event));
|
|
1110
|
-
|
|
1178
|
+
// A COMPLETION change (a step finishing, or a different guide's steps —
|
|
1179
|
+
// e.g. an env switch — arriving) drops the explicit accordion choice so
|
|
1180
|
+
// the default — the first incomplete step — takes over again and the
|
|
1181
|
+
// guide auto-advances. Only the completion signature matters: cosmetic
|
|
1182
|
+
// model updates (a description or secondary label changing, say, because
|
|
1183
|
+
// navigation made a step's help available) keep the user's choice, and
|
|
1184
|
+
// the effect's initial run must not wipe an `openStepId` the consumer
|
|
1185
|
+
// passed in at mount (e.g. a banner targeting a specific step).
|
|
1186
|
+
effect(() => {
|
|
1187
|
+
const steps = this.steps();
|
|
1188
|
+
// Sorted tokens make the signature order-insensitive: reordering
|
|
1189
|
+
// steps is cosmetic and must not reset the accordion.
|
|
1190
|
+
const signature = steps
|
|
1191
|
+
.map((s) => `${s.id}:${this.isComplete(s) ? '1' : '0'}`)
|
|
1192
|
+
.sort()
|
|
1193
|
+
.join('|');
|
|
1194
|
+
untracked(() => {
|
|
1195
|
+
// An empty model is "not loaded yet", not a guide whose steps all
|
|
1196
|
+
// vanished — drop the baseline so the first non-empty arrival is
|
|
1197
|
+
// treated like the initial run and a pre-targeted `openStepId`
|
|
1198
|
+
// survives an async load.
|
|
1199
|
+
if (steps.length === 0) {
|
|
1200
|
+
this.prevCompletionSignature = null;
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
if (this.prevCompletionSignature !== null &&
|
|
1204
|
+
this.prevCompletionSignature !== signature) {
|
|
1205
|
+
this.openStepId.set(undefined);
|
|
1206
|
+
}
|
|
1207
|
+
this.prevCompletionSignature = signature;
|
|
1208
|
+
// An explicit choice is only meaningful while it references a real
|
|
1209
|
+
// step. A dangling id — e.g. a guide swap through an empty load,
|
|
1210
|
+
// where no baseline existed to trigger the reset above — falls
|
|
1211
|
+
// back to the default instead of leaving every row closed.
|
|
1212
|
+
const explicit = this.openStepId();
|
|
1213
|
+
if (typeof explicit === 'string' &&
|
|
1214
|
+
!steps.some((s) => s.id === explicit)) {
|
|
1215
|
+
this.openStepId.set(undefined);
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
});
|
|
1219
|
+
// The card ↔ pill swap changes the footprint drastically; reposition
|
|
1220
|
+
// right after the swap renders. The ResizeObserver covers the same
|
|
1221
|
+
// case, but its deliveries ride the rendering loop — which pauses in
|
|
1222
|
+
// occluded/backgrounded tabs — so the swap gets a deterministic,
|
|
1223
|
+
// task-scheduled companion (layout can be forced without frames).
|
|
1224
|
+
effect(() => {
|
|
1225
|
+
this.collapsed();
|
|
1226
|
+
untracked(() => {
|
|
1227
|
+
if (typeof window !== 'undefined') {
|
|
1228
|
+
setTimeout(() => this.reposition());
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
});
|
|
1232
|
+
// Confine live dragging to the boundary — CDK enforces it during the
|
|
1233
|
+
// drag, and `clamp` applies the same region to programmatic placement.
|
|
1234
|
+
// The assignment is unconditional so clearing the input releases CDK's
|
|
1235
|
+
// constraint too (an empty string disables it), and the position
|
|
1236
|
+
// re-clamps immediately so a runtime boundary change moves the widget
|
|
1237
|
+
// into the new region instead of waiting for the next geometry trigger.
|
|
1238
|
+
// `boundaryMargin` is tracked alongside `boundary` so a runtime margin
|
|
1239
|
+
// change re-clamps immediately instead of waiting for the next
|
|
1240
|
+
// geometry trigger. Hydration runs first, synchronously, on the very
|
|
1241
|
+
// first execution — during initial change detection, BEFORE the first
|
|
1242
|
+
// paint — so a returning user's saved position and collapsed state
|
|
1243
|
+
// apply without a visible jump from the automatic anchor.
|
|
1244
|
+
effect(() => {
|
|
1245
|
+
const boundary = this.boundary();
|
|
1246
|
+
this.boundaryMargin();
|
|
1247
|
+
untracked(() => {
|
|
1248
|
+
this.hydrateOnce();
|
|
1249
|
+
this.drag.boundaryElement = boundary ?? '';
|
|
1250
|
+
this.syncBoundaryObservation();
|
|
1251
|
+
this.reposition();
|
|
1252
|
+
});
|
|
1253
|
+
});
|
|
1254
|
+
afterNextRender(() => {
|
|
1255
|
+
this.watchGeometry();
|
|
1256
|
+
// Re-clamp once real layout has settled — first-CD measurements are
|
|
1257
|
+
// forced synchronously and can differ slightly (fonts, late styles).
|
|
1258
|
+
this.reposition();
|
|
1259
|
+
});
|
|
1260
|
+
this.destroyRef.onDestroy(() => {
|
|
1261
|
+
this.footprintObserver?.disconnect();
|
|
1262
|
+
if (typeof window !== 'undefined') {
|
|
1263
|
+
window.removeEventListener('resize', this.onViewportResize);
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1111
1266
|
}
|
|
1112
1267
|
// ===== COMPLETION LOGIC =====
|
|
1113
1268
|
isComplete(step) {
|
|
@@ -1116,15 +1271,18 @@ class OnboardingGuideComponent {
|
|
|
1116
1271
|
}
|
|
1117
1272
|
return step.done;
|
|
1118
1273
|
}
|
|
1274
|
+
isPending(step) {
|
|
1275
|
+
return !this.isComplete(step) && step.pending === true;
|
|
1276
|
+
}
|
|
1119
1277
|
isActive(step) {
|
|
1120
|
-
return !this.isComplete(step) && this.
|
|
1278
|
+
return !this.isComplete(step) && this.resolvedOpenStepId() === step.id;
|
|
1121
1279
|
}
|
|
1122
1280
|
actionLabelFor(step) {
|
|
1123
1281
|
return step.actionLabel || this.startLabel();
|
|
1124
1282
|
}
|
|
1125
1283
|
// ===== INTERACTIONS =====
|
|
1126
1284
|
toggleStep(stepId) {
|
|
1127
|
-
this.openStepId.
|
|
1285
|
+
this.openStepId.set(this.resolvedOpenStepId() === stepId ? null : stepId);
|
|
1128
1286
|
}
|
|
1129
1287
|
onAction(step) {
|
|
1130
1288
|
this.stepAction.emit(step.id);
|
|
@@ -1132,6 +1290,9 @@ class OnboardingGuideComponent {
|
|
|
1132
1290
|
onMarkDone(step) {
|
|
1133
1291
|
this.stepMarkDone.emit(step.id);
|
|
1134
1292
|
}
|
|
1293
|
+
onSecondaryAction(step) {
|
|
1294
|
+
this.stepSecondaryAction.emit(step.id);
|
|
1295
|
+
}
|
|
1135
1296
|
onSubstep(step, substep) {
|
|
1136
1297
|
this.substepToggle.emit({ stepId: step.id, substepId: substep.id });
|
|
1137
1298
|
}
|
|
@@ -1150,63 +1311,211 @@ class OnboardingGuideComponent {
|
|
|
1150
1311
|
onDismiss() {
|
|
1151
1312
|
this.dismiss.emit();
|
|
1152
1313
|
}
|
|
1153
|
-
|
|
1154
|
-
this.
|
|
1314
|
+
onFooterAction() {
|
|
1315
|
+
this.footerAction.emit();
|
|
1155
1316
|
}
|
|
1156
1317
|
setCollapsed(value) {
|
|
1157
1318
|
this.collapsed.set(value);
|
|
1158
|
-
this.collapsedChange.emit(value);
|
|
1159
1319
|
this.persist();
|
|
1160
1320
|
}
|
|
1161
1321
|
// ===== DRAG =====
|
|
1162
1322
|
onDragEnded(event) {
|
|
1323
|
+
this.dragging = false;
|
|
1163
1324
|
const clamped = this.clamp(this.drag.getFreeDragPosition());
|
|
1164
1325
|
this.dragPosition.set(clamped);
|
|
1165
1326
|
this.drag.setFreeDragPosition(clamped);
|
|
1166
|
-
|
|
1167
|
-
|
|
1327
|
+
const moved = Math.abs(event.distance.x) > DRAG_THRESHOLD ||
|
|
1328
|
+
Math.abs(event.distance.y) > DRAG_THRESHOLD;
|
|
1168
1329
|
if (moved) {
|
|
1330
|
+
this.userMoved = true;
|
|
1169
1331
|
this.justDragged = true;
|
|
1170
1332
|
setTimeout(() => (this.justDragged = false));
|
|
1333
|
+
this.persist();
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
// ===== GEOMETRY =====
|
|
1337
|
+
/**
|
|
1338
|
+
* Keep the resting position valid as the world changes around it. Two
|
|
1339
|
+
* signals feed the same repositioning: the viewport resizing, and the
|
|
1340
|
+
* widget's own footprint changing (pill ↔ card swap, a step expanding) —
|
|
1341
|
+
* the latter is what stops a corner-parked pill from reopening as a card
|
|
1342
|
+
* that's mostly off-screen.
|
|
1343
|
+
*/
|
|
1344
|
+
watchGeometry() {
|
|
1345
|
+
if (typeof window === 'undefined') {
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
window.addEventListener('resize', this.onViewportResize);
|
|
1349
|
+
// Environments without ResizeObserver (some test runners, older
|
|
1350
|
+
// runtimes) keep the viewport-resize path; footprint tracking is a
|
|
1351
|
+
// progressive enhancement.
|
|
1352
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
this.footprintObserver = new ResizeObserver(() => this.reposition());
|
|
1356
|
+
this.footprintObserver.observe(this.host.nativeElement);
|
|
1357
|
+
this.syncBoundaryObservation();
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* The boundary region can resize without a viewport resize — a side
|
|
1361
|
+
* panel opening, chrome banners toggling — so the confining element is
|
|
1362
|
+
* observed alongside the widget's own footprint. Re-synced whenever the
|
|
1363
|
+
* `boundary` input changes.
|
|
1364
|
+
*/
|
|
1365
|
+
syncBoundaryObservation() {
|
|
1366
|
+
if (!this.footprintObserver) {
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
const next = this.resolveBoundaryElement();
|
|
1370
|
+
if (next === this.observedBoundary) {
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
if (this.observedBoundary) {
|
|
1374
|
+
this.footprintObserver.unobserve(this.observedBoundary);
|
|
1375
|
+
}
|
|
1376
|
+
if (next) {
|
|
1377
|
+
this.footprintObserver.observe(next);
|
|
1171
1378
|
}
|
|
1379
|
+
this.observedBoundary = next;
|
|
1380
|
+
}
|
|
1381
|
+
resolveBoundaryElement() {
|
|
1382
|
+
const boundary = this.boundary();
|
|
1383
|
+
if (typeof window === 'undefined' || !boundary) {
|
|
1384
|
+
return null;
|
|
1385
|
+
}
|
|
1386
|
+
return typeof boundary === 'string'
|
|
1387
|
+
? document.querySelector(boundary)
|
|
1388
|
+
: boundary;
|
|
1389
|
+
}
|
|
1390
|
+
onViewportResize = () => this.reposition();
|
|
1391
|
+
reposition() {
|
|
1392
|
+
if (typeof window === 'undefined') {
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
// Never fight a drag in flight: CDK owns the transform until the
|
|
1396
|
+
// pointer is released, and writing a position here (a resize or an
|
|
1397
|
+
// observer callback can land mid-drag) would tear the widget away
|
|
1398
|
+
// from the pointer. `onDragEnded` clamps the drop point anyway.
|
|
1399
|
+
if (this.dragging) {
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
// A selector boundary may not have matched at mount — re-resolve on
|
|
1403
|
+
// every geometry trigger so a late-appearing element gets observed.
|
|
1404
|
+
this.syncBoundaryObservation();
|
|
1405
|
+
const current = this.drag.getFreeDragPosition();
|
|
1406
|
+
const target = this.userMoved
|
|
1407
|
+
? this.clamp(current)
|
|
1408
|
+
: this.defaultBottomRight();
|
|
1409
|
+
if (target.x !== current.x || target.y !== current.y) {
|
|
1410
|
+
this.dragPosition.set(target);
|
|
1411
|
+
this.drag.setFreeDragPosition(target);
|
|
1412
|
+
// A deliberate placement that just got re-clamped (e.g. the card ↔
|
|
1413
|
+
// pill swap) supersedes whatever was stored moments earlier.
|
|
1414
|
+
if (this.userMoved) {
|
|
1415
|
+
this.persist();
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
/** The confining region: the `boundary` element's rect, or the viewport. */
|
|
1420
|
+
boundaryRect() {
|
|
1421
|
+
const el = this.resolveBoundaryElement();
|
|
1422
|
+
if (el) {
|
|
1423
|
+
return el.getBoundingClientRect();
|
|
1424
|
+
}
|
|
1425
|
+
return {
|
|
1426
|
+
left: 0,
|
|
1427
|
+
top: 0,
|
|
1428
|
+
right: typeof window === 'undefined' ? 0 : window.innerWidth,
|
|
1429
|
+
bottom: typeof window === 'undefined' ? 0 : window.innerHeight,
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Constrain one axis to `[start + margin, end - size - margin]`.
|
|
1434
|
+
*
|
|
1435
|
+
* When the region can't fit the widget plus both margins that range is
|
|
1436
|
+
* inverted, so the margin is dropped and the widget is fitted to the
|
|
1437
|
+
* region's own edges instead — keeping it inside the boundary rather
|
|
1438
|
+
* than letting the inset push it out. A widget genuinely larger than
|
|
1439
|
+
* its region still overflows (the card has a fixed width and something
|
|
1440
|
+
* has to give); it pins to the start edge.
|
|
1441
|
+
*/
|
|
1442
|
+
clampAxis(value, start, end, size, margin) {
|
|
1443
|
+
const min = start + margin;
|
|
1444
|
+
const max = end - size - margin;
|
|
1445
|
+
if (min <= max) {
|
|
1446
|
+
return Math.max(min, Math.min(value, max));
|
|
1447
|
+
}
|
|
1448
|
+
const tightMax = end - size;
|
|
1449
|
+
if (start <= tightMax) {
|
|
1450
|
+
return Math.max(start, Math.min(value, tightMax));
|
|
1451
|
+
}
|
|
1452
|
+
return start;
|
|
1172
1453
|
}
|
|
1173
1454
|
clamp(point) {
|
|
1174
1455
|
if (typeof window === 'undefined') {
|
|
1175
1456
|
return point;
|
|
1176
1457
|
}
|
|
1177
1458
|
const rect = this.host.nativeElement.getBoundingClientRect();
|
|
1178
|
-
const
|
|
1179
|
-
const
|
|
1459
|
+
const bounds = this.boundaryRect();
|
|
1460
|
+
const margin = this.boundaryMargin();
|
|
1180
1461
|
return {
|
|
1181
|
-
x:
|
|
1182
|
-
y:
|
|
1462
|
+
x: this.clampAxis(point.x, bounds.left, bounds.right, rect.width, margin),
|
|
1463
|
+
y: this.clampAxis(point.y, bounds.top, bounds.bottom, rect.height, margin),
|
|
1183
1464
|
};
|
|
1184
1465
|
}
|
|
1185
|
-
// ===== PERSISTENCE =====
|
|
1186
|
-
hydrate() {
|
|
1187
|
-
const saved = this.read();
|
|
1188
|
-
this.collapsed.set(saved.collapsed ?? this.startCollapsed());
|
|
1189
|
-
const pos = saved.pos ? this.clamp(saved.pos) : this.defaultBottomRight();
|
|
1190
|
-
this.dragPosition.set(pos);
|
|
1191
|
-
this.drag.setFreeDragPosition(pos);
|
|
1192
|
-
}
|
|
1193
1466
|
defaultBottomRight() {
|
|
1194
1467
|
if (typeof window === 'undefined') {
|
|
1195
1468
|
return { x: 0, y: 0 };
|
|
1196
1469
|
}
|
|
1197
1470
|
const rect = this.host.nativeElement.getBoundingClientRect();
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1471
|
+
const bounds = this.boundaryRect();
|
|
1472
|
+
const margin = this.boundaryMargin();
|
|
1473
|
+
// Aim at the bottom-right inset and let the shared clamp resolve it —
|
|
1474
|
+
// including the too-narrow-region fallbacks.
|
|
1475
|
+
return this.clamp({
|
|
1476
|
+
x: bounds.right - rect.width - margin,
|
|
1477
|
+
y: bounds.bottom - rect.height - margin,
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
// ===== PERSISTENCE =====
|
|
1481
|
+
/**
|
|
1482
|
+
* One-time restore of the persisted preferences, run from the first
|
|
1483
|
+
* execution of the boundary effect — inputs are set by then, storage
|
|
1484
|
+
* reads are synchronous, and it lands during initial change detection
|
|
1485
|
+
* so the restored state is what the first paint shows. The caller's
|
|
1486
|
+
* `reposition()` clamps the raw restored position right afterwards.
|
|
1487
|
+
*/
|
|
1488
|
+
hydrateOnce() {
|
|
1489
|
+
if (this.hydrated) {
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
this.hydrated = true;
|
|
1493
|
+
const saved = this.read();
|
|
1494
|
+
// The stored collapsed preference restores only when the model still
|
|
1495
|
+
// holds its default — a consumer that mounted the widget with a bound,
|
|
1496
|
+
// non-default `collapsed` gave an explicit instruction, and storage
|
|
1497
|
+
// must not overwrite it (or echo the stale value back out through
|
|
1498
|
+
// `collapsedChange`). Fully controlled usage should disable
|
|
1499
|
+
// persistence with `storageKey: null`.
|
|
1500
|
+
if (saved.collapsed != null && this.collapsed() === false) {
|
|
1501
|
+
this.collapsed.set(saved.collapsed);
|
|
1502
|
+
}
|
|
1503
|
+
if (saved.pos) {
|
|
1504
|
+
// A persisted position is a deliberate placement from a previous
|
|
1505
|
+
// session — keep honouring it instead of re-anchoring bottom-right.
|
|
1506
|
+
this.userMoved = true;
|
|
1507
|
+
this.dragPosition.set(saved.pos);
|
|
1508
|
+
this.drag.setFreeDragPosition(saved.pos);
|
|
1509
|
+
}
|
|
1202
1510
|
}
|
|
1203
1511
|
read() {
|
|
1204
1512
|
const store = this.storage();
|
|
1205
|
-
|
|
1513
|
+
const key = this.storageKey();
|
|
1514
|
+
if (!store || !key) {
|
|
1206
1515
|
return {};
|
|
1207
1516
|
}
|
|
1208
1517
|
try {
|
|
1209
|
-
return JSON.parse(store.getItem(
|
|
1518
|
+
return JSON.parse(store.getItem(key) || '{}');
|
|
1210
1519
|
}
|
|
1211
1520
|
catch {
|
|
1212
1521
|
return {};
|
|
@@ -1214,15 +1523,20 @@ class OnboardingGuideComponent {
|
|
|
1214
1523
|
}
|
|
1215
1524
|
persist() {
|
|
1216
1525
|
const store = this.storage();
|
|
1217
|
-
|
|
1526
|
+
const key = this.storageKey();
|
|
1527
|
+
if (!store || !key) {
|
|
1218
1528
|
return;
|
|
1219
1529
|
}
|
|
1530
|
+
// Position is a preference only once the user has actually dragged —
|
|
1531
|
+
// persisting the automatic bottom-right anchor would make the next
|
|
1532
|
+
// load's hydrate treat it as a deliberate placement and stop the
|
|
1533
|
+
// widget from re-anchoring on geometry changes.
|
|
1220
1534
|
const state = {
|
|
1221
1535
|
collapsed: this.collapsed(),
|
|
1222
|
-
pos: this.dragPosition(),
|
|
1536
|
+
pos: this.userMoved ? this.dragPosition() : undefined,
|
|
1223
1537
|
};
|
|
1224
1538
|
try {
|
|
1225
|
-
store.setItem(
|
|
1539
|
+
store.setItem(key, JSON.stringify(state));
|
|
1226
1540
|
}
|
|
1227
1541
|
catch {
|
|
1228
1542
|
// Storage unavailable (private mode / quota) — preferences just don't stick.
|
|
@@ -1237,12 +1551,12 @@ class OnboardingGuideComponent {
|
|
|
1237
1551
|
}
|
|
1238
1552
|
}
|
|
1239
1553
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OnboardingGuideComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1240
|
-
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 });
|
|
1554
|
+
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 }, boundary: { classPropertyName: "boundary", publicName: "boundary", isSignal: true, isRequired: false, transformFunction: null }, boundaryMargin: { classPropertyName: "boundaryMargin", publicName: "boundaryMargin", isSignal: true, isRequired: false, transformFunction: null }, renamable: { classPropertyName: "renamable", publicName: "renamable", isSignal: true, isRequired: false, transformFunction: null }, showMarkDone: { classPropertyName: "showMarkDone", publicName: "showMarkDone", 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 }, footerActionLabel: { classPropertyName: "footerActionLabel", publicName: "footerActionLabel", 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 }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", 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 }, openStepId: { classPropertyName: "openStepId", publicName: "openStepId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange", stepAction: "stepAction", stepMarkDone: "stepMarkDone", stepSecondaryAction: "stepSecondaryAction", substepToggle: "substepToggle", footerAction: "footerAction", renameClick: "renameClick", dismiss: "dismiss", openStepId: "openStepIdChange" }, 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 @if (renamable()) {\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\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 pending = isPending(step);\n @let open = resolvedOpenStepId() === 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--pending]=\"pending\"\n [class.fk-onboarding-guide__check--active]=\"active && !pending\"\n [class.fk-onboarding-guide__check--todo]=\"\n !done && !active && !pending\n \"\n aria-hidden=\"true\"\n >\n @if (done) {\n <fk-icon [name]=\"checkIcon()\" size=\"xs\" />\n } @else if (active || pending) {\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 (pending && step.pendingNote) {\n <p class=\"fk-onboarding-guide__pending-note\">\n {{ step.pendingNote }}\n </p>\n }\n\n @if (!done || step.secondaryActionLabel) {\n <div class=\"fk-onboarding-guide__actions\">\n @if (!done) {\n <fk-button\n variant=\"primary\"\n size=\"sm\"\n (click)=\"onAction(step)\"\n >\n {{ actionLabelFor(step) }}\n <fk-icon\n fkButtonIconEnd\n [name]=\"actionIcon()\"\n size=\"xs\"\n />\n </fk-button>\n\n @if (showMarkDone()) {\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__skip\"\n (click)=\"onMarkDone(step)\"\n >\n {{ markDoneLabel() }}\n </button>\n }\n }\n\n @if (step.secondaryActionLabel) {\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__secondary\"\n (click)=\"onSecondaryAction(step)\"\n >\n {{ step.secondaryActionLabel }}\n </button>\n }\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\n @if (footerActionLabel(); as footerLabel) {\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__go\"\n (click)=\"onFooterAction()\"\n >\n {{ footerLabel }}\n <fk-icon [name]=\"actionIcon()\" size=\"xs\" />\n </button>\n }\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__icon-btn:focus-visible,.fk-onboarding-guide__step-row:focus-visible,.fk-onboarding-guide__sub:focus-visible,.fk-onboarding-guide__skip:focus-visible,.fk-onboarding-guide__secondary:focus-visible,.fk-onboarding-guide__go:focus-visible,.fk-onboarding-guide__pill:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(37, 99, 235, .18))}.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__check--pending{border:2px solid var(--fk-onboarding-guide-pending, var(--fk-color-warning, #d97706));background:color-mix(in srgb,var(--fk-onboarding-guide-pending, var(--fk-color-warning, #d97706)) 12%,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__check--pending .fk-onboarding-guide__dot{background:var(--fk-onboarding-guide-pending, var(--fk-color-warning, #d97706))}.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__pending-note{font-size:12.5px;line-height:1.5;color:var(--fk-onboarding-guide-pending-strong, var(--fk-onboarding-guide-pending, var(--fk-color-warning, #92400e)));margin-bottom:12px}.fk-onboarding-guide__actions{display:flex;align-items:center;gap:8px}.fk-onboarding-guide__skip,.fk-onboarding-guide__secondary{font-size:13px;font-weight:600;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b));background:none;border:none;border-radius:6px;padding:6px;cursor:pointer}.fk-onboarding-guide__skip:hover,.fk-onboarding-guide__secondary: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 });
|
|
1241
1555
|
}
|
|
1242
1556
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OnboardingGuideComponent, decorators: [{
|
|
1243
1557
|
type: Component,
|
|
1244
|
-
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"] }]
|
|
1245
|
-
}], 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 }] }],
|
|
1558
|
+
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 @if (renamable()) {\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\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 pending = isPending(step);\n @let open = resolvedOpenStepId() === 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--pending]=\"pending\"\n [class.fk-onboarding-guide__check--active]=\"active && !pending\"\n [class.fk-onboarding-guide__check--todo]=\"\n !done && !active && !pending\n \"\n aria-hidden=\"true\"\n >\n @if (done) {\n <fk-icon [name]=\"checkIcon()\" size=\"xs\" />\n } @else if (active || pending) {\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 (pending && step.pendingNote) {\n <p class=\"fk-onboarding-guide__pending-note\">\n {{ step.pendingNote }}\n </p>\n }\n\n @if (!done || step.secondaryActionLabel) {\n <div class=\"fk-onboarding-guide__actions\">\n @if (!done) {\n <fk-button\n variant=\"primary\"\n size=\"sm\"\n (click)=\"onAction(step)\"\n >\n {{ actionLabelFor(step) }}\n <fk-icon\n fkButtonIconEnd\n [name]=\"actionIcon()\"\n size=\"xs\"\n />\n </fk-button>\n\n @if (showMarkDone()) {\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__skip\"\n (click)=\"onMarkDone(step)\"\n >\n {{ markDoneLabel() }}\n </button>\n }\n }\n\n @if (step.secondaryActionLabel) {\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__secondary\"\n (click)=\"onSecondaryAction(step)\"\n >\n {{ step.secondaryActionLabel }}\n </button>\n }\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\n @if (footerActionLabel(); as footerLabel) {\n <button\n type=\"button\"\n class=\"fk-onboarding-guide__go\"\n (click)=\"onFooterAction()\"\n >\n {{ footerLabel }}\n <fk-icon [name]=\"actionIcon()\" size=\"xs\" />\n </button>\n }\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__icon-btn:focus-visible,.fk-onboarding-guide__step-row:focus-visible,.fk-onboarding-guide__sub:focus-visible,.fk-onboarding-guide__skip:focus-visible,.fk-onboarding-guide__secondary:focus-visible,.fk-onboarding-guide__go:focus-visible,.fk-onboarding-guide__pill:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(37, 99, 235, .18))}.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__check--pending{border:2px solid var(--fk-onboarding-guide-pending, var(--fk-color-warning, #d97706));background:color-mix(in srgb,var(--fk-onboarding-guide-pending, var(--fk-color-warning, #d97706)) 12%,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__check--pending .fk-onboarding-guide__dot{background:var(--fk-onboarding-guide-pending, var(--fk-color-warning, #d97706))}.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__pending-note{font-size:12.5px;line-height:1.5;color:var(--fk-onboarding-guide-pending-strong, var(--fk-onboarding-guide-pending, var(--fk-color-warning, #92400e)));margin-bottom:12px}.fk-onboarding-guide__actions{display:flex;align-items:center;gap:8px}.fk-onboarding-guide__skip,.fk-onboarding-guide__secondary{font-size:13px;font-weight:600;color:var(--fk-onboarding-guide-text-muted, var(--fk-color-muted, #64748b));background:none;border:none;border-radius:6px;padding:6px;cursor:pointer}.fk-onboarding-guide__skip:hover,.fk-onboarding-guide__secondary: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"] }]
|
|
1559
|
+
}], 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 }] }], boundary: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundary", required: false }] }], boundaryMargin: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundaryMargin", required: false }] }], renamable: [{ type: i0.Input, args: [{ isSignal: true, alias: "renamable", required: false }] }], showMarkDone: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMarkDone", required: false }] }], startLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "startLabel", required: false }] }], markDoneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "markDoneLabel", required: false }] }], footerActionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerActionLabel", 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 }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], stepAction: [{ type: i0.Output, args: ["stepAction"] }], stepMarkDone: [{ type: i0.Output, args: ["stepMarkDone"] }], stepSecondaryAction: [{ type: i0.Output, args: ["stepSecondaryAction"] }], substepToggle: [{ type: i0.Output, args: ["substepToggle"] }], footerAction: [{ type: i0.Output, args: ["footerAction"] }], renameClick: [{ type: i0.Output, args: ["renameClick"] }], 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 }] }], openStepId: [{ type: i0.Input, args: [{ isSignal: true, alias: "openStepId", required: false }] }, { type: i0.Output, args: ["openStepIdChange"] }], hostClass: [{
|
|
1246
1560
|
type: HostBinding,
|
|
1247
1561
|
args: ['class']
|
|
1248
1562
|
}], hostId: [{
|