h2o-library 2.0.1 → 2.1.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.
- package/CHANGELOG.md +32 -0
- package/dist/charts/components/index.d.ts +1 -0
- package/dist/charts/components/src/button/button.d.ts +3 -0
- package/dist/charts/components/src/chip/chip.d.ts +4 -1
- package/dist/charts/components/src/content-switch/content-switch.d.ts +3 -0
- package/dist/charts/components/src/input/input.d.ts +5 -0
- package/dist/charts/components/src/onboarding/onboarding.d.ts +100 -0
- package/dist/charts/components/src/radio-group/radio-group.d.ts +3 -0
- package/dist/charts/hooks/index.d.ts +1 -0
- package/dist/charts/hooks/src/use-click-outside.d.ts +1 -1
- package/dist/charts/hooks/src/use-dropdown-position.d.ts +1 -1
- package/dist/charts/hooks/src/use-onboarding.d.ts +66 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/src/button/button.d.ts +3 -0
- package/dist/components/src/chip/chip.d.ts +4 -1
- package/dist/components/src/content-switch/content-switch.d.ts +3 -0
- package/dist/components/src/input/input.d.ts +5 -0
- package/dist/components/src/onboarding/onboarding.d.ts +100 -0
- package/dist/components/src/radio-group/radio-group.d.ts +3 -0
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/src/button/button.d.ts +3 -0
- package/dist/esm/components/src/button/button.js +1 -1
- package/dist/esm/components/src/chip/chip.d.ts +4 -1
- package/dist/esm/components/src/chip/chip.js +1 -1
- package/dist/esm/components/src/content-switch/content-switch.d.ts +3 -0
- package/dist/esm/components/src/content-switch/content-switch.js +1 -1
- package/dist/esm/components/src/input/input.d.ts +5 -0
- package/dist/esm/components/src/input/input.js +1 -1
- package/dist/esm/components/src/modal/modal.js +1 -1
- package/dist/esm/components/src/onboarding/onboarding.css +178 -0
- package/dist/esm/components/src/onboarding/onboarding.d.ts +100 -0
- package/dist/esm/components/src/onboarding/onboarding.js +2 -0
- package/dist/esm/components/src/radio-group/radio-group.d.ts +3 -0
- package/dist/esm/components/src/radio-group/radio-group.js +1 -1
- package/dist/esm/hooks/index.d.ts +1 -0
- package/dist/esm/hooks/src/use-click-outside.d.ts +1 -1
- package/dist/esm/hooks/src/use-dropdown-position.d.ts +1 -1
- package/dist/esm/hooks/src/use-onboarding.d.ts +66 -0
- package/dist/esm/hooks/src/use-onboarding.js +2 -0
- package/dist/esm/index.js +1 -1
- package/dist/hooks/components/index.d.ts +1 -0
- package/dist/hooks/components/src/button/button.d.ts +3 -0
- package/dist/hooks/components/src/chip/chip.d.ts +4 -1
- package/dist/hooks/components/src/content-switch/content-switch.d.ts +3 -0
- package/dist/hooks/components/src/input/input.d.ts +5 -0
- package/dist/hooks/components/src/onboarding/onboarding.d.ts +100 -0
- package/dist/hooks/components/src/radio-group/radio-group.d.ts +3 -0
- package/dist/hooks/hooks/index.d.ts +1 -0
- package/dist/hooks/hooks/src/use-click-outside.d.ts +1 -1
- package/dist/hooks/hooks/src/use-dropdown-position.d.ts +1 -1
- package/dist/hooks/hooks/src/use-onboarding.d.ts +66 -0
- package/dist/hooks/index.cjs.js +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/src/use-click-outside.d.ts +1 -1
- package/dist/hooks/src/use-dropdown-position.d.ts +1 -1
- package/dist/hooks/src/use-onboarding.d.ts +66 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.css +1 -1
- package/dist/types/components/index.d.ts +1 -0
- package/dist/types/components/src/button/button.d.ts +3 -0
- package/dist/types/components/src/chip/chip.d.ts +4 -1
- package/dist/types/components/src/content-switch/content-switch.d.ts +3 -0
- package/dist/types/components/src/input/input.d.ts +5 -0
- package/dist/types/components/src/onboarding/onboarding.d.ts +100 -0
- package/dist/types/components/src/radio-group/radio-group.d.ts +3 -0
- package/dist/types/hooks/index.d.ts +1 -0
- package/dist/types/hooks/src/use-click-outside.d.ts +1 -1
- package/dist/types/hooks/src/use-dropdown-position.d.ts +1 -1
- package/dist/types/hooks/src/use-onboarding.d.ts +66 -0
- package/dist/utils/components/index.d.ts +1 -0
- package/dist/utils/components/src/button/button.d.ts +3 -0
- package/dist/utils/components/src/chip/chip.d.ts +4 -1
- package/dist/utils/components/src/content-switch/content-switch.d.ts +3 -0
- package/dist/utils/components/src/input/input.d.ts +5 -0
- package/dist/utils/components/src/onboarding/onboarding.d.ts +100 -0
- package/dist/utils/components/src/radio-group/radio-group.d.ts +3 -0
- package/dist/utils/hooks/index.d.ts +1 -0
- package/dist/utils/hooks/src/use-click-outside.d.ts +1 -1
- package/dist/utils/hooks/src/use-dropdown-position.d.ts +1 -1
- package/dist/utils/hooks/src/use-onboarding.d.ts +66 -0
- package/package.json +8 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./chip.css";
|
|
2
2
|
import type React from "react";
|
|
3
|
+
import { Icon } from "../icon/icon";
|
|
3
4
|
import type { IconType, TooltipPosition } from "../../../types";
|
|
4
5
|
/**
|
|
5
6
|
* Props for the `Chip` component: extends `HTMLAttributes<HTMLElement>` with H2O badge fields.
|
|
@@ -7,6 +8,7 @@ import type { IconType, TooltipPosition } from "../../../types";
|
|
|
7
8
|
*
|
|
8
9
|
* @property {string} label - Visible text (required).
|
|
9
10
|
* @property {import("../../../types").IconType} [icon] - Leading icon; overrides `dot` when present.
|
|
11
|
+
* @property {Omit<React.ComponentProps<typeof Icon>, "icon">} [iconProps] - Extra props forwarded to the built-in `Icon` (and its `<svg>`); overrides `iconSize`/`iconStrokeWidth`/`iconClassName`.
|
|
10
12
|
* @property {"neutral"|"primary"|"success"|"warning"|"error"} [variant] - Color family. Default `"neutral"`.
|
|
11
13
|
* @property {"sm"|"md"|"lg"} [size] - Padding and font. Default `"md"`.
|
|
12
14
|
* @property {boolean} [dot] - Renders a status dot instead of an icon when no `icon`.
|
|
@@ -16,6 +18,7 @@ import type { IconType, TooltipPosition } from "../../../types";
|
|
|
16
18
|
type ChipProps = React.HTMLAttributes<HTMLElement> & {
|
|
17
19
|
label: string;
|
|
18
20
|
icon?: IconType;
|
|
21
|
+
iconProps?: Omit<React.ComponentProps<typeof Icon>, "icon">;
|
|
19
22
|
variant?: "neutral" | "primary" | "success" | "warning" | "error";
|
|
20
23
|
size?: "sm" | "md" | "lg";
|
|
21
24
|
iconStrokeWidth?: number;
|
|
@@ -33,5 +36,5 @@ type ChipProps = React.HTMLAttributes<HTMLElement> & {
|
|
|
33
36
|
* @param {ChipProps} props - Extends native element props; includes `label` and presentation.
|
|
34
37
|
* @returns {JSX.Element} A `span` or `button`-like chip per `onClick` usage.
|
|
35
38
|
*/
|
|
36
|
-
export declare const Chip: ({ label, icon, variant, size, iconStrokeWidth, iconClassName, iconSize, disabled, showBorder, dot, tooltip, tooltipPosition, className, onClick, ...props }: ChipProps) => import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
export declare const Chip: ({ label, icon, iconProps, variant, size, iconStrokeWidth, iconClassName, iconSize, disabled, showBorder, dot, tooltip, tooltipPosition, className, onClick, ...props }: ChipProps) => import("react/jsx-runtime").JSX.Element;
|
|
37
40
|
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{__rest as
|
|
1
|
+
import{__rest as o}from"../../../node_modules/tslib/tslib.es6.mjs.js";import{jsxs as i,jsx as s}from"react/jsx-runtime";import"./chip.css";import{Icon as t}from"../icon/icon.js";import{Tooltip as e}from"../tooltip/tooltip.js";import{cn as c}from"../../../utils/src/cn.js";const n=n=>{var{label:a,icon:l,iconProps:r,variant:d="neutral",size:p="md",iconStrokeWidth:m=1.5,iconClassName:h="",iconSize:b=14,disabled:v=!1,showBorder:j=!1,dot:N=!1,tooltip:k,tooltipPosition:u="top",className:f="",onClick:z}=n,C=o(n,["label","icon","iconProps","variant","size","iconStrokeWidth","iconClassName","iconSize","disabled","showBorder","dot","tooltip","tooltipPosition","className","onClick"]);const x=!!z,P=N&&!l,S=["chip-base",`chip-${d}`,`chip--${p}`,j?"chip--border":"",P?"chip--dot":"",l?"chip--icon":"",v?"chip--disabled":"",f].filter(Boolean).join(" "),w=i("div",Object.assign({className:S,role:x?"button":void 0,tabIndex:x&&!v?0:void 0,"aria-disabled":v||void 0,onClick:v?void 0:z,onKeyDown:x&&!v?o=>{"Enter"!==o.key&&" "!==o.key||!z||(o.preventDefault(),z(o))}:void 0},C,{children:[P&&s("span",{className:"chip-dot"}),l&&s(t,Object.assign({icon:l,size:b,strokeWidth:m},r,{className:c(h,null==r?void 0:r.className)})),s("span",{className:"chip-label",children:a})]}));return k?s(e,{text:k,position:u,children:w}):w};export{n as Chip};
|
|
2
2
|
//# sourceMappingURL=chip.js.map
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import "./content-switch.css";
|
|
2
|
+
import { Icon } from "../icon/icon";
|
|
2
3
|
import type { IconType } from "../../../types";
|
|
3
4
|
/**
|
|
4
5
|
* Tab-like option in a `ContentSwitch` row (`role="tablist"` / `role="tab"`).
|
|
5
6
|
* @property {string} value - Passed to `onChange` when selected.
|
|
6
7
|
* @property {IconType} [icon] - Optional leading icon on the tab.
|
|
8
|
+
* @property {Omit<React.ComponentProps<typeof Icon>, "icon">} [iconProps] - Extra props forwarded to the built-in `Icon` (and its `<svg>`).
|
|
7
9
|
*/
|
|
8
10
|
export type ContentSwitchOption = {
|
|
9
11
|
label: string;
|
|
10
12
|
value: string;
|
|
11
13
|
icon?: IconType;
|
|
14
|
+
iconProps?: Omit<React.ComponentProps<typeof Icon>, "icon">;
|
|
12
15
|
disabled?: boolean;
|
|
13
16
|
};
|
|
14
17
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as t,jsxs as
|
|
1
|
+
import{jsx as t,jsxs as o}from"react/jsx-runtime";import"./content-switch.css";import{Icon as i}from"../icon/icon.js";import{cn as e}from"../../../utils/src/cn.js";const n=({options:n,selected:s,onChange:a,variant:c="primary",className:l,optionClassName:r,disabled:m=!1,ariaLabel:p})=>t("div",{role:"tablist","aria-label":p,className:e("primary"===c?"content-switch-primary":"content-switch-secondary",l),children:n.map((n=>{var c;const l=m||n.disabled;return o("button",{type:"button",role:"tab","aria-selected":s===n.value,onClick:()=>!l&&a(n.value),disabled:l,className:e("content-switch-option",r,s===n.value&&"content-switch-option-selected",l&&"content-switch-option-disabled"),children:[n.icon&&t(i,Object.assign({icon:n.icon,size:16},n.iconProps,{className:e("content-switch-option-icon",null===(c=n.iconProps)||void 0===c?void 0:c.className)})),n.label]},n.value)}))});export{n as ContentSwitch};
|
|
2
2
|
//# sourceMappingURL=content-switch.js.map
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import "./input.css";
|
|
2
2
|
import type { IconType, TooltipPosition } from "../../../types";
|
|
3
|
+
import { Icon } from "../icon/icon";
|
|
3
4
|
/**
|
|
4
5
|
* Extends `HTMLInputElement` attributes
|
|
5
6
|
*
|
|
6
7
|
* @property {"sm"|"md"|"lg"} [size] - Field height and font scale. Default `"md"`.
|
|
7
8
|
* @property {import("../../../types").IconType} [leftIcon] - Optional left decorative/action icon.
|
|
9
|
+
* @property {Omit<React.ComponentProps<typeof Icon>, "icon">} [leftIconProps] - Extra props forwarded to the left `Icon` (and its `<svg>`).
|
|
8
10
|
* @property {import("../../../types").IconType} [rightIcon] - Optional right icon; pair with `onRightIconClick` for an action.
|
|
11
|
+
* @property {Omit<React.ComponentProps<typeof Icon>, "icon">} [rightIconProps] - Extra props forwarded to the right `Icon` (and its `<svg>`).
|
|
9
12
|
* @property {boolean} [showClear] - Shows a clear control when the field has a value. Default `false`.
|
|
10
13
|
* @property {string} [note] - Helper text below the field (non-error).
|
|
11
14
|
* @property {string} [error] - Error message; styles the field as invalid when set.
|
|
@@ -16,7 +19,9 @@ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "
|
|
|
16
19
|
size?: "sm" | "md" | "lg";
|
|
17
20
|
label?: string;
|
|
18
21
|
leftIcon?: IconType;
|
|
22
|
+
leftIconProps?: Omit<React.ComponentProps<typeof Icon>, "icon">;
|
|
19
23
|
rightIcon?: IconType;
|
|
24
|
+
rightIconProps?: Omit<React.ComponentProps<typeof Icon>, "icon">;
|
|
20
25
|
onRightIconClick?: () => void;
|
|
21
26
|
showClear?: boolean;
|
|
22
27
|
note?: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{__rest as i}from"../../../node_modules/tslib/tslib.es6.mjs.js";import{jsxs as t,jsx as e}from"react/jsx-runtime";import"./input.css";import{forwardRef as o,useState as n,useId as s}from"react";import{Icon as a}from"../icon/icon.js";const l=o(((o,l)=>{var{size:c="md",label:r,leftIcon:p,
|
|
1
|
+
import{__rest as i}from"../../../node_modules/tslib/tslib.es6.mjs.js";import{jsxs as t,jsx as e}from"react/jsx-runtime";import"./input.css";import{forwardRef as o,useState as n,useId as s}from"react";import{Icon as a}from"../icon/icon.js";const l=o(((o,l)=>{var{size:c="md",label:r,leftIcon:p,leftIconProps:d,rightIcon:u,rightIconProps:m,onRightIconClick:h,showClear:b,note:N,error:g="",className:f="",containerClassName:v="",type:I="text",disabled:C,tooltip:y,tooltipPosition:j="top",value:w}=o,z=i(o,["size","label","leftIcon","leftIconProps","rightIcon","rightIconProps","onRightIconClick","showClear","note","error","className","containerClassName","type","disabled","tooltip","tooltipPosition","value"]);const[P,x]=n(!1),$=s(),k="password"===I,O={sm:16,md:20,lg:24},R=void 0!==w&&""!==w;return t("div",{className:`input-container ${v}`,children:[(r||b&&R)&&t("div",{className:"input-header",children:[r&&t("label",{htmlFor:$,className:"input-label",children:[r,y&&e(a,{icon:"info",size:16,tooltip:y,className:"input-info-icon",tooltipPosition:j})]}),b&&R&&!C&&e("button",{type:"button",onClick:()=>{var i;null===(i=z.onChange)||void 0===i||i.call(z,{target:{value:""}})},className:"input-clear",children:"Clear"})]}),t("div",{className:`input-wrapper ${g?"input-error":""} ${C?"input-disabled":""}`,children:[p&&e("div",{className:"input-icon-left",children:e(a,Object.assign({icon:p,size:O[c]},d))}),e("input",Object.assign({id:$,ref:l,type:k?P?"text":"password":I,className:`input input-${c} ${(()=>{const i=void 0!==p,t=void 0!==u||k;return i&&t?"input-has-both-icons":i?"input-has-left-icon":t?"input-has-right-icon":""})()} ${f}`,disabled:C,value:w},z)),(u||k)&&e("div",{className:"input-icon-right",children:k?e("button",{type:"button",onClick:()=>x(!P),className:"input-icon-button",tabIndex:-1,"aria-label":P?"Hide password":"Show password",children:e(a,{icon:P?"eye":"eye-off",size:O[c]})}):u&&h?e("button",{type:"button",onClick:h,className:"input-icon-button",tabIndex:-1,disabled:C,children:e(a,Object.assign({icon:u,size:O[c]},m))}):u?e(a,Object.assign({icon:u,size:O[c]},m)):null})]}),(N||g)&&e("small",{className:g?"input-error-message":"input-note",children:g||N})]})}));l.displayName="Input";const c=l;export{c as Input};
|
|
2
2
|
//# sourceMappingURL=input.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as e,jsxs as o}from"react/jsx-runtime";import"./modal.css";import{useRef as
|
|
1
|
+
import{jsx as e,jsxs as o}from"react/jsx-runtime";import"./modal.css";import{useRef as t,useEffect as a}from"react";import{createPortal as r}from"react-dom";import{Button as l}from"../button/button.js";import{useFocusTrap as s}from"../../../hooks/src/use-focus-trap.js";let n=0;const d=({isOpen:d,onClose:c,title:i,description:m,children:u,footer:N,size:v="md",className:h="",titleContainerClassName:p="",titleClassName:y="",descriptionClassName:f="",closeButtonClassName:C="",headerClassName:b="",contentClassName:$=""})=>{const k=t(null),w=t("modal-title-"+ ++n).current,g=t(!1);return s(k,d),a((()=>{const e=e=>{"Escape"===e.key&&d&&c&&c()};return document.addEventListener("keydown",e),()=>document.removeEventListener("keydown",e)}),[d,c]),a((()=>{if(d){const e=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=e}}}),[d]),d?r(e("div",{className:"modal-overlay",onMouseDown:e=>{g.current=e.target===e.currentTarget},onClick:e=>{c&&g.current&&e.target===e.currentTarget&&c(),g.current=!1},children:o("div",{ref:k,role:"dialog","aria-modal":"true","aria-labelledby":i?w:void 0,className:`modal modal--${v}${h?` ${h}`:""}`,children:[o("div",{className:"modal-header"+(b?` ${b}`:""),children:[o("div",{className:"modal-title-container"+(p?` ${p}`:""),children:[i&&e("h2",{id:w,className:"modal-title"+(y?` ${y}`:""),children:i}),m&&e("p",{className:"modal-description"+(f?` ${f}`:""),children:m})]}),c&&e(l,{variant:"secondary",size:"sm",onClick:c,className:"modal-close"+(C?` ${C}`:""),"aria-label":"Close modal",icon:"close",iconOnly:!0})]}),e("div",{className:"modal-content"+($?` ${$}`:""),children:u}),N&&e("div",{className:"modal-footer",children:N})]})}),document.body):null};export{d as Modal};
|
|
2
2
|
//# sourceMappingURL=modal.js.map
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
.onboarding {
|
|
2
|
+
position: fixed;
|
|
3
|
+
inset: 0;
|
|
4
|
+
pointer-events: none;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/* Dim + click capture. Sits over the page; the spotlight glow draws the cutout. */
|
|
8
|
+
.onboarding__blocker {
|
|
9
|
+
position: fixed;
|
|
10
|
+
background: rgba(0, 0, 0, 0.5);
|
|
11
|
+
pointer-events: auto;
|
|
12
|
+
animation: onboardingFade 0.2s ease;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Visual-only highlight ring around the target (clicks pass through). */
|
|
16
|
+
.onboarding__spotlight {
|
|
17
|
+
position: fixed;
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
box-shadow:
|
|
20
|
+
0 0 0 9999px rgba(0, 0, 0, 0.5),
|
|
21
|
+
0 0 0 2px hsl(var(--primary)),
|
|
22
|
+
0 0 18px 4px hsl(var(--primary) / 0.45);
|
|
23
|
+
transition:
|
|
24
|
+
top 0.25s cubic-bezier(0.16, 1, 0.3, 1),
|
|
25
|
+
left 0.25s cubic-bezier(0.16, 1, 0.3, 1),
|
|
26
|
+
width 0.25s cubic-bezier(0.16, 1, 0.3, 1),
|
|
27
|
+
height 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* When the spotlight is shown, its box-shadow provides the dim — hide the flat blockers' bg. */
|
|
31
|
+
.onboarding:has(.onboarding__spotlight) .onboarding__blocker {
|
|
32
|
+
background: transparent;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.onboarding__popup {
|
|
36
|
+
position: fixed;
|
|
37
|
+
pointer-events: auto;
|
|
38
|
+
width: 320px;
|
|
39
|
+
max-width: calc(100vw - 24px);
|
|
40
|
+
background: hsl(var(--card, var(--background)));
|
|
41
|
+
color: hsl(var(--foreground));
|
|
42
|
+
border: 1px solid hsl(var(--border));
|
|
43
|
+
border-radius: var(--radius-md);
|
|
44
|
+
box-shadow: 0 10px 38px rgba(0, 0, 0, 0.22);
|
|
45
|
+
padding: 16px;
|
|
46
|
+
animation: onboardingPopIn 0.22s cubic-bezier(0.16, 1, 0.3, 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.onboarding__close {
|
|
50
|
+
position: absolute;
|
|
51
|
+
top: 10px;
|
|
52
|
+
right: 10px;
|
|
53
|
+
display: inline-flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: center;
|
|
56
|
+
width: 24px;
|
|
57
|
+
height: 24px;
|
|
58
|
+
border: none;
|
|
59
|
+
background: transparent;
|
|
60
|
+
color: hsl(var(--muted));
|
|
61
|
+
border-radius: var(--radius-sm, 6px);
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.onboarding__close:hover {
|
|
66
|
+
background: hsl(var(--primary) / 0.08);
|
|
67
|
+
color: hsl(var(--foreground));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.onboarding__close:focus-visible {
|
|
71
|
+
outline: 2px solid hsl(var(--primary));
|
|
72
|
+
outline-offset: 1px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.onboarding__title {
|
|
76
|
+
font-size: 15px;
|
|
77
|
+
font-weight: 600;
|
|
78
|
+
line-height: 1.3;
|
|
79
|
+
margin-bottom: 6px;
|
|
80
|
+
padding-right: 24px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.onboarding__content {
|
|
84
|
+
font-size: 13px;
|
|
85
|
+
line-height: 1.5;
|
|
86
|
+
color: hsl(var(--muted));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.onboarding__footer {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
gap: 8px;
|
|
94
|
+
margin-top: 16px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.onboarding__footer-right {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
gap: 8px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.onboarding__progress {
|
|
104
|
+
font-size: 12px;
|
|
105
|
+
color: hsl(var(--muted));
|
|
106
|
+
font-variant-numeric: tabular-nums;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Arrow caret pointing at the target. */
|
|
110
|
+
.onboarding__arrow {
|
|
111
|
+
position: absolute;
|
|
112
|
+
width: 10px;
|
|
113
|
+
height: 10px;
|
|
114
|
+
background: hsl(var(--card, var(--background)));
|
|
115
|
+
border: 1px solid hsl(var(--border));
|
|
116
|
+
transform: rotate(45deg);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.onboarding__popup[data-placement="bottom"] .onboarding__arrow {
|
|
120
|
+
top: -6px;
|
|
121
|
+
left: 50%;
|
|
122
|
+
margin-left: -5px;
|
|
123
|
+
border-right: none;
|
|
124
|
+
border-bottom: none;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.onboarding__popup[data-placement="top"] .onboarding__arrow {
|
|
128
|
+
bottom: -6px;
|
|
129
|
+
left: 50%;
|
|
130
|
+
margin-left: -5px;
|
|
131
|
+
border-left: none;
|
|
132
|
+
border-top: none;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.onboarding__popup[data-placement="right"] .onboarding__arrow {
|
|
136
|
+
left: -6px;
|
|
137
|
+
top: 50%;
|
|
138
|
+
margin-top: -5px;
|
|
139
|
+
border-top: none;
|
|
140
|
+
border-right: none;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.onboarding__popup[data-placement="left"] .onboarding__arrow {
|
|
144
|
+
right: -6px;
|
|
145
|
+
top: 50%;
|
|
146
|
+
margin-top: -5px;
|
|
147
|
+
border-bottom: none;
|
|
148
|
+
border-left: none;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@keyframes onboardingFade {
|
|
152
|
+
from {
|
|
153
|
+
opacity: 0;
|
|
154
|
+
}
|
|
155
|
+
to {
|
|
156
|
+
opacity: 1;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@keyframes onboardingPopIn {
|
|
161
|
+
from {
|
|
162
|
+
opacity: 0;
|
|
163
|
+
transform: scale(0.96);
|
|
164
|
+
}
|
|
165
|
+
to {
|
|
166
|
+
opacity: 1;
|
|
167
|
+
transform: scale(1);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@media (prefers-reduced-motion: reduce) {
|
|
172
|
+
.onboarding__blocker,
|
|
173
|
+
.onboarding__popup,
|
|
174
|
+
.onboarding__spotlight {
|
|
175
|
+
animation: none;
|
|
176
|
+
transition: none;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import "./onboarding.css";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { type OnboardingStorage, type UseOnboardingReturn } from "../../../hooks/src/use-onboarding";
|
|
5
|
+
/** Where a step popup sits relative to its target. `"center"` ignores the target and centers on screen. */
|
|
6
|
+
export type OnboardingPlacement = "top" | "bottom" | "left" | "right" | "auto" | "center";
|
|
7
|
+
/**
|
|
8
|
+
* How a step finds its anchor element:
|
|
9
|
+
* - `string` — a CSS selector (e.g. `"#sidebar"`, `".profile"`, `"[data-tour=save]"`). A bare word falls back to an element id.
|
|
10
|
+
* - `HTMLElement` — the element directly.
|
|
11
|
+
* - `() => HTMLElement | null` — resolved lazily when the step shows.
|
|
12
|
+
*/
|
|
13
|
+
export type OnboardingTarget = string | HTMLElement | (() => HTMLElement | null);
|
|
14
|
+
/**
|
|
15
|
+
* A single step in an onboarding flow.
|
|
16
|
+
*
|
|
17
|
+
* @property {OnboardingTarget} [target] - Element to anchor + spotlight. Omit for a centered step (welcome/finish screens).
|
|
18
|
+
* @property {ReactNode} [title] - Heading shown at the top of the popup.
|
|
19
|
+
* @property {ReactNode} content - Body message (required).
|
|
20
|
+
* @property {OnboardingPlacement} [placement] - Popup side. Default: the flow's `placement` (which defaults to `"auto"`).
|
|
21
|
+
* @property {number} [spotlightPadding] - Extra px around the target in the spotlight cutout.
|
|
22
|
+
* @property {number} [spotlightRadius] - Spotlight corner radius in px.
|
|
23
|
+
* @property {boolean} [disableInteraction] - Block clicks on the spotlighted element. Default: the flow's `disableInteraction`.
|
|
24
|
+
*/
|
|
25
|
+
export interface OnboardingStep {
|
|
26
|
+
target?: OnboardingTarget;
|
|
27
|
+
title?: ReactNode;
|
|
28
|
+
content: ReactNode;
|
|
29
|
+
placement?: OnboardingPlacement;
|
|
30
|
+
spotlightPadding?: number;
|
|
31
|
+
spotlightRadius?: number;
|
|
32
|
+
disableInteraction?: boolean;
|
|
33
|
+
showBack?: boolean;
|
|
34
|
+
showSkip?: boolean;
|
|
35
|
+
nextLabel?: string;
|
|
36
|
+
backLabel?: string;
|
|
37
|
+
finishLabel?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Context passed to `renderStep` / `renderFooter` for fully custom popup UI. */
|
|
40
|
+
export interface OnboardingRenderContext extends UseOnboardingReturn {
|
|
41
|
+
step: OnboardingStep;
|
|
42
|
+
}
|
|
43
|
+
/** Imperative handle exposed via `ref` on `Onboarding`. */
|
|
44
|
+
export type OnboardingHandle = UseOnboardingReturn;
|
|
45
|
+
/**
|
|
46
|
+
* Props for `Onboarding`. Most are flow-wide defaults that individual steps can override.
|
|
47
|
+
*
|
|
48
|
+
* @property {string} id - Stable flow id; persistence key.
|
|
49
|
+
* @property {OnboardingStep[]} steps - Ordered steps.
|
|
50
|
+
* @property {boolean} [run] - Controlled active state. Omit to auto-manage (see `autoStart`).
|
|
51
|
+
*/
|
|
52
|
+
export interface OnboardingProps {
|
|
53
|
+
id: string;
|
|
54
|
+
steps: OnboardingStep[];
|
|
55
|
+
run?: boolean;
|
|
56
|
+
autoStart?: boolean;
|
|
57
|
+
persist?: boolean;
|
|
58
|
+
storage?: OnboardingStorage;
|
|
59
|
+
startDelay?: number;
|
|
60
|
+
placement?: OnboardingPlacement;
|
|
61
|
+
spotlightPadding?: number;
|
|
62
|
+
spotlightRadius?: number;
|
|
63
|
+
disableInteraction?: boolean;
|
|
64
|
+
closeOnOverlayClick?: boolean;
|
|
65
|
+
closeOnEsc?: boolean;
|
|
66
|
+
keyboardNavigation?: boolean;
|
|
67
|
+
scrollIntoView?: boolean;
|
|
68
|
+
scrollBehavior?: ScrollBehavior;
|
|
69
|
+
offset?: number;
|
|
70
|
+
zIndex?: number;
|
|
71
|
+
showProgress?: boolean;
|
|
72
|
+
showSkip?: boolean;
|
|
73
|
+
showBack?: boolean;
|
|
74
|
+
showClose?: boolean;
|
|
75
|
+
nextLabel?: string;
|
|
76
|
+
backLabel?: string;
|
|
77
|
+
skipLabel?: string;
|
|
78
|
+
finishLabel?: string;
|
|
79
|
+
className?: string;
|
|
80
|
+
overlayClassName?: string;
|
|
81
|
+
onStart?: () => void;
|
|
82
|
+
onStepChange?: (index: number, previous: number, step: OnboardingStep) => void;
|
|
83
|
+
onSkip?: (index: number) => void;
|
|
84
|
+
onFinish?: () => void;
|
|
85
|
+
onClose?: () => void;
|
|
86
|
+
renderStep?: (ctx: OnboardingRenderContext) => ReactNode;
|
|
87
|
+
renderFooter?: (ctx: OnboardingRenderContext) => ReactNode;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Modular onboarding / product-tour overlay. Drive a flow by `id` with a `steps`
|
|
91
|
+
* array — each step spotlights an element (by selector/id) and shows a popup with
|
|
92
|
+
* Back / Skip / Next controls. Persists completion so a flow shows once.
|
|
93
|
+
*
|
|
94
|
+
* Headless control lives in {@link useOnboarding}; this component is the batteries-included UI.
|
|
95
|
+
*
|
|
96
|
+
* @param {OnboardingProps} props
|
|
97
|
+
* @param {React.Ref<OnboardingHandle>} ref - Imperative controls (`start`, `next`, `skip`, `reset`, …).
|
|
98
|
+
* @returns {JSX.Element | null} A portal with the spotlight overlay + popup while active.
|
|
99
|
+
*/
|
|
100
|
+
export declare const Onboarding: React.ForwardRefExoticComponent<OnboardingProps & React.RefAttributes<UseOnboardingReturn>>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsxs as t,jsx as e,Fragment as o}from"react/jsx-runtime";import"./onboarding.css";import{forwardRef as n,useImperativeHandle as i,useRef as l,useState as r,useCallback as a,useEffect as s,useLayoutEffect as d}from"react";import{createPortal as h}from"react-dom";import{Button as c}from"../button/button.js";import{Icon as u}from"../icon/icon.js";import{useOnboarding as p}from"../../../hooks/src/use-onboarding.js";import{cn as f}from"../../../utils/src/cn.js";function g(t){if(!t)return null;if("function"==typeof t)return t();if("string"!=typeof t)return t;if("undefined"==typeof document)return null;let e=document.querySelector(t);return!e&&/^[A-Za-z][\w-]*$/.test(t)&&(e=document.getElementById(t)),e}const m=n((function({id:n,steps:m,run:v,autoStart:b,persist:w,storage:y,startDelay:k,placement:_="auto",spotlightPadding:x=8,spotlightRadius:L=8,disableInteraction:N=!0,closeOnOverlayClick:S=!1,closeOnEsc:C=!0,keyboardNavigation:E=!0,scrollIntoView:z=!0,scrollBehavior:I="smooth",offset:M=12,zIndex:j=1e3,showProgress:B=!0,showSkip:D=!0,showBack:O=!0,showClose:R=!0,nextLabel:F="Next",backLabel:A="Back",skipLabel:P="Skip",finishLabel:H="Done",className:V,overlayClassName:W,onStart:q,onStepChange:Z,onSkip:$,onFinish:G,onClose:J,renderStep:K,renderFooter:Q},T){var U,X,Y,tt,et,ot,nt,it,lt,rt,at,st;const dt=p({id:n,total:m.length,run:v,autoStart:b,persist:w,storage:y,startDelay:k,onStart:q,onStepChange:(t,e)=>null==Z?void 0:Z(t,e,m[t]),onSkip:$,onFinish:G,onStop:J});i(T,(()=>dt),[dt]);const{active:ht,index:ct}=dt,ut=m[ct],pt=l(null),[ft,gt]=r(null),[mt,vt]=r(null),bt=null!==(U=null==ut?void 0:ut.placement)&&void 0!==U?U:_,wt=null!==(X=null==ut?void 0:ut.spotlightPadding)&&void 0!==X?X:x,yt=null!==(Y=null==ut?void 0:ut.spotlightRadius)&&void 0!==Y?Y:L,kt=null!==(tt=null==ut?void 0:ut.disableInteraction)&&void 0!==tt?tt:N,_t=a((()=>{if(!ht||!ut)return;const t="center"===bt?null:g(ut.target),e=t?(()=>{const e=t.getBoundingClientRect();return{top:e.top,left:e.left,width:e.width,height:e.height}})():null;gt(e);const o=pt.current,n=o?{width:o.offsetWidth,height:o.offsetHeight}:{width:320,height:160};vt(function(t,e,o,n,i=12){var l;const r=window.innerWidth,a=window.innerHeight,s=t=>Math.max(i,Math.min(t,r-e.width-i)),d=t=>Math.max(i,Math.min(t,a-e.height-i));if(!t||"center"===o)return{top:Math.max(i,(a-e.height)/2),left:Math.max(i,(r-e.width)/2),placement:"center"};const h={top:t.top,bottom:a-(t.top+t.height),left:t.left,right:r-(t.left+t.width)},c=["bottom","top","right","left"];let u;if("auto"===o){const t={bottom:h.bottom>=e.height+n,top:h.top>=e.height+n,right:h.right>=e.width+n,left:h.left>=e.width+n};u=null!==(l=c.find((e=>t[e])))&&void 0!==l?l:[...c].sort(((t,e)=>h[e]-h[t]))[0]}else u=o;const p=t.left+t.width/2-e.width/2,f=t.top+t.height/2-e.height/2;switch(u){case"top":return{top:d(t.top-e.height-n),left:s(p),placement:"top"};case"bottom":return{top:d(t.top+t.height+n),left:s(p),placement:"bottom"};case"left":return{top:d(f),left:s(t.left-e.width-n),placement:"left"};default:return{top:d(f),left:s(t.left+t.width+n),placement:"right"}}}(e,n,bt,M))}),[ht,ut,bt,M]);if(s((()=>{if(!ht||!ut||"center"===bt)return;if(!z)return;const t=g(ut.target);null==t||t.scrollIntoView({behavior:I,block:"center",inline:"center"})}),[ht,ct,ut,bt,z,I]),d((()=>{_t()}),[_t,ct]),s((()=>{if(!ht)return;const t=()=>_t();return window.addEventListener("resize",t),window.addEventListener("scroll",t,!0),()=>{window.removeEventListener("resize",t),window.removeEventListener("scroll",t,!0)}}),[ht,_t]),s((()=>{if(!ht||!kt)return;const t=document.body.style.overflow;return document.body.style.overflow="hidden",()=>{document.body.style.overflow=t}}),[ht,kt]),s((()=>{var t;ht&&(null===(t=pt.current)||void 0===t||t.focus())}),[ht,ct]),s((()=>{if(!ht)return;const t=t=>{if("Escape"===t.key&&C)return t.preventDefault(),void dt.skip();E&&("ArrowRight"===t.key||"Enter"===t.key?(t.preventDefault(),dt.next()):"ArrowLeft"===t.key&&(t.preventDefault(),dt.prev()))};return document.addEventListener("keydown",t),()=>document.removeEventListener("keydown",t)}),[ht,C,E,dt]),!ht||!ut||"undefined"==typeof document)return null;const xt=Object.assign(Object.assign({},dt),{step:ut}),Lt=!!ft&&"center"!==bt?{top:ft.top-wt,left:ft.left-wt,width:ft.width+2*wt,height:ft.height+2*wt}:null,Nt=()=>{S&&dt.skip()},St=[];kt||!Lt?St.push({inset:0}):St.push({top:0,left:0,right:0,height:Math.max(0,Lt.top)},{top:Lt.top+Lt.height,left:0,right:0,bottom:0},{top:Lt.top,left:0,width:Math.max(0,Lt.left),height:Lt.height},{top:Lt.top,left:Lt.left+Lt.width,right:0,height:Lt.height});const Ct=(null!==(et=ut.showBack)&&void 0!==et?et:O)&&!dt.isFirst,Et=null!==(ot=ut.showSkip)&&void 0!==ot?ot:D,zt=dt.isLast?null!==(nt=ut.finishLabel)&&void 0!==nt?nt:H:null!==(it=ut.nextLabel)&&void 0!==it?it:F,It=null!==(lt=ut.backLabel)&&void 0!==lt?lt:A;return h(t("div",{className:f("onboarding",W),style:{zIndex:j},role:"presentation",children:[St.map(((t,o)=>e("div",{className:"onboarding__blocker",style:t,onClick:Nt,"aria-hidden":"true"},o))),Lt&&e("div",{className:"onboarding__spotlight",style:{top:Lt.top,left:Lt.left,width:Lt.width,height:Lt.height,borderRadius:yt},"aria-hidden":"true"}),t("div",{ref:pt,className:f("onboarding__popup",V),"data-placement":null!==(rt=null==mt?void 0:mt.placement)&&void 0!==rt?rt:"center",role:"dialog","aria-modal":kt,"aria-label":"string"==typeof ut.title?ut.title:"Onboarding step",tabIndex:-1,style:{top:null!==(at=null==mt?void 0:mt.top)&&void 0!==at?at:0,left:null!==(st=null==mt?void 0:mt.left)&&void 0!==st?st:0,visibility:mt?"visible":"hidden"},children:[(null==mt?void 0:mt.placement)&&"center"!==mt.placement&&e("span",{className:"onboarding__arrow","aria-hidden":"true"}),K?K(xt):t(o,{children:[R&&e("button",{type:"button",className:"onboarding__close","aria-label":"Close onboarding",onClick:()=>dt.skip(),children:e(u,{icon:"close",size:16})}),null!=ut.title&&e("div",{className:"onboarding__title",children:ut.title}),e("div",{className:"onboarding__content",children:ut.content}),Q?Q(xt):t("div",{className:"onboarding__footer",children:[e("div",{className:"onboarding__footer-left",children:B&&t("span",{className:"onboarding__progress","aria-live":"polite",children:[ct+1," of ",m.length]})}),t("div",{className:"onboarding__footer-right",children:[Et&&!dt.isLast&&e(c,{variant:"ghost",size:"sm",label:P,onClick:()=>dt.skip()}),Ct&&e(c,{variant:"secondary",size:"sm",label:It,onClick:()=>dt.prev()}),e(c,{variant:"primary",size:"sm",label:zt,onClick:()=>dt.next()})]})]})]})]})]}),document.body)}));export{m as Onboarding};
|
|
2
|
+
//# sourceMappingURL=onboarding.js.map
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import "./radio-group.css";
|
|
2
|
+
import { Icon } from "../icon/icon";
|
|
2
3
|
import type { IconType, TooltipPosition } from "../../../types";
|
|
3
4
|
/**
|
|
4
5
|
* One entry in a `RadioGroup` `options` array.
|
|
5
6
|
* @property {string} label - Option title.
|
|
6
7
|
* @property {string} value - Emitted to `onChange` when selected.
|
|
7
8
|
* @property {import("../../../types").IconType} [icon] - Optional leading icon.
|
|
9
|
+
* @property {Omit<React.ComponentProps<typeof Icon>, "icon">} [iconProps] - Extra props forwarded to the built-in `Icon` (and its `<svg>`).
|
|
8
10
|
* @property {"active"|"inactive"} [dot] - Optional status dot style when no icon.
|
|
9
11
|
*/
|
|
10
12
|
export interface RadioOption {
|
|
11
13
|
label: string;
|
|
12
14
|
value: string;
|
|
13
15
|
icon?: IconType;
|
|
16
|
+
iconProps?: Omit<React.ComponentProps<typeof Icon>, "icon">;
|
|
14
17
|
dot?: "active" | "inactive";
|
|
15
18
|
disabled?: boolean;
|
|
16
19
|
tooltip?: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as o,jsxs as i}from"react/jsx-runtime";import"./radio-group.css";import{Icon as a}from"../icon/icon.js";const
|
|
1
|
+
import{jsx as o,jsxs as i}from"react/jsx-runtime";import"./radio-group.css";import{Icon as a}from"../icon/icon.js";import{cn as r}from"../../../utils/src/cn.js";const e=({options:e,selected:s,onChange:t,ariaLabel:l,size:n="md",className:p="",optionClassName:d=""})=>o("div",{role:"radiogroup","aria-label":l,className:`radio-group radio-group--${n} ${p}`,children:e.map((e=>{var l,n;return i("button",{type:"button",onClick:()=>!e.disabled&&t(e.value),disabled:e.disabled,className:`radio-group-option ${s===e.value?"radio-group-option-selected":""} ${e.disabled?"radio-group-option-disabled":""} ${d}`,role:"radio","aria-checked":s===e.value,children:[e.dot&&o("span",{className:`radio-group-option-dot radio-group-option-dot--${e.dot}`}),o("span",{className:"radio-group-option-label",children:e.label}),e.icon&&o(a,Object.assign({icon:e.icon,size:16},e.iconProps,{className:r("radio-group-option-icon",null===(l=e.iconProps)||void 0===l?void 0:l.className)})),e.tooltip&&o(a,{icon:"info",size:14,tooltip:e.tooltip,tooltipPosition:null!==(n=e.tooltipPosition)&&void 0!==n?n:"top",className:"radio-group-option-info"})]},e.value)}))});export{e as RadioGroup};
|
|
2
2
|
//# sourceMappingURL=radio-group.js.map
|
|
@@ -12,6 +12,7 @@ export * from "./src/use-key-press";
|
|
|
12
12
|
export * from "./src/use-long-press";
|
|
13
13
|
export * from "./src/use-media-query";
|
|
14
14
|
export * from "./src/use-mouse";
|
|
15
|
+
export * from "./src/use-onboarding";
|
|
15
16
|
export * from "./src/use-timeout";
|
|
16
17
|
export { useToast, ToastContext, type ToastContextType } from "./src/use-toast";
|
|
17
18
|
export * from "./src/use-window-scroll";
|
|
@@ -9,5 +9,5 @@ type Handler = (event: MouseEvent | TouchEvent) => void;
|
|
|
9
9
|
* @param handler - Called when an outside interaction happens.
|
|
10
10
|
* @param mouseEvent - Which mouse event to listen for (defaults to `"mousedown"`).
|
|
11
11
|
*/
|
|
12
|
-
export declare function useClickOutside<T extends HTMLElement = HTMLElement>(ref: RefObject<T>, handler: Handler, mouseEvent?: "mousedown" | "mouseup"): void;
|
|
12
|
+
export declare function useClickOutside<T extends HTMLElement = HTMLElement>(ref: RefObject<T | null>, handler: Handler, mouseEvent?: "mousedown" | "mouseup"): void;
|
|
13
13
|
export {};
|
|
@@ -7,4 +7,4 @@ import type { RefObject } from "react";
|
|
|
7
7
|
* @param direction - Force `"top"`/`"bottom"` or let it auto-detect.
|
|
8
8
|
* @param menuHeight - Expected menu height (px) used for the calculation.
|
|
9
9
|
*/
|
|
10
|
-
export declare function useDropdownPosition(containerRef: RefObject<HTMLElement>, isOpen: boolean, direction?: "top" | "bottom" | "auto", menuHeight?: number): "top" | "bottom";
|
|
10
|
+
export declare function useDropdownPosition(containerRef: RefObject<HTMLElement | null>, isOpen: boolean, direction?: "top" | "bottom" | "auto", menuHeight?: number): "top" | "bottom";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable persistence adapter for onboarding flows. Swap to back completion
|
|
3
|
+
* with cookies, a backend, or anything else by implementing these two methods.
|
|
4
|
+
*
|
|
5
|
+
* @property {(id: string) => boolean} isCompleted - Whether the flow `id` was already finished/skipped.
|
|
6
|
+
* @property {(id: string, completed: boolean) => void} setCompleted - Persist (or clear) completion for `id`.
|
|
7
|
+
*/
|
|
8
|
+
export interface OnboardingStorage {
|
|
9
|
+
isCompleted: (id: string) => boolean;
|
|
10
|
+
setCompleted: (id: string, completed: boolean) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const localStorageOnboarding: OnboardingStorage;
|
|
13
|
+
export declare const memoryOnboarding: OnboardingStorage;
|
|
14
|
+
/**
|
|
15
|
+
* Options for {@link useOnboarding}.
|
|
16
|
+
*
|
|
17
|
+
* @property {string} id - Stable flow identifier; used as the persistence key.
|
|
18
|
+
* @property {number} total - Number of steps in the flow.
|
|
19
|
+
* @property {boolean} [run] - Controlled active state. Omit to let the hook manage start/stop internally.
|
|
20
|
+
* @property {boolean} [autoStart] - In uncontrolled mode, start on mount when the flow is not yet completed. Default `true`.
|
|
21
|
+
* @property {boolean} [persist] - Persist completion so a finished/skipped flow does not auto-restart. Default `true`.
|
|
22
|
+
* @property {OnboardingStorage} [storage] - Persistence adapter. Default `localStorageOnboarding`.
|
|
23
|
+
* @property {number} [startDelay] - Delay (ms) before auto-start, e.g. to let the page settle. Default `0`.
|
|
24
|
+
*/
|
|
25
|
+
export interface UseOnboardingOptions {
|
|
26
|
+
id: string;
|
|
27
|
+
total: number;
|
|
28
|
+
run?: boolean;
|
|
29
|
+
autoStart?: boolean;
|
|
30
|
+
persist?: boolean;
|
|
31
|
+
storage?: OnboardingStorage;
|
|
32
|
+
startDelay?: number;
|
|
33
|
+
onStart?: () => void;
|
|
34
|
+
onStepChange?: (index: number, previous: number) => void;
|
|
35
|
+
onSkip?: (index: number) => void;
|
|
36
|
+
onFinish?: () => void;
|
|
37
|
+
onStop?: () => void;
|
|
38
|
+
}
|
|
39
|
+
/** Imperative control surface + state returned by {@link useOnboarding}. */
|
|
40
|
+
export interface UseOnboardingReturn {
|
|
41
|
+
active: boolean;
|
|
42
|
+
index: number;
|
|
43
|
+
total: number;
|
|
44
|
+
isFirst: boolean;
|
|
45
|
+
isLast: boolean;
|
|
46
|
+
completed: boolean;
|
|
47
|
+
start: () => void;
|
|
48
|
+
stop: () => void;
|
|
49
|
+
next: () => void;
|
|
50
|
+
prev: () => void;
|
|
51
|
+
goTo: (index: number) => void;
|
|
52
|
+
skip: () => void;
|
|
53
|
+
finish: () => void;
|
|
54
|
+
reset: () => void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Headless onboarding/tour state machine: drives a flow by `id`, tracks the
|
|
58
|
+
* current step, persists completion, and supports controlled or uncontrolled use.
|
|
59
|
+
*
|
|
60
|
+
* Pair with the `Onboarding` component for ready-made coachmarks, or render your
|
|
61
|
+
* own UI from the returned state for a fully custom flow.
|
|
62
|
+
*
|
|
63
|
+
* @param {UseOnboardingOptions} options
|
|
64
|
+
* @returns {UseOnboardingReturn} State (`active`, `index`, `isFirst`, …) and controls (`next`, `prev`, `skip`, `finish`, `reset`, …).
|
|
65
|
+
*/
|
|
66
|
+
export declare function useOnboarding({ id, total, run, autoStart, persist, storage, startDelay, onStart, onStepChange, onSkip, onFinish, onStop, }: UseOnboardingOptions): UseOnboardingReturn;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useState as t,useRef as n,useCallback as e,useEffect as o}from"react";const r="h2o-onboarding:",i={isCompleted:t=>{try{return"undefined"!=typeof window&&"1"===window.localStorage.getItem(r+t)}catch(t){return!1}},setCompleted:(t,n)=>{try{if("undefined"==typeof window)return;n?window.localStorage.setItem(r+t,"1"):window.localStorage.removeItem(r+t)}catch(t){}}};function a({id:r,total:a,run:l,autoStart:c=!0,persist:s=!0,storage:u=i,startDelay:p=0,onStart:d,onStepChange:S,onSkip:v,onFinish:g,onStop:h}){const m=void 0!==l,[C,f]=t(!1),[w,k]=t(0),[y,F]=t((()=>!!s&&u.isCompleted(r))),x=m?l:C,I=n(0),T=e((t=>{I.current=t,k(t)}),[]),M=n({onStart:d,onStepChange:S,onSkip:v,onFinish:g,onStop:h,storage:u,persist:s,id:r});M.current={onStart:d,onStepChange:S,onSkip:v,onFinish:g,onStop:h,storage:u,persist:s,id:r};const b=e((t=>{const{persist:n,storage:e,id:o}=M.current;n&&e.setCompleted(o,t),F(t)}),[]),D=e((()=>{var t,n;T(0),m||f(!0),null===(n=(t=M.current).onStart)||void 0===n||n.call(t)}),[m,T]),L=e((()=>{var t,n;m||f(!1),null===(n=(t=M.current).onStop)||void 0===n||n.call(t)}),[m]),j=e((t=>{var n,e;const o=Math.max(0,Math.min(t,a-1)),r=I.current;o!==r&&(T(o),null===(e=(n=M.current).onStepChange)||void 0===e||e.call(n,o,r))}),[a,T]),q=e((()=>{var t,n,e,o;b(!0),m||f(!1),null===(n=(t=M.current).onFinish)||void 0===n||n.call(t),null===(o=(e=M.current).onStop)||void 0===o||o.call(e)}),[m,b]),z=e((()=>{var t,n;const e=I.current;e>=a-1?q():(T(e+1),null===(n=(t=M.current).onStepChange)||void 0===n||n.call(t,e+1,e))}),[a,q,T]),A=e((()=>{var t,n;const e=I.current;e<=0||(T(e-1),null===(n=(t=M.current).onStepChange)||void 0===n||n.call(t,e-1,e))}),[T]),B=e((()=>{var t,n,e,o;b(!0),m||f(!1),null===(n=(t=M.current).onSkip)||void 0===n||n.call(t,I.current),null===(o=(e=M.current).onStop)||void 0===o||o.call(e)}),[m,b]),E=e((()=>{b(!1)}),[b]),G=n(!1);o((()=>{if(!m&&c&&!G.current&&!(s&&u.isCompleted(r)||a<=0)){if(G.current=!0,p>0){const t=setTimeout(D,p);return()=>clearTimeout(t)}D()}}),[m,c,s,r,a,p]);const H=n(x);return o((()=>{x&&!H.current&&T(0),H.current=x}),[x,T]),{active:x,index:w,total:a,isFirst:w<=0,isLast:w>=a-1,completed:y,start:D,stop:L,next:z,prev:A,goTo:j,skip:B,finish:q,reset:E}}export{i as localStorageOnboarding,a as useOnboarding};
|
|
2
|
+
//# sourceMappingURL=use-onboarding.js.map
|
package/dist/esm/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export{ToastProvider}from"./components/src/toast/toast.js";export{Button}from"./components/src/button/button.js";export{Checkbox,CheckboxGroup}from"./components/src/checkbox/checkbox.js";export{Icon}from"./components/src/icon/icon.js";export{Radio}from"./components/src/radio/radio.js";export{Tooltip}from"./components/src/tooltip/tooltip.js";export{Toggle}from"./components/src/toggle/toggle.js";export{ContentSwitch}from"./components/src/content-switch/content-switch.js";export{Input}from"./components/src/input/input.js";export{Textarea}from"./components/src/textarea/textarea.js";export{Dropdown}from"./components/src/dropdown/dropdown.js";export{Slider}from"./components/src/slider/slider.js";export{SidePanel}from"./components/src/side-panel/side-panel.js";export{Modal}from"./components/src/modal/modal.js";export{Tag}from"./components/src/tag/tag.js";export{Accordion}from"./components/src/accordion/accordion.js";export{MultiSelect}from"./components/src/multi-select/multi-select.js";export{RadioGroup}from"./components/src/radio-group/radio-group.js";export{Tabs}from"./components/src/tabs/tabs.js";export{PopOver,PopOverContent,PopOverTrigger}from"./components/src/popover/popover.js";export{Pagination}from"./components/src/pagination/pagination.js";export{Table}from"./components/src/table/table.js";export{Logo}from"./components/src/logo/logo.js";export{Calendar}from"./components/src/calendar/calendar.js";export{MonthCalendar}from"./components/src/month-calendar/month-calendar.js";export{Loader}from"./components/src/loader/loader.js";export{Message}from"./components/src/message/message.js";export{MultiMenu}from"./components/src/multi-menu/multi-menu.js";export{CompactMultiSelect}from"./components/src/compact-multi-select/compact-multi-select.js";export{DateRangePicker}from"./components/src/date-range-picker/date-range-picker.js";export{ExpandableTable}from"./components/src/expandable-table/expandable-table.js";export{Chip}from"./components/src/chip/chip.js";export{SegmentedToggle}from"./components/src/segmented-toggle/segmented-toggle.js";export{Skeleton}from"./components/src/skeleton/skeleton.js";export{DatePicker}from"./components/src/date-picker/date-picker.js";export{TimePicker}from"./components/src/time-picker/time-picker.js";export{ExpandableSearch}from"./components/src/expandable-search/expandable-search.js";
|
|
1
|
+
export{ToastProvider}from"./components/src/toast/toast.js";export{Button}from"./components/src/button/button.js";export{Checkbox,CheckboxGroup}from"./components/src/checkbox/checkbox.js";export{Icon}from"./components/src/icon/icon.js";export{Radio}from"./components/src/radio/radio.js";export{Tooltip}from"./components/src/tooltip/tooltip.js";export{Toggle}from"./components/src/toggle/toggle.js";export{ContentSwitch}from"./components/src/content-switch/content-switch.js";export{Input}from"./components/src/input/input.js";export{Textarea}from"./components/src/textarea/textarea.js";export{Dropdown}from"./components/src/dropdown/dropdown.js";export{Slider}from"./components/src/slider/slider.js";export{SidePanel}from"./components/src/side-panel/side-panel.js";export{Modal}from"./components/src/modal/modal.js";export{Tag}from"./components/src/tag/tag.js";export{Accordion}from"./components/src/accordion/accordion.js";export{MultiSelect}from"./components/src/multi-select/multi-select.js";export{RadioGroup}from"./components/src/radio-group/radio-group.js";export{Tabs}from"./components/src/tabs/tabs.js";export{PopOver,PopOverContent,PopOverTrigger}from"./components/src/popover/popover.js";export{Pagination}from"./components/src/pagination/pagination.js";export{Table}from"./components/src/table/table.js";export{Logo}from"./components/src/logo/logo.js";export{Calendar}from"./components/src/calendar/calendar.js";export{MonthCalendar}from"./components/src/month-calendar/month-calendar.js";export{Loader}from"./components/src/loader/loader.js";export{Message}from"./components/src/message/message.js";export{MultiMenu}from"./components/src/multi-menu/multi-menu.js";export{CompactMultiSelect}from"./components/src/compact-multi-select/compact-multi-select.js";export{DateRangePicker}from"./components/src/date-range-picker/date-range-picker.js";export{ExpandableTable}from"./components/src/expandable-table/expandable-table.js";export{Chip}from"./components/src/chip/chip.js";export{SegmentedToggle}from"./components/src/segmented-toggle/segmented-toggle.js";export{Skeleton}from"./components/src/skeleton/skeleton.js";export{DatePicker}from"./components/src/date-picker/date-picker.js";export{TimePicker}from"./components/src/time-picker/time-picker.js";export{ExpandableSearch}from"./components/src/expandable-search/expandable-search.js";export{Onboarding}from"./components/src/onboarding/onboarding.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./button.css";
|
|
2
2
|
import type { IconType, TooltipPosition } from "../../../types";
|
|
3
|
+
import { Icon } from "../icon/icon";
|
|
3
4
|
import React from "react";
|
|
4
5
|
/**
|
|
5
6
|
* Extends native `HTMLButtonElement` attributes
|
|
@@ -11,6 +12,7 @@ import React from "react";
|
|
|
11
12
|
* @property {"left"|"right"} [iconPosition] - Icon position relative to the label. Default `"right"`.
|
|
12
13
|
* @property {boolean} [iconOnly] - Icon without visible text.
|
|
13
14
|
* @property {number} [iconStrokeWidth] - SVG stroke width for the icon. Default `1.5`.
|
|
15
|
+
* @property {Omit<React.ComponentProps<typeof Icon>, "icon">} [iconProps] - Extra props forwarded to the built-in `Icon` (and its `<svg>`); overrides defaults like `size`/`strokeWidth`.
|
|
14
16
|
* @property {boolean} [isLoading] - Shows a spinner and disables the button. Default `false`.
|
|
15
17
|
* @property {boolean} [fullWidth] - Stretches to 100% width of the container. Default `false`.
|
|
16
18
|
* @property {string} [tooltip] - If set, wraps the button in a `Tooltip` with this text.
|
|
@@ -22,6 +24,7 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
22
24
|
label?: string;
|
|
23
25
|
iconStrokeWidth?: number;
|
|
24
26
|
icon?: IconType;
|
|
27
|
+
iconProps?: Omit<React.ComponentProps<typeof Icon>, "icon">;
|
|
25
28
|
iconPosition?: "left" | "right";
|
|
26
29
|
iconOnly?: boolean;
|
|
27
30
|
isLoading?: boolean;
|