@geomak/ui 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +199 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -16
- package/dist/index.d.ts +69 -16
- package/dist/index.js +200 -62
- package/dist/index.js.map +1 -1
- package/dist/styles.css +50 -26
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -928,34 +928,87 @@ interface ContextMenuPosition {
|
|
|
928
928
|
}
|
|
929
929
|
|
|
930
930
|
interface WizardStep {
|
|
931
|
-
/** Ref to the DOM element to highlight */
|
|
932
|
-
stepRef: React$1.RefObject<HTMLElement>;
|
|
931
|
+
/** Ref to the DOM element to highlight for this step. */
|
|
932
|
+
stepRef: React$1.RefObject<HTMLElement | null>;
|
|
933
|
+
/** Tooltip body content. */
|
|
933
934
|
description: React$1.ReactNode;
|
|
934
|
-
/**
|
|
935
|
-
|
|
935
|
+
/**
|
|
936
|
+
* Tooltip placement relative to the highlighted element.
|
|
937
|
+
* - `'right'` (default) — to the right of the highlight
|
|
938
|
+
* - `'left'` — to the left of the highlight
|
|
939
|
+
* - `'top'` — above the highlight
|
|
940
|
+
* - `'bottom'` — below the highlight
|
|
941
|
+
*/
|
|
942
|
+
placement?: 'right' | 'left' | 'top' | 'bottom';
|
|
943
|
+
/** Optional heading for the step's tooltip. */
|
|
944
|
+
title?: React$1.ReactNode;
|
|
936
945
|
}
|
|
937
946
|
interface WizardProps {
|
|
947
|
+
/** The wrapped subtree — refs in `steps` point into this tree. */
|
|
938
948
|
children: React$1.ReactNode;
|
|
949
|
+
/** Ordered list of steps to walk the user through. */
|
|
939
950
|
steps: WizardStep[];
|
|
940
|
-
/**
|
|
941
|
-
|
|
951
|
+
/**
|
|
952
|
+
* `localStorage` key used to remember dismissal. When the user clicks
|
|
953
|
+
* "Done" (or "Skip" when dismissible), this key is set so the wizard
|
|
954
|
+
* won't reopen on subsequent mounts.
|
|
955
|
+
*
|
|
956
|
+
* Pass `null` to disable persistence entirely (useful for tests or
|
|
957
|
+
* tours that should always run).
|
|
958
|
+
*
|
|
959
|
+
* Default: `'oxygen.wizard.completed'`.
|
|
960
|
+
*/
|
|
961
|
+
storageKey?: string | null;
|
|
962
|
+
/** Show a "Skip tour" button + Esc-to-dismiss. Default `true`. */
|
|
963
|
+
dismissible?: boolean;
|
|
964
|
+
/** Called when the user reaches the final step's "Done" button. */
|
|
965
|
+
onComplete?: () => void;
|
|
966
|
+
/** Called when the user skips (or presses Esc) before completing. */
|
|
967
|
+
onSkip?: () => void;
|
|
942
968
|
}
|
|
943
969
|
/**
|
|
944
|
-
* Guided-tour overlay
|
|
970
|
+
* Guided-tour overlay that walks the user through a sequence of UI elements.
|
|
971
|
+
*
|
|
972
|
+
* Highlights each step's target element with an outlined "spotlight", shows a
|
|
973
|
+
* tooltip with the description, and provides Prev / Next / Done navigation.
|
|
974
|
+
* The wrapped tree is rendered unchanged; the overlay sits on top of it via a
|
|
975
|
+
* portal so it always covers the real viewport regardless of where Wizard
|
|
976
|
+
* lives in the React tree.
|
|
945
977
|
*
|
|
946
|
-
*
|
|
947
|
-
*
|
|
978
|
+
* **What's improved over the previous version**
|
|
979
|
+
* - `localStorage` access is SSR-safe and try/catch-guarded (Safari private
|
|
980
|
+
* mode, quota exceeded, no-storage browsers all degrade silently).
|
|
981
|
+
* - No more `classList.add(...)` on consumer-owned DOM. The spotlight is a
|
|
982
|
+
* portaled outline rectangle that tracks the target's bbox via
|
|
983
|
+
* `ResizeObserver` + scroll/resize listeners. The consumer's DOM is never
|
|
984
|
+
* mutated, so unmounting Wizard mid-tour leaves no orphan classes.
|
|
985
|
+
* - Focus trap inside the tooltip — Tab and Shift+Tab cycle through the
|
|
986
|
+
* tooltip's buttons. Esc dismisses (when `dismissible`).
|
|
987
|
+
* - Backdrop blocks click-through on the rest of the page so the user can't
|
|
988
|
+
* stumble into unrelated UI mid-tour. The highlighted target itself stays
|
|
989
|
+
* click-blocked too (use the "Next" button to advance).
|
|
990
|
+
* - Position recalculates on scroll / resize / target size changes via
|
|
991
|
+
* `ResizeObserver`.
|
|
992
|
+
* - All `dark-cornflower-blue` / `prussian-blue` / `bg-white` swapped for
|
|
993
|
+
* semantic tokens — both light and dark modes work out of the box.
|
|
948
994
|
*
|
|
949
995
|
* @example
|
|
950
|
-
*
|
|
951
|
-
* const
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
* <Wizard
|
|
955
|
-
*
|
|
996
|
+
* ```tsx
|
|
997
|
+
* const dashRef = useRef<HTMLDivElement>(null)
|
|
998
|
+
* const sidebarRef = useRef<HTMLElement>(null)
|
|
999
|
+
*
|
|
1000
|
+
* <Wizard
|
|
1001
|
+
* steps={[
|
|
1002
|
+
* { stepRef: dashRef, title: 'Dashboard', description: 'Your fleet at a glance.' },
|
|
1003
|
+
* { stepRef: sidebarRef, title: 'Navigation', description: 'Jump between sections here.', placement: 'right' },
|
|
1004
|
+
* ]}
|
|
1005
|
+
* onComplete={() => track('onboarding_complete')}
|
|
1006
|
+
* >
|
|
1007
|
+
* <Layout dashRef={dashRef} sidebarRef={sidebarRef} />
|
|
956
1008
|
* </Wizard>
|
|
1009
|
+
* ```
|
|
957
1010
|
*/
|
|
958
|
-
declare function Wizard({ children, steps, storageKey }: WizardProps): react_jsx_runtime.JSX.Element;
|
|
1011
|
+
declare function Wizard({ children, steps, storageKey, dismissible, onComplete, onSkip, }: WizardProps): react_jsx_runtime.JSX.Element;
|
|
959
1012
|
|
|
960
1013
|
/** ─────────────────── types ─────────────────── */
|
|
961
1014
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -928,34 +928,87 @@ interface ContextMenuPosition {
|
|
|
928
928
|
}
|
|
929
929
|
|
|
930
930
|
interface WizardStep {
|
|
931
|
-
/** Ref to the DOM element to highlight */
|
|
932
|
-
stepRef: React$1.RefObject<HTMLElement>;
|
|
931
|
+
/** Ref to the DOM element to highlight for this step. */
|
|
932
|
+
stepRef: React$1.RefObject<HTMLElement | null>;
|
|
933
|
+
/** Tooltip body content. */
|
|
933
934
|
description: React$1.ReactNode;
|
|
934
|
-
/**
|
|
935
|
-
|
|
935
|
+
/**
|
|
936
|
+
* Tooltip placement relative to the highlighted element.
|
|
937
|
+
* - `'right'` (default) — to the right of the highlight
|
|
938
|
+
* - `'left'` — to the left of the highlight
|
|
939
|
+
* - `'top'` — above the highlight
|
|
940
|
+
* - `'bottom'` — below the highlight
|
|
941
|
+
*/
|
|
942
|
+
placement?: 'right' | 'left' | 'top' | 'bottom';
|
|
943
|
+
/** Optional heading for the step's tooltip. */
|
|
944
|
+
title?: React$1.ReactNode;
|
|
936
945
|
}
|
|
937
946
|
interface WizardProps {
|
|
947
|
+
/** The wrapped subtree — refs in `steps` point into this tree. */
|
|
938
948
|
children: React$1.ReactNode;
|
|
949
|
+
/** Ordered list of steps to walk the user through. */
|
|
939
950
|
steps: WizardStep[];
|
|
940
|
-
/**
|
|
941
|
-
|
|
951
|
+
/**
|
|
952
|
+
* `localStorage` key used to remember dismissal. When the user clicks
|
|
953
|
+
* "Done" (or "Skip" when dismissible), this key is set so the wizard
|
|
954
|
+
* won't reopen on subsequent mounts.
|
|
955
|
+
*
|
|
956
|
+
* Pass `null` to disable persistence entirely (useful for tests or
|
|
957
|
+
* tours that should always run).
|
|
958
|
+
*
|
|
959
|
+
* Default: `'oxygen.wizard.completed'`.
|
|
960
|
+
*/
|
|
961
|
+
storageKey?: string | null;
|
|
962
|
+
/** Show a "Skip tour" button + Esc-to-dismiss. Default `true`. */
|
|
963
|
+
dismissible?: boolean;
|
|
964
|
+
/** Called when the user reaches the final step's "Done" button. */
|
|
965
|
+
onComplete?: () => void;
|
|
966
|
+
/** Called when the user skips (or presses Esc) before completing. */
|
|
967
|
+
onSkip?: () => void;
|
|
942
968
|
}
|
|
943
969
|
/**
|
|
944
|
-
* Guided-tour overlay
|
|
970
|
+
* Guided-tour overlay that walks the user through a sequence of UI elements.
|
|
971
|
+
*
|
|
972
|
+
* Highlights each step's target element with an outlined "spotlight", shows a
|
|
973
|
+
* tooltip with the description, and provides Prev / Next / Done navigation.
|
|
974
|
+
* The wrapped tree is rendered unchanged; the overlay sits on top of it via a
|
|
975
|
+
* portal so it always covers the real viewport regardless of where Wizard
|
|
976
|
+
* lives in the React tree.
|
|
945
977
|
*
|
|
946
|
-
*
|
|
947
|
-
*
|
|
978
|
+
* **What's improved over the previous version**
|
|
979
|
+
* - `localStorage` access is SSR-safe and try/catch-guarded (Safari private
|
|
980
|
+
* mode, quota exceeded, no-storage browsers all degrade silently).
|
|
981
|
+
* - No more `classList.add(...)` on consumer-owned DOM. The spotlight is a
|
|
982
|
+
* portaled outline rectangle that tracks the target's bbox via
|
|
983
|
+
* `ResizeObserver` + scroll/resize listeners. The consumer's DOM is never
|
|
984
|
+
* mutated, so unmounting Wizard mid-tour leaves no orphan classes.
|
|
985
|
+
* - Focus trap inside the tooltip — Tab and Shift+Tab cycle through the
|
|
986
|
+
* tooltip's buttons. Esc dismisses (when `dismissible`).
|
|
987
|
+
* - Backdrop blocks click-through on the rest of the page so the user can't
|
|
988
|
+
* stumble into unrelated UI mid-tour. The highlighted target itself stays
|
|
989
|
+
* click-blocked too (use the "Next" button to advance).
|
|
990
|
+
* - Position recalculates on scroll / resize / target size changes via
|
|
991
|
+
* `ResizeObserver`.
|
|
992
|
+
* - All `dark-cornflower-blue` / `prussian-blue` / `bg-white` swapped for
|
|
993
|
+
* semantic tokens — both light and dark modes work out of the box.
|
|
948
994
|
*
|
|
949
995
|
* @example
|
|
950
|
-
*
|
|
951
|
-
* const
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
* <Wizard
|
|
955
|
-
*
|
|
996
|
+
* ```tsx
|
|
997
|
+
* const dashRef = useRef<HTMLDivElement>(null)
|
|
998
|
+
* const sidebarRef = useRef<HTMLElement>(null)
|
|
999
|
+
*
|
|
1000
|
+
* <Wizard
|
|
1001
|
+
* steps={[
|
|
1002
|
+
* { stepRef: dashRef, title: 'Dashboard', description: 'Your fleet at a glance.' },
|
|
1003
|
+
* { stepRef: sidebarRef, title: 'Navigation', description: 'Jump between sections here.', placement: 'right' },
|
|
1004
|
+
* ]}
|
|
1005
|
+
* onComplete={() => track('onboarding_complete')}
|
|
1006
|
+
* >
|
|
1007
|
+
* <Layout dashRef={dashRef} sidebarRef={sidebarRef} />
|
|
956
1008
|
* </Wizard>
|
|
1009
|
+
* ```
|
|
957
1010
|
*/
|
|
958
|
-
declare function Wizard({ children, steps, storageKey }: WizardProps): react_jsx_runtime.JSX.Element;
|
|
1011
|
+
declare function Wizard({ children, steps, storageKey, dismissible, onComplete, onSkip, }: WizardProps): react_jsx_runtime.JSX.Element;
|
|
959
1012
|
|
|
960
1013
|
/** ─────────────────── types ─────────────────── */
|
|
961
1014
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { colors_default } from './chunk-GKXP6OJJ.js';
|
|
2
2
|
export { colors_default as COLORS, PALETTE as palette, semanticTokens, vars } from './chunk-GKXP6OJJ.js';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
-
import React8, { createContext, useState, useEffect, useMemo, useContext, useRef, useCallback,
|
|
4
|
+
import React8, { createContext, useState, useEffect, useMemo, useContext, useRef, useId, useCallback, useLayoutEffect } from 'react';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
6
|
import * as Dialog from '@radix-ui/react-dialog';
|
|
7
7
|
import { useReducedMotion, AnimatePresence, motion } from 'framer-motion';
|
|
@@ -1432,70 +1432,208 @@ function ContextMenuLabel({ icon, value }) {
|
|
|
1432
1432
|
function ChevronRight2() {
|
|
1433
1433
|
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4 flex-shrink-0", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
1434
1434
|
}
|
|
1435
|
-
function
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1435
|
+
function readDismissed(key) {
|
|
1436
|
+
if (key === null) return false;
|
|
1437
|
+
if (typeof window === "undefined") return false;
|
|
1438
|
+
try {
|
|
1439
|
+
return window.localStorage.getItem(key) === "true";
|
|
1440
|
+
} catch {
|
|
1441
|
+
return false;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
function writeDismissed(key) {
|
|
1445
|
+
if (key === null) return;
|
|
1446
|
+
if (typeof window === "undefined") return;
|
|
1447
|
+
try {
|
|
1448
|
+
window.localStorage.setItem(key, "true");
|
|
1449
|
+
} catch {
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
function useTargetBbox(ref) {
|
|
1453
|
+
const [bbox, setBbox] = useState(null);
|
|
1454
|
+
useLayoutEffect(() => {
|
|
1455
|
+
const el = ref?.current;
|
|
1456
|
+
if (!el) {
|
|
1457
|
+
setBbox(null);
|
|
1451
1458
|
return;
|
|
1452
1459
|
}
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
const
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1460
|
+
const update = () => setBbox(el.getBoundingClientRect());
|
|
1461
|
+
update();
|
|
1462
|
+
window.addEventListener("scroll", update, true);
|
|
1463
|
+
window.addEventListener("resize", update);
|
|
1464
|
+
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(update) : null;
|
|
1465
|
+
ro?.observe(el);
|
|
1466
|
+
return () => {
|
|
1467
|
+
window.removeEventListener("scroll", update, true);
|
|
1468
|
+
window.removeEventListener("resize", update);
|
|
1469
|
+
ro?.disconnect();
|
|
1470
|
+
};
|
|
1471
|
+
}, [ref]);
|
|
1472
|
+
return bbox;
|
|
1473
|
+
}
|
|
1474
|
+
var TOOLTIP_WIDTH = 280;
|
|
1475
|
+
var TOOLTIP_GAP = 12;
|
|
1476
|
+
function tooltipStyleFor(bbox, placement) {
|
|
1477
|
+
const pl = placement ?? "right";
|
|
1478
|
+
if (pl === "right") return { left: bbox.right + TOOLTIP_GAP, top: bbox.top + bbox.height / 2, transform: "translateY(-50%)", width: TOOLTIP_WIDTH };
|
|
1479
|
+
if (pl === "left") return { left: bbox.left - TOOLTIP_WIDTH - TOOLTIP_GAP, top: bbox.top + bbox.height / 2, transform: "translateY(-50%)", width: TOOLTIP_WIDTH };
|
|
1480
|
+
if (pl === "bottom") return { left: bbox.left + bbox.width / 2, top: bbox.bottom + TOOLTIP_GAP, transform: "translateX(-50%)", width: TOOLTIP_WIDTH };
|
|
1481
|
+
return { left: bbox.left + bbox.width / 2, top: bbox.top - TOOLTIP_GAP, transform: "translate(-50%, -100%)", width: TOOLTIP_WIDTH };
|
|
1482
|
+
}
|
|
1483
|
+
function useFocusTrap(containerRef, active) {
|
|
1484
|
+
useEffect(() => {
|
|
1485
|
+
if (!active) return;
|
|
1486
|
+
const el = containerRef.current;
|
|
1487
|
+
if (!el) return;
|
|
1488
|
+
const t = setTimeout(() => {
|
|
1489
|
+
const first = el.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
1490
|
+
first?.focus();
|
|
1491
|
+
}, 0);
|
|
1492
|
+
const onKey = (e) => {
|
|
1493
|
+
if (e.key !== "Tab") return;
|
|
1494
|
+
const focusables = el.querySelectorAll(
|
|
1495
|
+
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
1496
|
+
);
|
|
1497
|
+
if (focusables.length === 0) return;
|
|
1498
|
+
const first = focusables[0];
|
|
1499
|
+
const last = focusables[focusables.length - 1];
|
|
1500
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
1501
|
+
e.preventDefault();
|
|
1502
|
+
last.focus();
|
|
1503
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
1504
|
+
e.preventDefault();
|
|
1505
|
+
first.focus();
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
document.addEventListener("keydown", onKey);
|
|
1509
|
+
return () => {
|
|
1510
|
+
clearTimeout(t);
|
|
1511
|
+
document.removeEventListener("keydown", onKey);
|
|
1512
|
+
};
|
|
1513
|
+
}, [containerRef, active]);
|
|
1514
|
+
}
|
|
1515
|
+
function Wizard({
|
|
1516
|
+
children,
|
|
1517
|
+
steps,
|
|
1518
|
+
storageKey = "oxygen.wizard.completed",
|
|
1519
|
+
dismissible = true,
|
|
1520
|
+
onComplete,
|
|
1521
|
+
onSkip
|
|
1522
|
+
}) {
|
|
1523
|
+
const tooltipRef = useRef(null);
|
|
1524
|
+
const tooltipTitleId = useId();
|
|
1525
|
+
const tooltipBodyId = useId();
|
|
1526
|
+
const [open, setOpen] = useState(() => steps.length > 0 && !readDismissed(storageKey));
|
|
1527
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
1528
|
+
const step = steps[activeIndex];
|
|
1529
|
+
const bbox = useTargetBbox(step?.stepRef);
|
|
1530
|
+
useFocusTrap(tooltipRef, open);
|
|
1531
|
+
useEffect(() => {
|
|
1532
|
+
if (!open || !dismissible) return;
|
|
1533
|
+
const onKey = (e) => {
|
|
1534
|
+
if (e.key === "Escape") {
|
|
1535
|
+
e.preventDefault();
|
|
1536
|
+
handleSkip();
|
|
1496
1537
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1538
|
+
};
|
|
1539
|
+
document.addEventListener("keydown", onKey);
|
|
1540
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
1541
|
+
}, [open, dismissible]);
|
|
1542
|
+
const handleSkip = useCallback(() => {
|
|
1543
|
+
writeDismissed(storageKey);
|
|
1544
|
+
setOpen(false);
|
|
1545
|
+
onSkip?.();
|
|
1546
|
+
}, [storageKey, onSkip]);
|
|
1547
|
+
const handleComplete = useCallback(() => {
|
|
1548
|
+
writeDismissed(storageKey);
|
|
1549
|
+
setOpen(false);
|
|
1550
|
+
onComplete?.();
|
|
1551
|
+
}, [storageKey, onComplete]);
|
|
1552
|
+
const handleNext = () => {
|
|
1553
|
+
if (activeIndex < steps.length - 1) setActiveIndex((i) => i + 1);
|
|
1554
|
+
else handleComplete();
|
|
1555
|
+
};
|
|
1556
|
+
const handlePrev = () => {
|
|
1557
|
+
if (activeIndex > 0) setActiveIndex((i) => i - 1);
|
|
1558
|
+
};
|
|
1559
|
+
const highlightStyle = bbox ? {
|
|
1560
|
+
left: bbox.left - 4,
|
|
1561
|
+
top: bbox.top - 4,
|
|
1562
|
+
width: bbox.width + 8,
|
|
1563
|
+
height: bbox.height + 8
|
|
1564
|
+
} : { display: "none" };
|
|
1565
|
+
const tooltipStyle = bbox ? tooltipStyleFor(bbox, step?.placement) : { display: "none" };
|
|
1566
|
+
const isLast = activeIndex === steps.length - 1;
|
|
1567
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1568
|
+
children,
|
|
1569
|
+
open && step && /* @__PURE__ */ jsxs(Portal, { children: [
|
|
1570
|
+
/* @__PURE__ */ jsx(
|
|
1571
|
+
"div",
|
|
1572
|
+
{
|
|
1573
|
+
className: "fixed inset-0 z-[7000000] bg-foreground/40 backdrop-blur-[1px] pointer-events-auto",
|
|
1574
|
+
"aria-hidden": "true"
|
|
1575
|
+
}
|
|
1576
|
+
),
|
|
1577
|
+
/* @__PURE__ */ jsx(
|
|
1578
|
+
"div",
|
|
1579
|
+
{
|
|
1580
|
+
className: "fixed z-[7000001] pointer-events-none rounded-md ring-2 ring-accent ring-offset-2 ring-offset-background transition-all duration-200",
|
|
1581
|
+
style: highlightStyle,
|
|
1582
|
+
"aria-hidden": "true"
|
|
1583
|
+
}
|
|
1584
|
+
),
|
|
1585
|
+
/* @__PURE__ */ jsxs(
|
|
1586
|
+
"div",
|
|
1587
|
+
{
|
|
1588
|
+
ref: tooltipRef,
|
|
1589
|
+
role: "dialog",
|
|
1590
|
+
"aria-modal": "true",
|
|
1591
|
+
"aria-labelledby": step.title ? tooltipTitleId : void 0,
|
|
1592
|
+
"aria-describedby": tooltipBodyId,
|
|
1593
|
+
className: "fixed z-[7000002] rounded-lg bg-surface text-foreground border border-border shadow-xl p-4 pointer-events-auto",
|
|
1594
|
+
style: tooltipStyle,
|
|
1595
|
+
children: [
|
|
1596
|
+
step.title && /* @__PURE__ */ jsx("h3", { id: tooltipTitleId, className: "text-sm font-semibold text-foreground mb-1", children: step.title }),
|
|
1597
|
+
/* @__PURE__ */ jsx("div", { id: tooltipBodyId, className: "text-sm text-foreground-secondary leading-relaxed", children: step.description }),
|
|
1598
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center justify-between", children: [
|
|
1599
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-foreground-muted tabular-nums", children: [
|
|
1600
|
+
activeIndex + 1,
|
|
1601
|
+
" / ",
|
|
1602
|
+
steps.length
|
|
1603
|
+
] }),
|
|
1604
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1605
|
+
dismissible && !isLast && /* @__PURE__ */ jsx(
|
|
1606
|
+
Button,
|
|
1607
|
+
{
|
|
1608
|
+
variant: "ghost",
|
|
1609
|
+
size: "sm",
|
|
1610
|
+
content: "Skip",
|
|
1611
|
+
onClick: handleSkip
|
|
1612
|
+
}
|
|
1613
|
+
),
|
|
1614
|
+
activeIndex > 0 && /* @__PURE__ */ jsx(
|
|
1615
|
+
Button,
|
|
1616
|
+
{
|
|
1617
|
+
variant: "secondary",
|
|
1618
|
+
size: "sm",
|
|
1619
|
+
content: "Back",
|
|
1620
|
+
onClick: handlePrev
|
|
1621
|
+
}
|
|
1622
|
+
),
|
|
1623
|
+
/* @__PURE__ */ jsx(
|
|
1624
|
+
Button,
|
|
1625
|
+
{
|
|
1626
|
+
size: "sm",
|
|
1627
|
+
content: isLast ? "Done" : "Next",
|
|
1628
|
+
onClick: handleNext
|
|
1629
|
+
}
|
|
1630
|
+
)
|
|
1631
|
+
] })
|
|
1632
|
+
] })
|
|
1633
|
+
]
|
|
1634
|
+
}
|
|
1635
|
+
)
|
|
1636
|
+
] })
|
|
1499
1637
|
] });
|
|
1500
1638
|
}
|
|
1501
1639
|
var SearchInput = React8.forwardRef(function SearchInput2({
|