@homebound/beam 3.70.0 → 3.72.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 +70 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -21
- package/dist/index.d.ts +23 -21
- package/dist/index.js +68 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -9944,7 +9944,26 @@ type StepperTabProps = {
|
|
|
9944
9944
|
};
|
|
9945
9945
|
declare function StepperTab(props: StepperTabProps): JSX.Element;
|
|
9946
9946
|
|
|
9947
|
-
type
|
|
9947
|
+
type WorkflowActionsProps = {
|
|
9948
|
+
isFirstStep: boolean;
|
|
9949
|
+
isLastStep: boolean;
|
|
9950
|
+
isMobile: boolean;
|
|
9951
|
+
onBack: () => void;
|
|
9952
|
+
/** Leaves the workflow without saving. Always shown. */
|
|
9953
|
+
onCancel: (e: PressEvent) => void;
|
|
9954
|
+
/** Saves partial progress and exits. Used whenever canExitEarly is true. */
|
|
9955
|
+
onSaveAndExit?: (e: PressEvent) => void | Promise<void>;
|
|
9956
|
+
/** Label for the completion button shown on the last step. */
|
|
9957
|
+
completeLabel: "Create" | "Save";
|
|
9958
|
+
/** Called when the completion button is clicked. Only shown on the last step. */
|
|
9959
|
+
onComplete: (e: PressEvent) => void | Promise<void>;
|
|
9960
|
+
/** Runs before continuing to the next step. Return `false` to stay put, like when the save failed. */
|
|
9961
|
+
onContinue: () => boolean | void | Promise<boolean | void>;
|
|
9962
|
+
/** Disables whichever of Continue/Complete is currently shown, e.g. while the active step is invalid. */
|
|
9963
|
+
primaryDisabled?: boolean;
|
|
9964
|
+
};
|
|
9965
|
+
|
|
9966
|
+
type StepperTabsStep = Pick<StepperTabProps, "label" | "value" | "completed" | "disabled"> & Partial<Pick<WorkflowActionsProps, "onContinue">>;
|
|
9948
9967
|
type StepperTabsProps = {
|
|
9949
9968
|
steps: StepperTabsStep[];
|
|
9950
9969
|
currentStep: StepperTabsStep["value"];
|
|
@@ -10237,24 +10256,6 @@ type PageHeaderLayoutProps<V extends string, X> = {
|
|
|
10237
10256
|
/** Page header + body shell with auto-hide chrome. Contract: `docs/layouts.md`. */
|
|
10238
10257
|
declare function PageHeaderLayout<V extends string, X extends Only<TabsContentXss, X>>(props: PageHeaderLayoutProps<V, X>): JSX.Element;
|
|
10239
10258
|
|
|
10240
|
-
type WorkflowActionsProps = {
|
|
10241
|
-
isFirstStep: boolean;
|
|
10242
|
-
isLastStep: boolean;
|
|
10243
|
-
isMobile: boolean;
|
|
10244
|
-
onBack: () => void;
|
|
10245
|
-
/** Leaves the workflow without saving. Always shown. */
|
|
10246
|
-
onCancel: (e: PressEvent) => void;
|
|
10247
|
-
/** Saves partial progress and exits. Used whenever canExitEarly is true. */
|
|
10248
|
-
onSaveAndExit?: (e: PressEvent) => void | Promise<void>;
|
|
10249
|
-
/** Label for the completion button shown on the last step. */
|
|
10250
|
-
completeLabel: "Create" | "Save";
|
|
10251
|
-
/** Called when the completion button is clicked. Only shown on the last step. */
|
|
10252
|
-
onComplete: (e: PressEvent) => void | Promise<void>;
|
|
10253
|
-
onContinue: () => void;
|
|
10254
|
-
/** Disables whichever of Continue/Complete is currently shown, e.g. while the active step is invalid. */
|
|
10255
|
-
primaryDisabled?: boolean;
|
|
10256
|
-
};
|
|
10257
|
-
|
|
10258
10259
|
/** A `WorkflowLayout` step: a `StepperTabsStep` (minus `value`, which is derived from `label`) plus the page content rendered while it's active — `completed` also gates the Continue/Complete CTA when this is the active step. */
|
|
10259
10260
|
type WorkflowLayoutStep = Omit<StepperTabsStep, "value"> & {
|
|
10260
10261
|
/** Rendered as the page body while this is the active step. */
|
|
@@ -10273,8 +10274,9 @@ type WorkflowLayoutProps = Pick<BaseHeaderProps, "title" | "documentTitleSuffix"
|
|
|
10273
10274
|
*
|
|
10274
10275
|
* A standalone, full-page layout for step-based workflow pages — nest it directly under
|
|
10275
10276
|
* `EnvironmentBannerLayout`, never under `NavbarLayout`/`SideNavLayout`/`PageHeaderLayout`. Unlike
|
|
10276
|
-
* `PageHeaderLayout`, the header here never auto-hides
|
|
10277
|
-
* indicator bar on mobile
|
|
10277
|
+
* `PageHeaderLayout`, the header here never auto-hides. The stepper tabs always collapse to a condensed
|
|
10278
|
+
* indicator bar on mobile, and on larger viewports also collapse once scrolled past a threshold,
|
|
10279
|
+
* re-expanding on scroll-up (even before reaching the top).
|
|
10278
10280
|
*
|
|
10279
10281
|
* Owns the workflow's fixed CTA set (Back/Cancel/Save & Exit/Continue-or-Complete) via `WorkflowActions`
|
|
10280
10282
|
* so it can move them into a mobile footer at the `sm` breakpoint — `WorkflowHeader` itself is not part
|
package/dist/index.d.ts
CHANGED
|
@@ -9944,7 +9944,26 @@ type StepperTabProps = {
|
|
|
9944
9944
|
};
|
|
9945
9945
|
declare function StepperTab(props: StepperTabProps): JSX.Element;
|
|
9946
9946
|
|
|
9947
|
-
type
|
|
9947
|
+
type WorkflowActionsProps = {
|
|
9948
|
+
isFirstStep: boolean;
|
|
9949
|
+
isLastStep: boolean;
|
|
9950
|
+
isMobile: boolean;
|
|
9951
|
+
onBack: () => void;
|
|
9952
|
+
/** Leaves the workflow without saving. Always shown. */
|
|
9953
|
+
onCancel: (e: PressEvent) => void;
|
|
9954
|
+
/** Saves partial progress and exits. Used whenever canExitEarly is true. */
|
|
9955
|
+
onSaveAndExit?: (e: PressEvent) => void | Promise<void>;
|
|
9956
|
+
/** Label for the completion button shown on the last step. */
|
|
9957
|
+
completeLabel: "Create" | "Save";
|
|
9958
|
+
/** Called when the completion button is clicked. Only shown on the last step. */
|
|
9959
|
+
onComplete: (e: PressEvent) => void | Promise<void>;
|
|
9960
|
+
/** Runs before continuing to the next step. Return `false` to stay put, like when the save failed. */
|
|
9961
|
+
onContinue: () => boolean | void | Promise<boolean | void>;
|
|
9962
|
+
/** Disables whichever of Continue/Complete is currently shown, e.g. while the active step is invalid. */
|
|
9963
|
+
primaryDisabled?: boolean;
|
|
9964
|
+
};
|
|
9965
|
+
|
|
9966
|
+
type StepperTabsStep = Pick<StepperTabProps, "label" | "value" | "completed" | "disabled"> & Partial<Pick<WorkflowActionsProps, "onContinue">>;
|
|
9948
9967
|
type StepperTabsProps = {
|
|
9949
9968
|
steps: StepperTabsStep[];
|
|
9950
9969
|
currentStep: StepperTabsStep["value"];
|
|
@@ -10237,24 +10256,6 @@ type PageHeaderLayoutProps<V extends string, X> = {
|
|
|
10237
10256
|
/** Page header + body shell with auto-hide chrome. Contract: `docs/layouts.md`. */
|
|
10238
10257
|
declare function PageHeaderLayout<V extends string, X extends Only<TabsContentXss, X>>(props: PageHeaderLayoutProps<V, X>): JSX.Element;
|
|
10239
10258
|
|
|
10240
|
-
type WorkflowActionsProps = {
|
|
10241
|
-
isFirstStep: boolean;
|
|
10242
|
-
isLastStep: boolean;
|
|
10243
|
-
isMobile: boolean;
|
|
10244
|
-
onBack: () => void;
|
|
10245
|
-
/** Leaves the workflow without saving. Always shown. */
|
|
10246
|
-
onCancel: (e: PressEvent) => void;
|
|
10247
|
-
/** Saves partial progress and exits. Used whenever canExitEarly is true. */
|
|
10248
|
-
onSaveAndExit?: (e: PressEvent) => void | Promise<void>;
|
|
10249
|
-
/** Label for the completion button shown on the last step. */
|
|
10250
|
-
completeLabel: "Create" | "Save";
|
|
10251
|
-
/** Called when the completion button is clicked. Only shown on the last step. */
|
|
10252
|
-
onComplete: (e: PressEvent) => void | Promise<void>;
|
|
10253
|
-
onContinue: () => void;
|
|
10254
|
-
/** Disables whichever of Continue/Complete is currently shown, e.g. while the active step is invalid. */
|
|
10255
|
-
primaryDisabled?: boolean;
|
|
10256
|
-
};
|
|
10257
|
-
|
|
10258
10259
|
/** A `WorkflowLayout` step: a `StepperTabsStep` (minus `value`, which is derived from `label`) plus the page content rendered while it's active — `completed` also gates the Continue/Complete CTA when this is the active step. */
|
|
10259
10260
|
type WorkflowLayoutStep = Omit<StepperTabsStep, "value"> & {
|
|
10260
10261
|
/** Rendered as the page body while this is the active step. */
|
|
@@ -10273,8 +10274,9 @@ type WorkflowLayoutProps = Pick<BaseHeaderProps, "title" | "documentTitleSuffix"
|
|
|
10273
10274
|
*
|
|
10274
10275
|
* A standalone, full-page layout for step-based workflow pages — nest it directly under
|
|
10275
10276
|
* `EnvironmentBannerLayout`, never under `NavbarLayout`/`SideNavLayout`/`PageHeaderLayout`. Unlike
|
|
10276
|
-
* `PageHeaderLayout`, the header here never auto-hides
|
|
10277
|
-
* indicator bar on mobile
|
|
10277
|
+
* `PageHeaderLayout`, the header here never auto-hides. The stepper tabs always collapse to a condensed
|
|
10278
|
+
* indicator bar on mobile, and on larger viewports also collapse once scrolled past a threshold,
|
|
10279
|
+
* re-expanding on scroll-up (even before reaching the top).
|
|
10278
10280
|
*
|
|
10279
10281
|
* Owns the workflow's fixed CTA set (Back/Cancel/Save & Exit/Continue-or-Complete) via `WorkflowActions`
|
|
10280
10282
|
* so it can move them into a mobile footer at the `sm` breakpoint — `WorkflowHeader` itself is not part
|
package/dist/index.js
CHANGED
|
@@ -26469,7 +26469,7 @@ function PageHeaderLayout(props) {
|
|
|
26469
26469
|
}
|
|
26470
26470
|
|
|
26471
26471
|
// src/layouts/WorkflowLayout/WorkflowLayout.tsx
|
|
26472
|
-
import { useLayoutEffect as
|
|
26472
|
+
import { useLayoutEffect as useLayoutEffect11, useRef as useRef69, useState as useState60 } from "react";
|
|
26473
26473
|
|
|
26474
26474
|
// src/components/Headers/WorkflowHeader.tsx
|
|
26475
26475
|
import { jsx as jsx215 } from "react/jsx-runtime";
|
|
@@ -26479,14 +26479,65 @@ function WorkflowHeader(props) {
|
|
|
26479
26479
|
return /* @__PURE__ */ jsx215(BaseHeader, { ...otherProps, bottomSlot: /* @__PURE__ */ jsx215(StepperTabs, { ...stepperTabs, ...tid.stepperTabs }) });
|
|
26480
26480
|
}
|
|
26481
26481
|
|
|
26482
|
+
// src/layouts/useScrollCollapse.ts
|
|
26483
|
+
import { useLayoutEffect as useLayoutEffect10, useRef as useRef67, useState as useState59 } from "react";
|
|
26484
|
+
function useScrollCollapse(enabled, restingOffset) {
|
|
26485
|
+
const [collapsed, setCollapsed] = useState59(() => typeof window !== "undefined" && window.scrollY > 0);
|
|
26486
|
+
const collapsedRef = useRef67(collapsed);
|
|
26487
|
+
collapsedRef.current = collapsed;
|
|
26488
|
+
const restingOffsetRef = useRef67(restingOffset);
|
|
26489
|
+
restingOffsetRef.current = restingOffset;
|
|
26490
|
+
const lastScrollYRef = useRef67(Number.POSITIVE_INFINITY);
|
|
26491
|
+
const lastScrollHeightRef = useRef67(0);
|
|
26492
|
+
useLayoutEffect10(() => {
|
|
26493
|
+
lastScrollHeightRef.current = document.documentElement.scrollHeight;
|
|
26494
|
+
}, [restingOffset]);
|
|
26495
|
+
useLayoutEffect10(() => {
|
|
26496
|
+
if (!enabled) return;
|
|
26497
|
+
const commit = (next) => {
|
|
26498
|
+
if (next !== collapsedRef.current) {
|
|
26499
|
+
collapsedRef.current = next;
|
|
26500
|
+
setCollapsed(next);
|
|
26501
|
+
}
|
|
26502
|
+
};
|
|
26503
|
+
const updateCollapsed = () => {
|
|
26504
|
+
const doc = document.documentElement;
|
|
26505
|
+
const currentY = window.scrollY;
|
|
26506
|
+
if (currentY <= 0) {
|
|
26507
|
+
lastScrollYRef.current = 0;
|
|
26508
|
+
lastScrollHeightRef.current = doc.scrollHeight;
|
|
26509
|
+
commit(false);
|
|
26510
|
+
return;
|
|
26511
|
+
}
|
|
26512
|
+
const currentScrollHeight = doc.scrollHeight;
|
|
26513
|
+
const scrollHeightChanged = lastScrollHeightRef.current !== 0 && currentScrollHeight !== lastScrollHeightRef.current;
|
|
26514
|
+
const dy = currentY - lastScrollYRef.current;
|
|
26515
|
+
lastScrollYRef.current = currentY;
|
|
26516
|
+
lastScrollHeightRef.current = currentScrollHeight;
|
|
26517
|
+
if (currentY <= restingOffsetRef.current) return;
|
|
26518
|
+
if (scrollHeightChanged) {
|
|
26519
|
+
commit(true);
|
|
26520
|
+
return;
|
|
26521
|
+
}
|
|
26522
|
+
const atBottom = currentY >= doc.scrollHeight - doc.clientHeight;
|
|
26523
|
+
if (dy > 0) commit(true);
|
|
26524
|
+
else if (dy < 0 && !atBottom) commit(false);
|
|
26525
|
+
};
|
|
26526
|
+
updateCollapsed();
|
|
26527
|
+
window.addEventListener("scroll", updateCollapsed, { passive: true });
|
|
26528
|
+
return () => window.removeEventListener("scroll", updateCollapsed);
|
|
26529
|
+
}, [enabled]);
|
|
26530
|
+
return collapsed;
|
|
26531
|
+
}
|
|
26532
|
+
|
|
26482
26533
|
// src/layouts/WorkflowLayout/useUnsavedChangesGuard.tsx
|
|
26483
|
-
import { useEffect as useEffect38, useRef as
|
|
26534
|
+
import { useEffect as useEffect38, useRef as useRef68 } from "react";
|
|
26484
26535
|
import { useBlocker } from "react-router-dom";
|
|
26485
26536
|
import { jsx as jsx216 } from "react/jsx-runtime";
|
|
26486
26537
|
function useUnsavedChangesGuard(options) {
|
|
26487
26538
|
const { isDirty, onCancel } = options;
|
|
26488
26539
|
const { openModal } = useModal();
|
|
26489
|
-
const isDirtyRef =
|
|
26540
|
+
const isDirtyRef = useRef68(isDirty);
|
|
26490
26541
|
isDirtyRef.current = isDirty;
|
|
26491
26542
|
useEffect38(() => {
|
|
26492
26543
|
const onBeforeUnload = (e) => {
|
|
@@ -26570,7 +26621,7 @@ function WorkflowLayout(props) {
|
|
|
26570
26621
|
...step,
|
|
26571
26622
|
value: defaultTestId(step.label)
|
|
26572
26623
|
}));
|
|
26573
|
-
const [currentStep, setCurrentStep] =
|
|
26624
|
+
const [currentStep, setCurrentStep] = useState60(() => getInitialStep(tabSteps, defaultStep));
|
|
26574
26625
|
const tid = useTestIds(props, "workflowLayout");
|
|
26575
26626
|
const {
|
|
26576
26627
|
sm: isMobile
|
|
@@ -26582,9 +26633,11 @@ function WorkflowLayout(props) {
|
|
|
26582
26633
|
isDirty,
|
|
26583
26634
|
onCancel
|
|
26584
26635
|
});
|
|
26585
|
-
const
|
|
26636
|
+
const bannerAndNavbarHeight = useBannerAndNavbarHeight();
|
|
26637
|
+
const headerMetricsRef = useRef69(null);
|
|
26586
26638
|
const headerHeight = useMeasuredHeight(headerMetricsRef, true);
|
|
26587
|
-
const
|
|
26639
|
+
const scrollCollapsed = useScrollCollapse(!isMobile, bannerAndNavbarHeight + headerHeight);
|
|
26640
|
+
const collapsed = isMobile || scrollCollapsed;
|
|
26588
26641
|
const headerWidth = documentScrollChromeWidth();
|
|
26589
26642
|
const outerTop = bannerAndNavbarChromeTop();
|
|
26590
26643
|
const cssVars = headerHeight > 0 ? {
|
|
@@ -26594,9 +26647,16 @@ function WorkflowLayout(props) {
|
|
|
26594
26647
|
const isFirstStep = currentIndex <= 0;
|
|
26595
26648
|
const isLastStep = currentIndex === tabSteps.length - 1;
|
|
26596
26649
|
const activeStep = tabSteps[currentIndex];
|
|
26597
|
-
const buttons = /* @__PURE__ */ jsx218(WorkflowActions, { isFirstStep, isLastStep, isMobile, onBack: () => setCurrentStep(tabSteps[currentIndex - 1].value), onCancel: onCancelClick, onSaveAndExit, completeLabel, onComplete, primaryDisabled: !activeStep?.completed, onContinue: () =>
|
|
26650
|
+
const buttons = /* @__PURE__ */ jsx218(WorkflowActions, { isFirstStep, isLastStep, isMobile, onBack: () => setCurrentStep(tabSteps[currentIndex - 1].value), onCancel: onCancelClick, onSaveAndExit, completeLabel, onComplete, primaryDisabled: !activeStep?.completed, onContinue: async () => {
|
|
26651
|
+
const onContinue = activeStep?.onContinue;
|
|
26652
|
+
if (onContinue) {
|
|
26653
|
+
const allowed = await onContinue();
|
|
26654
|
+
if (allowed === false) return;
|
|
26655
|
+
}
|
|
26656
|
+
setCurrentStep(tabSteps[currentIndex + 1].value);
|
|
26657
|
+
} });
|
|
26598
26658
|
const showFooter = isMobile;
|
|
26599
|
-
|
|
26659
|
+
useLayoutEffect11(() => {
|
|
26600
26660
|
const root = document.documentElement;
|
|
26601
26661
|
const previous = root.style.getPropertyValue(beamWorkflowLayoutFooterHeightVar);
|
|
26602
26662
|
root.style.setProperty(beamWorkflowLayoutFooterHeightVar, showFooter ? `${mobileFooterHeightPx}px` : "0px");
|