@factorialco/f0-react 6.85.0 → 6.86.1

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.
@@ -791,6 +791,15 @@ declare type AlertVariant = (typeof alertVariantOptions)[number];
791
791
 
792
792
  declare const alertVariantOptions: readonly ["info", "warning", "critical", "neutral", "positive"];
793
793
 
794
+ /**
795
+ * The attribute a guidance's `anchor()` writes, and the one its steps are
796
+ * resolved through. A data attribute rather than the `id` attribute: an id is
797
+ * the page's own namespace — one per document, handed out by whatever renders
798
+ * the element — and a walkthrough that claimed ids would collide with the app's
799
+ * own the first time two of them named the same thing.
800
+ */
801
+ declare const ANCHOR_ATTRIBUTE = "data-f0-coachmark";
802
+
794
803
  /**
795
804
  * @experimental This is an experimental component use it at your own risk
796
805
  */
@@ -2839,17 +2848,33 @@ export declare type CoachmarkAction = {
2839
2848
  onClick?: () => void;
2840
2849
  };
2841
2850
 
2842
- declare type CoachmarkBase = CoachmarkPlacement & {
2851
+ declare type CoachmarkBase = CoachmarkPlacement & CoachmarkFocus & {
2843
2852
  /**
2844
2853
  * Stable identity. Opening again with the same id replaces that coachmark
2845
2854
  * instead of queueing a second one, so an effect that runs twice shows one
2846
2855
  * coachmark. Defaults to a generated id.
2847
2856
  */
2848
2857
  id?: CoachmarkId;
2858
+ /**
2859
+ * HOW IT ENDED, IN ONE PLACE — reached the end, left part-way through, or
2860
+ * pressed past until it gave up, and how far the reader got either way. The
2861
+ * callback to reach for when tracking a walkthrough: every ending comes
2862
+ * through here exactly once, so a funnel is one event carrying a `reason`
2863
+ * rather than two callbacks to join up afterwards.
2864
+ *
2865
+ * NOT called when the app itself closes the coachmark (`coachmarks.close`, a
2866
+ * guidance's `stop()`, the page unmounting): nobody ended it, so there is no
2867
+ * outcome to report.
2868
+ */
2869
+ onEnd?: (end: CoachmarkEnd) => void;
2849
2870
  /**
2850
2871
  * Called when the user closes the coachmark with the close button or Escape,
2851
2872
  * before the last step is reached. For tracking only — the coachmark closes
2852
2873
  * itself either way.
2874
+ *
2875
+ * Also fires when a walkthrough gives up after too many presses on the
2876
+ * dimmed page, which is a dismissal by any other name. `onEnd` is what tells
2877
+ * those two apart.
2853
2878
  */
2854
2879
  onDismiss?: () => void;
2855
2880
  /**
@@ -2857,6 +2882,26 @@ declare type CoachmarkBase = CoachmarkPlacement & {
2857
2882
  * — the coachmark closes itself either way.
2858
2883
  */
2859
2884
  onComplete?: () => void;
2885
+ /**
2886
+ * SPOTLIGHT THE TARGET: dims the whole page except the element this step
2887
+ * points at, and swallows every press on the page while the coachmark is up
2888
+ * (see `skipAfterOutsideClicks` for how a user who keeps pressing gets out).
2889
+ *
2890
+ * Off by default — one coachmark pointing something out should not take the
2891
+ * page hostage. Turn it on for a walkthrough that has to be followed in order.
2892
+ */
2893
+ overlay?: boolean;
2894
+ /**
2895
+ * HOW MANY PRESSES ON THE DIMMED PAGE END THE COACHMARK. The panel wiggles at
2896
+ * each one to say the press went nowhere, and gives up at this many: a user
2897
+ * pressing outside over and over is telling us they want out, and the way out
2898
+ * cannot be the button they are ignoring. Reported to `onDismiss` like any
2899
+ * other abandonment. Defaults to 5; `0` never gives up.
2900
+ *
2901
+ * Only has an effect alongside `overlay` — without the shield there are no
2902
+ * presses to count, because they reach the page.
2903
+ */
2904
+ skipAfterOutsideClicks?: number;
2860
2905
  };
2861
2906
 
2862
2907
  declare type CoachmarkContent = {
@@ -2868,6 +2913,144 @@ declare type CoachmarkContent = {
2868
2913
  action?: CoachmarkAction;
2869
2914
  };
2870
2915
 
2916
+ /** What `onEnd` is told. */
2917
+ export declare type CoachmarkEnd = {
2918
+ reason: CoachmarkEndReason;
2919
+ /**
2920
+ * The step it ended on, 1-based — how far the reader got. `0` when it never
2921
+ * opened (`unavailable`).
2922
+ */
2923
+ step: number;
2924
+ /**
2925
+ * How many steps the reader was actually offered. Not necessarily how many
2926
+ * were declared: a guidance leaves out the steps whose element was not there.
2927
+ */
2928
+ totalSteps: number;
2929
+ /**
2930
+ * Presses on the dimmed page over the whole coachmark — the wiggles. A tour
2931
+ * that completed with six of these was fought with; one that completed with
2932
+ * none was followed. Always `0` without `overlay`, which has no shield to
2933
+ * press.
2934
+ */
2935
+ outsidePresses: number;
2936
+ };
2937
+
2938
+ /**
2939
+ * HOW A COACHMARK ENDED. One value per way out, so a funnel can be read off it
2940
+ * without joining two callbacks together:
2941
+ *
2942
+ * - `completed` — the action on the last step. The reader saw the whole thing.
2943
+ * - `dismissed` — the close button or Escape, before the last step. They left
2944
+ * part-way through, and `step` says where.
2945
+ * - `skipped` — it gave up after `skipAfterOutsideClicks` presses on the dimmed
2946
+ * page. Not the same as dismissing: the reader never used the way out they
2947
+ * were offered, they pressed past it until it went away.
2948
+ * - `unavailable` — it never opened, because nothing it points at was on the
2949
+ * page (only `defineStepByStepCoachmarkGuidance` reports this). The reason a
2950
+ * funnel can be missing readers who were never shown anything.
2951
+ */
2952
+ export declare type CoachmarkEndReason = "completed" | "dismissed" | "skipped" | "unavailable";
2953
+
2954
+ declare type CoachmarkFocus = {
2955
+ /**
2956
+ * PUT THE CARET WHERE THE STEP IS POINTING. Focus goes to the target — or to
2957
+ * the first field inside it — instead of to the panel, so the element the
2958
+ * coachmark is explaining lights up the way it does when the reader lands on
2959
+ * it themselves: a composer with its cursor in it and its own focus glow,
2960
+ * rather than a box being described.
2961
+ *
2962
+ * OFF BY DEFAULT, and worth being deliberate about. The panel takes focus
2963
+ * precisely so a screen reader reads the step out and so Enter cannot fire
2964
+ * the action unread; handing focus to a field instead trades that away —
2965
+ * the step is no longer announced, and typing goes into the page. Use it on a
2966
+ * step whose whole point is the field (a composer, a search box), and leave
2967
+ * every other step to the panel.
2968
+ *
2969
+ * Escape still closes the coachmark from anywhere, and the action button is
2970
+ * still one Tab away.
2971
+ */
2972
+ focusTarget?: boolean;
2973
+ };
2974
+
2975
+ export declare type CoachmarkGuidance<TElement extends string> = {
2976
+ /** The id every `start()` opens under, and the one `stop()` closes. */
2977
+ id: CoachmarkId;
2978
+ /**
2979
+ * MARKS AN ELEMENT AS A STEP'S TARGET. Spread onto the element (or onto any
2980
+ * component that forwards unknown props to its DOM node):
2981
+ *
2982
+ * `<section {...guidance.anchor("needs-you")}>`
2983
+ *
2984
+ * Only names declared by a step type-check, so a renamed step breaks at the
2985
+ * anchor rather than at run time — where a missing target is a coachmark that
2986
+ * silently waits for an element that is never coming.
2987
+ */
2988
+ anchor: (element: TElement) => Record<typeof ANCHOR_ATTRIBUTE, TElement>;
2989
+ /** The selector `anchor(element)` is found by. For tests and edge cases. */
2990
+ selector: (element: TElement) => string;
2991
+ /**
2992
+ * Start the walkthrough — once the elements it points at are actually on the
2993
+ * page (see `lookForTargetsMs`). Steps whose element never turns up are left
2994
+ * out, and a walkthrough with nothing left to point at never opens at all.
2995
+ * Returns the id it will open under, whether it has opened yet or not.
2996
+ */
2997
+ start: () => CoachmarkId;
2998
+ /** End it wherever it is. Reports nothing: nobody dismissed it. */
2999
+ stop: () => void;
3000
+ };
3001
+
3002
+ export declare type CoachmarkGuidanceOptions<TElement extends string> = {
3003
+ /**
3004
+ * Stable identity, so starting the same guidance twice shows ONE walkthrough.
3005
+ * Defaults to a generated id.
3006
+ */
3007
+ id?: CoachmarkId;
3008
+ /** The walkthrough, in order. */
3009
+ steps: readonly CoachmarkGuidanceStep<TElement>[];
3010
+ /**
3011
+ * Spotlight each step's element and shield the page from the pointer.
3012
+ * Defaults to `true` — a walkthrough is a sequence, and a page you can act on
3013
+ * mid-sequence is a sequence the user has already left.
3014
+ */
3015
+ overlay?: boolean;
3016
+ /** Presses on the dimmed page that end the walkthrough. Defaults to 5. */
3017
+ skipAfterOutsideClicks?: number;
3018
+ /**
3019
+ * HOW LONG `start()` KEEPS LOOKING for the steps whose elements are not on the
3020
+ * page yet, before running the walkthrough without them. Defaults to 2000ms.
3021
+ *
3022
+ * A walkthrough is started on mount, and the things it walks arrive over the
3023
+ * next few hundred milliseconds — a rail that is still measuring itself, a
3024
+ * widget waiting on its data. Opening on the first frame would drop those
3025
+ * steps; waiting forever on one that is genuinely absent (a control this user
3026
+ * has no permission for) would mean no walkthrough at all.
3027
+ */
3028
+ lookForTargetsMs?: number;
3029
+ /**
3030
+ * HOW IT ENDED, IN ONE PLACE: finished, left part-way through, pressed past
3031
+ * until it gave up — or never opened at all, because nothing it points at was
3032
+ * on the page. One event with a `reason`, which is what a funnel wants.
3033
+ */
3034
+ onEnd?: (end: CoachmarkEnd) => void;
3035
+ /** Abandoned: closed, escaped, or skipped by pressing past it. */
3036
+ onDismiss?: () => void;
3037
+ /** Finished: the action on the last step. */
3038
+ onComplete?: () => void;
3039
+ };
3040
+
3041
+ /**
3042
+ * One step of a walkthrough. It points either at a NAME the guidance knows —
3043
+ * marked on the element with `anchor()` — or, for an element you cannot put
3044
+ * props on (something a library renders), straight at a selector or an element.
3045
+ */
3046
+ export declare type CoachmarkGuidanceStep<TElement extends string> = Omit<CoachmarkStep, "targetElement"> & ({
3047
+ element: TElement;
3048
+ targetElement?: never;
3049
+ } | {
3050
+ element?: never;
3051
+ targetElement: CoachmarkTarget;
3052
+ });
3053
+
2871
3054
  export declare type CoachmarkId = string;
2872
3055
 
2873
3056
  /**
@@ -2977,15 +3160,25 @@ export declare type CoachmarkSingleOptions = CoachmarkBase & CoachmarkContent &
2977
3160
  * its own placement; anything it leaves out falls back to the value passed
2978
3161
  * alongside `steps`.
2979
3162
  */
2980
- export declare type CoachmarkStep = CoachmarkContent & CoachmarkPlacement & {
3163
+ export declare type CoachmarkStep = CoachmarkContent & CoachmarkPlacement & CoachmarkFocus & {
2981
3164
  /** Falls back to the `targetElement` passed alongside `steps`. */
2982
3165
  targetElement?: CoachmarkTarget;
2983
3166
  };
2984
3167
 
2985
3168
  /**
2986
- * What the coachmark points at: a CSS selector that must match exactly one
2987
- * element, or the element itself. A selector is re-resolved while the coachmark
2988
- * is queued, so it may point at something that mounts later.
3169
+ * What the coachmark points at: ANY CSS SELECTOR, or the element itself.
3170
+ *
3171
+ * An id (`"#filters-button"`), a class (`".js-filters"`), an attribute
3172
+ * (`'[data-add-widget="right"]'`), or anything else `querySelector` takes — the
3173
+ * string is handed straight to the DOM, so the choice is about what the page
3174
+ * can promise to keep stable, not about what this accepts. It must match
3175
+ * exactly ONE element: a selector that matches several anchors to the first and
3176
+ * warns in development, because a coachmark pointing at "one of these six
3177
+ * cards" is pointing at nothing in particular.
3178
+ *
3179
+ * A selector is re-resolved while the coachmark is queued, so it may point at
3180
+ * something that mounts later. An ELEMENT is not re-resolved (there is nothing
3181
+ * to re-run), so one that unmounts takes its coachmark off screen with it.
2989
3182
  */
2990
3183
  export declare type CoachmarkTarget = string | HTMLElement;
2991
3184
 
@@ -5235,6 +5428,48 @@ declare const defaultTranslations: {
5235
5428
  };
5236
5429
  };
5237
5430
 
5431
+ /**
5432
+ * A STEP-BY-STEP WALKTHROUGH OF A PAGE, declared in one place.
5433
+ *
5434
+ * `coachmarks.open({ steps })` already shows steps one at a time; what it takes
5435
+ * is a CSS selector per step, which means every walkthrough invents its own
5436
+ * convention for marking the elements it walks — and a selector written against
5437
+ * someone else's markup breaks the next time that markup is refactored, without
5438
+ * a single type error to say so.
5439
+ *
5440
+ * This closes that loop: the steps name their targets, `anchor()` marks them,
5441
+ * and the names are a union the compiler holds both sides to. The walkthrough
5442
+ * also arrives with the manners a walkthrough needs — the page dimmed to the
5443
+ * step's element, the pointer shielded, and a way out for the user who keeps
5444
+ * pressing past it — because those are properties of walking someone through a
5445
+ * page rather than of one coachmark.
5446
+ *
5447
+ * @example
5448
+ * const tour = defineStepByStepCoachmarkGuidance({
5449
+ * id: "home-tour",
5450
+ * steps: [
5451
+ * { element: "composer", title: "Let One do it for you", side: "bottom" },
5452
+ * { element: "needs-you", title: "What needs you", side: "right" },
5453
+ * // Something f0 renders: point at it directly.
5454
+ * { targetElement: '[data-add-widget="right"]', title: "Add a widget" },
5455
+ * ],
5456
+ * // Finished, dropped out at step N, pressed past it, or never shown.
5457
+ * onEnd: ({ reason, step, totalSteps }) =>
5458
+ * track("home-tour-ended", { reason, step, totalSteps }),
5459
+ * })
5460
+ *
5461
+ * // In the page
5462
+ * <div {...tour.anchor("composer")}>…</div>
5463
+ * <section {...tour.anchor("needs-you")}>…</section>
5464
+ *
5465
+ * // Whenever it should run
5466
+ * useEffect(() => {
5467
+ * tour.start()
5468
+ * return () => tour.stop()
5469
+ * }, [tour])
5470
+ */
5471
+ export declare const defineStepByStepCoachmarkGuidance: <const TElement extends string>(options: CoachmarkGuidanceOptions<TElement>) => CoachmarkGuidance<TElement>;
5472
+
5238
5473
  export declare interface DeleteBlockNotesTextEditorPageDocumentPatch {
5239
5474
  type: "delete_block";
5240
5475
  targetId: string;
@@ -15714,18 +15949,6 @@ declare module "@tiptap/core" {
15714
15949
  }
15715
15950
 
15716
15951
 
15717
- declare module "@tiptap/core" {
15718
- interface Commands<ReturnType> {
15719
- enhanceHighlight: {
15720
- setEnhanceHighlight: (from: number, to: number, options?: {
15721
- placeholder?: string;
15722
- }) => ReturnType;
15723
- clearEnhanceHighlight: () => ReturnType;
15724
- };
15725
- }
15726
- }
15727
-
15728
-
15729
15952
  declare module "@tiptap/core" {
15730
15953
  interface Commands<ReturnType> {
15731
15954
  fontSize: {
@@ -15747,6 +15970,18 @@ declare module "@tiptap/core" {
15747
15970
  }
15748
15971
 
15749
15972
 
15973
+ declare module "@tiptap/core" {
15974
+ interface Commands<ReturnType> {
15975
+ enhanceHighlight: {
15976
+ setEnhanceHighlight: (from: number, to: number, options?: {
15977
+ placeholder?: string;
15978
+ }) => ReturnType;
15979
+ clearEnhanceHighlight: () => ReturnType;
15980
+ };
15981
+ }
15982
+ }
15983
+
15984
+
15750
15985
  declare module "@tiptap/core" {
15751
15986
  interface Commands<ReturnType> {
15752
15987
  moodTracker: {