@ngrok/mantle 0.82.1 → 0.83.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/dist/accordion.d.ts +7 -7
- package/dist/agent.json +3 -1
- package/dist/{alert-BWwNPOMI.d.ts → alert-B1HXfkYn.d.ts} +9 -7
- package/dist/{alert-vwFd7qdp.js → alert-BuG2qS5l.js} +1 -1
- package/dist/alert-center.d.ts +3 -3
- package/dist/alert-center.js +1 -1
- package/dist/alert-dialog.d.ts +18 -18
- package/dist/alert.d.ts +1 -1
- package/dist/alert.js +1 -1
- package/dist/anchor.d.ts +2 -2
- package/dist/app-layout.js +1 -1
- package/dist/area-chart.d.ts +67 -19
- package/dist/area-chart.js +1 -1
- package/dist/avatar.d.ts +319 -0
- package/dist/avatar.js +1 -0
- package/dist/bar-chart.d.ts +73 -20
- package/dist/bar-chart.js +1 -1
- package/dist/breadcrumb.d.ts +50 -17
- package/dist/breadcrumb.js +1 -1
- package/dist/browser-only.d.ts +3 -3
- package/dist/{button-C-gMbPso.d.ts → button-BIf69CSV.d.ts} +2 -2
- package/dist/button-BLDjW1u9.js +1 -0
- package/dist/{button-IlxHhjPK.js → button-CCkGqE1t.js} +1 -1
- package/dist/{button-C7vAl6lS.d.ts → button-DJ7AAgII.d.ts} +4 -6
- package/dist/{button-DARFK2KJ.d.ts → button-PoIujPMX.d.ts} +2 -2
- package/dist/button.d.ts +3 -3
- package/dist/button.js +1 -1
- package/dist/calendar.d.ts +1 -1
- package/dist/calendar.js +1 -1
- package/dist/card.d.ts +15 -15
- package/dist/{chart-1cVjAcj8.d.ts → chart-0zcFJh4n.d.ts} +75 -22
- package/dist/chart-DBxT9BNl.js +2 -0
- package/dist/{choice-BNRQXszu.d.ts → choice-CVdQrs-m.d.ts} +118 -6
- package/dist/choice.d.ts +1 -1
- package/dist/{code-block-C9uaM9-V.js → code-block-CIE0F2fu.js} +1 -1
- package/dist/{code-block-B6OlZrSI.d.ts → code-block-CZWPrV4k.d.ts} +3 -3
- package/dist/code-block.d.ts +10 -7
- package/dist/code-block.js +2 -2
- package/dist/code-block_highlight-utils.d.ts +1 -1
- package/dist/code-block_highlight-utils.js +1 -1
- package/dist/combobox.d.ts +8 -9
- package/dist/command.d.ts +668 -106
- package/dist/command.js +1 -1
- package/dist/data-table.d.ts +6 -6
- package/dist/data-table.js +1 -1
- package/dist/dialog-32Hq6w6P.js +1 -0
- package/dist/dialog-B6h1BCDL.d.ts +895 -0
- package/dist/dialog.d.ts +2 -805
- package/dist/dialog.js +1 -1
- package/dist/dropdown-menu-BcALUQMp.d.ts +1 -0
- package/dist/{dropdown-menu-D6pMAY7I.d.ts → dropdown-menu-DZOfj6HB.d.ts} +1 -1
- package/dist/dropdown-menu.d.ts +2 -2
- package/dist/field.d.ts +5 -5
- package/dist/field.js +1 -1
- package/dist/flag.js +1 -1
- package/dist/hooks.d.ts +16 -17
- package/dist/hooks.js +1 -1
- package/dist/icons-DEVx1oP9.js +1 -0
- package/dist/icons.d.ts +7 -6
- package/dist/icons.js +1 -1
- package/dist/{in-view-DDEGUKDR.d.ts → in-view-gxyc9Q1_.d.ts} +1 -1
- package/dist/{input-BuYDU6CF.d.ts → input-DpnC3Xgj.d.ts} +2 -2
- package/dist/input.d.ts +1 -1
- package/dist/kbd.d.ts +1 -1
- package/dist/line-chart.d.ts +60 -13
- package/dist/line-chart.js +1 -1
- package/dist/list-CwL23L_B.js +1 -0
- package/dist/list.d.ts +2 -2
- package/dist/list.js +1 -1
- package/dist/llms.txt +3 -1
- package/dist/mantle-dark-high-contrast.css +30 -17
- package/dist/mantle-dark.css +48 -13
- package/dist/mantle-light-high-contrast.css +33 -15
- package/dist/mantle.css +436 -26
- package/dist/media-object.d.ts +5 -1
- package/dist/multi-select.d.ts +7 -7
- package/dist/multi-select.js +1 -1
- package/dist/otp-input.d.ts +4 -4
- package/dist/otp-input.js +1 -1
- package/dist/pagination.d.ts +2 -2
- package/dist/pagination.js +1 -1
- package/dist/progress.d.ts +4 -4
- package/dist/qr-code.d.ts +2 -2
- package/dist/sandbar.d.ts +471 -0
- package/dist/sandbar.js +1 -0
- package/dist/sandboxed-on-click.d.ts +4 -6
- package/dist/scatter-plot.d.ts +69 -24
- package/dist/scatter-plot.js +1 -1
- package/dist/{select-0z7HVS6r.d.ts → select-DVHmgih8.d.ts} +13 -7
- package/dist/select-LK9NX-vV.js +1 -0
- package/dist/select.d.ts +1 -1
- package/dist/select.js +1 -1
- package/dist/selectable-list.d.ts +4 -4
- package/dist/selectable-list.js +1 -1
- package/dist/{separator-B5UmkVRL.d.ts → separator-CNDs98Dv.d.ts} +2 -2
- package/dist/separator.d.ts +1 -1
- package/dist/{sheet-BxvTfPLM.js → sheet-BOSknSN9.js} +1 -1
- package/dist/sheet.d.ts +1 -1
- package/dist/sheet.js +1 -1
- package/dist/sidebar.d.ts +347 -366
- package/dist/sidebar.js +1 -1
- package/dist/slider.d.ts +1 -1
- package/dist/split-button.d.ts +12 -12
- package/dist/split-button.js +1 -1
- package/dist/switch.d.ts +2 -1
- package/dist/{table-CGxJwtbL.js → table-C_Nm_RSV.js} +1 -1
- package/dist/{table-CZBYEmyK.d.ts → table-D51ZtV4U.d.ts} +18 -8
- package/dist/table.d.ts +1 -1
- package/dist/table.js +1 -1
- package/dist/tabs.d.ts +8 -13
- package/dist/tabs.js +1 -1
- package/dist/{theme-FbfjYiD3.d.ts → theme-6OxNhf6l.d.ts} +6 -6
- package/dist/theme-CVfq037P.js +1 -0
- package/dist/theme-switcher.d.ts +3 -3
- package/dist/theme-switcher.js +1 -1
- package/dist/theme.d.ts +86 -23
- package/dist/theme.js +1 -1
- package/dist/toast.d.ts +9 -9
- package/dist/toast.js +1 -1
- package/dist/{tooltip-mrpLz4DZ.d.ts → tooltip-DuXGxIse.d.ts} +6 -4
- package/dist/tooltip.d.ts +1 -1
- package/dist/use-breakpoint-Dqu12Szq.js +1 -0
- package/dist/use-callback-ref-IYti9y7x.js +1 -0
- package/dist/use-is-apple-platform-Cxwrb86L.js +1 -0
- package/dist/use-matches-media-query-Bd_GhhVd.js +1 -0
- package/dist/utils.d.ts +1 -1
- package/package.json +5 -3
- package/dist/button-DvHMWAPl.js +0 -1
- package/dist/chart-B3pAvmkk.js +0 -2
- package/dist/dialog-DozL6n-F.js +0 -1
- package/dist/dropdown-menu-BbbQuae2.d.ts +0 -1
- package/dist/icons-Bb_zF2OV.js +0 -1
- package/dist/list-CJABVHwi.js +0 -1
- package/dist/platform-BVD3gpKD.js +0 -1
- package/dist/select-BReznXaw.js +0 -1
- package/dist/theme-BorfgnNh.js +0 -1
- package/dist/use-breakpoint-Wd7_5-Mu.js +0 -1
- package/dist/use-matches-media-query-CMSxHR9n.js +0 -1
package/dist/otp-input.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./cx-IiQEAKf5.js";import{t}from"./slot-DpNWVci8.js";import{t as n}from"./types-D85fCNV3.js";import{a as r,r as i}from"./field-Bpptk_9t.js";import{t as a}from"./field-B1wqeZr4.js";import{useContext as o}from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";import{MinusIcon as l}from"@phosphor-icons/react/Minus";import{OTPInput as u,OTPInputContext as d,REGEXP_ONLY_CHARS as f,REGEXP_ONLY_DIGITS as p,REGEXP_ONLY_DIGITS_AND_CHARS as m}from"input-otp";const h={error:`danger`,success:`success`,warning:`warning`},g=e=>`var(--color-${e}-600)`,_=e=>`var(--ring-color-focus-${e})`,v=({totalActive:e,total:t})=>e===0?`idle`:e===1?`caret`:e===t?`all`:`range`,y=({children:e,validation:t})=>{let r=o(d),i=r.slots.length,a=r.slots.reduce((e,t)=>e+ +!!t.isActive,0),c=v({totalActive:a,total:i}),l=t?h[t]:void 0,u=l?n({"--otp-validation-border":g(l),"--otp-validation-ring":_(l)}):void 0;return s(`div`,{className:`group/otp contents`,"data-otp-state":c,"data-validation":t||void 0,style:u,children:e})},b={Root:({"aria-invalid":t,children:n,className:c,containerClassName:l,ref:d,validation:f,...p})=>{let m=r(),h=o(a),{ariaInvalid:g,validation:_}=i({"aria-invalid":t,defaultAriaInvalid:!1,validation:f??m});return s(u,{ref:d,"aria-invalid":g,"data-slot":`otp-input`,containerClassName:e(`flex items-center gap-2 has-disabled:opacity-50`,l),className:e(`disabled:cursor-not-allowed`,c),...p,...h?{"aria-describedby":h[`aria-describedby`],"aria-errormessage":h[`aria-errormessage`],id:h.id,name:h.name}:void 0,children:s(y,{validation:_,children:n})})},Group:({asChild:n,children:r,className:i,ref:a,...o})=>s(n?t:`div`,{ref:a,"data-slot":`otp-input-group`,className:e(`relative flex items-center rounded-md`,`has-[[data-active]~[data-active]]:ring-focus-accent has-[[data-active]~[data-active]]:ring-4`,`group-data-validation/otp:has-[[data-active]~[data-active]]:ring-(--otp-validation-ring)`,i),...o,children:r}),Slot:({className:t,index:n,ref:r,...i})=>{let a=o(d).slots[n],l=a?.char??null,u=a?.hasFakeCaret??!1;return c(`div`,{ref:r,"data-slot":`otp-input-slot`,"data-active":
|
|
1
|
+
import{t as e}from"./cx-IiQEAKf5.js";import{t}from"./slot-DpNWVci8.js";import{t as n}from"./types-D85fCNV3.js";import{a as r,r as i}from"./field-Bpptk_9t.js";import{t as a}from"./field-B1wqeZr4.js";import{useContext as o}from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";import{MinusIcon as l}from"@phosphor-icons/react/Minus";import{OTPInput as u,OTPInputContext as d,REGEXP_ONLY_CHARS as f,REGEXP_ONLY_DIGITS as p,REGEXP_ONLY_DIGITS_AND_CHARS as m}from"input-otp";const h={error:`danger`,success:`success`,warning:`warning`},g=e=>`var(--color-${e}-600)`,_=e=>`var(--ring-color-focus-${e})`,v=({totalActive:e,total:t})=>e===0?`idle`:e===1?`caret`:e===t?`all`:`range`,y=({children:e,validation:t})=>{let r=o(d),i=r.slots.length,a=r.slots.reduce((e,t)=>e+ +!!t.isActive,0),c=v({totalActive:a,total:i}),l=t?h[t]:void 0,u=l?n({"--otp-validation-border":g(l),"--otp-validation-ring":_(l)}):void 0;return s(`div`,{className:`group/otp contents`,"data-otp-state":c,"data-validation":t||void 0,style:u,children:e})},b={Root:({"aria-invalid":t,children:n,className:c,containerClassName:l,ref:d,validation:f,...p})=>{let m=r(),h=o(a),{ariaInvalid:g,validation:_}=i({"aria-invalid":t,defaultAriaInvalid:!1,validation:f??m});return s(u,{ref:d,"aria-invalid":g,"data-slot":`otp-input`,containerClassName:e(`flex items-center gap-2 has-disabled:opacity-50`,l),className:e(`disabled:cursor-not-allowed`,c),...p,...h?{"aria-describedby":h[`aria-describedby`],"aria-errormessage":h[`aria-errormessage`],id:h.id,name:h.name}:void 0,children:s(y,{validation:_,children:n})})},Group:({asChild:n,children:r,className:i,ref:a,...o})=>s(n?t:`div`,{ref:a,"data-slot":`otp-input-group`,className:e(`relative flex items-center rounded-md`,`has-[[data-active]~[data-active]]:ring-focus-accent has-[[data-active]~[data-active]]:ring-4`,`group-data-validation/otp:has-[[data-active]~[data-active]]:ring-(--otp-validation-ring)`,i),...o,children:r}),Slot:({className:t,index:n,ref:r,...i})=>{let a=o(d).slots[n],l=a?.char??null,u=a?.hasFakeCaret??!1,f=a?.isActive??!1;return c(`div`,{ref:r,"data-slot":`otp-input-slot`,"data-active":f?``:void 0,className:e(`border-form bg-form text-strong relative flex h-10 w-10 items-center justify-center border-y border-r text-sm shadow-sm outline-hidden transition-all duration-150 ease-out`,`first:rounded-l-md first:border-l last:rounded-r-md`,`[&:has(+[data-active])]:group-data-[otp-state=caret]/otp:border-r-transparent`,`data-active:group-data-[otp-state=caret]/otp:border-accent-600`,`data-active:group-data-[otp-state=caret]/otp:border-l`,`data-active:group-data-[otp-state=caret]/otp:ring-focus-accent`,`data-active:group-data-[otp-state=caret]/otp:z-20`,`data-active:group-data-[otp-state=caret]/otp:ring-4`,`group-data-[otp-state=all]/otp:border-accent-600`,`group-data-validation/otp:border-y-(--otp-validation-border)`,`group-data-validation/otp:first:border-l-(--otp-validation-border)`,`group-data-validation/otp:last:border-r-(--otp-validation-border)`,`group-data-validation/otp:data-active:group-data-[otp-state=caret]/otp:border-(--otp-validation-border)`,`group-data-validation/otp:data-active:group-data-[otp-state=caret]/otp:ring-(--otp-validation-ring)`,`group-data-validation/otp:group-data-[otp-state=all]/otp:border-(--otp-validation-border)`,t),...i,children:[l,u&&s(`div`,{className:`pointer-events-none absolute inset-0 flex items-center justify-center`,children:s(`div`,{className:`bg-strong h-4 w-px animate-pulse`})})]})},Separator:({asChild:n,children:r,className:i,ref:a,semantic:o=!1,...c})=>{let u=n?t:`div`,d=o?{role:`separator`}:{"aria-hidden":!0,role:`none`};return s(u,{ref:a,"data-slot":`otp-input-separator`,className:e(`text-separator flex items-center`,i),...d,...c,children:r??s(l,{weight:`bold`})})}};export{b as OtpInput,f as REGEXP_ONLY_CHARS,p as REGEXP_ONLY_DIGITS,m as REGEXP_ONLY_DIGITS_AND_CHARS};
|
package/dist/pagination.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as ButtonGroup } from "./button-
|
|
2
|
-
import { t as Select } from "./select-
|
|
1
|
+
import { t as ButtonGroup } from "./button-BIf69CSV.js";
|
|
2
|
+
import { t as Select } from "./select-DVHmgih8.js";
|
|
3
3
|
import { ComponentProps } from "react";
|
|
4
4
|
//#region src/components/pagination/cursor-pagination.d.ts
|
|
5
5
|
type CursorPaginationProps = ComponentProps<"div"> & {
|
package/dist/pagination.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./cx-IiQEAKf5.js";import{t}from"./button-
|
|
1
|
+
import{t as e}from"./cx-IiQEAKf5.js";import{t}from"./button-CCkGqE1t.js";import{t as n}from"./button-DmJkYfyl.js";import{n as r}from"./separator-D0D6VyyM.js";import{t as i}from"./select-LK9NX-vV.js";import{createContext as a,useContext as o,useEffect as s,useMemo as c,useState as l}from"react";import u from"tiny-invariant";import{jsx as d,jsxs as f}from"react/jsx-runtime";import{CaretRightIcon as p}from"@phosphor-icons/react/CaretRight";import{CaretLeftIcon as m}from"@phosphor-icons/react/CaretLeft";const h=a(void 0),g=({className:t,children:n,defaultPageSize:r,ref:i,...a})=>{let[o,s]=l(r),u=c(()=>({defaultPageSize:r,pageSize:o,setPageSize:s}),[r,o]);return d(h.Provider,{value:u,children:d(`div`,{"data-slot":`cursor-pagination`,className:e(`inline-flex items-center justify-between gap-2`,t),ref:i,...a,children:n})})},_=({hasNextPage:e,hasPreviousPage:i,onNextPage:a,onPreviousPage:o,ref:s,...c})=>f(n,{"data-slot":`cursor-pagination-buttons`,appearance:`panel`,ref:s,...c,children:[d(t,{"data-slot":`cursor-pagination-previous`,appearance:`ghost`,disabled:!i,icon:d(m,{}),intent:`neutral`,label:`Previous page`,onClick:o,size:`sm`,type:`button`}),d(r,{"data-slot":`cursor-pagination-separator`,orientation:`vertical`,className:`min-h-5`}),d(t,{"data-slot":`cursor-pagination-next`,appearance:`ghost`,disabled:!e,icon:d(p,{}),intent:`neutral`,label:`Next page`,onClick:a,size:`sm`,type:`button`})]}),v=[5,10,20,50,100],y=({className:t,pageSizes:n=v,onChangePageSize:r,ref:a,...s})=>{let c=o(h);return u(c,`CursorPageSizeSelect must be used as a child of a CursorPagination component`),u(n.includes(c.defaultPageSize),`CursorPagination.defaultPageSize must be included in CursorPageSizeSelect.pageSizes`),u(n.includes(c.pageSize),`CursorPagination.pageSize must be included in CursorPageSizeSelect.pageSizes`),f(i.Root,{defaultValue:`${c.pageSize}`,onValueChange:e=>{let t=Number.parseInt(e,10);Number.isNaN(t)&&(t=c.defaultPageSize),c.setPageSize(t),r?.(t)},children:[d(i.Trigger,{ref:a,"data-slot":`cursor-pagination-page-size-select`,className:e(`w-auto min-w-36`,t),value:c.pageSize,...s,children:d(i.Value,{})}),d(i.Content,{width:`trigger`,children:n.map(e=>f(i.Item,{value:`${e}`,children:[e,` per page`]},e))})]})};function b({className:t,...n}){let r=o(h);return u(r,`CursorPageSizeValue must be used as a child of a CursorPagination component`),f(`span`,{"data-slot":`cursor-pagination-page-size-value`,className:e(`text-muted text-sm font-normal`,t),...n,children:[r.pageSize,` per page`]})}const x={Root:g,Buttons:_,PageSizeSelect:y,PageSizeValue:b};function S({listSize:e,pageSize:t}){let[n,r]=l(1),[i,a]=l(t);s(()=>{a(t),r(1)},[t]),s(()=>{r(1)},[e]);let o=Math.ceil(e/i),c=(n-1)*i,u=n>1,d=n<o;function f(e){r(Math.max(1,Math.min(e,o)))}function p(){d&&r(e=>Math.min(e+1,o))}function m(){u&&r(e=>Math.max(e-1,1))}function h(e){a(e),r(1)}function g(){r(o)}function _(){r(1)}return{currentPage:n,goToFirstPage:_,goToLastPage:g,goToPage:f,hasNextPage:d,hasPreviousPage:u,nextPage:p,offset:c,pageSize:i,previousPage:m,setPageSize:h,totalPages:o}}function C(e,t){return e.slice(t.offset,t.offset+t.pageSize)}export{x as CursorPagination,C as getOffsetPaginatedSlice,S as useOffsetPagination};
|
package/dist/progress.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ type Props = SvgAttributes & {
|
|
|
26
26
|
strokeWidth?: StrokeWidth;
|
|
27
27
|
/**
|
|
28
28
|
* The current value of the progress bar.
|
|
29
|
-
* This attribute specifies how much of the task
|
|
30
|
-
* It must be a valid floating point number between 0 and max
|
|
29
|
+
* This attribute specifies how much of the task is complete.
|
|
30
|
+
* It must be a valid floating point number between 0 and `max`, or, when `max` is omitted, between 0 and 100.
|
|
31
31
|
* If set to `"indeterminate"`, the progress bar is considered indeterminate.
|
|
32
32
|
*
|
|
33
33
|
* @default 0
|
|
@@ -159,8 +159,8 @@ type RootProps = ComponentProps<"div"> & {
|
|
|
159
159
|
max?: number | undefined;
|
|
160
160
|
/**
|
|
161
161
|
* The current value of the progress bar.
|
|
162
|
-
* This attribute specifies how much of the task
|
|
163
|
-
* It must be a valid floating point number between 0 and max
|
|
162
|
+
* This attribute specifies how much of the task is complete.
|
|
163
|
+
* It must be a valid floating point number between 0 and `max`, or, when `max` is omitted, between 0 and 100.
|
|
164
164
|
* If set to `"indeterminate"`, the progress bar is considered indeterminate. (for now there is no visual difference than 0)
|
|
165
165
|
*
|
|
166
166
|
* @default 0
|
package/dist/qr-code.d.ts
CHANGED
|
@@ -143,8 +143,8 @@ declare const Overlay: ({ asChild, className, ref, ...props }: QrCodeOverlayProp
|
|
|
143
143
|
/**
|
|
144
144
|
* A QR code. Compose `QrCode.Root` with a `QrCode.Frame` wrapping a
|
|
145
145
|
* `QrCode.Pattern`, and optionally a `QrCode.Overlay` for a centered logo.
|
|
146
|
-
*
|
|
147
|
-
*
|
|
146
|
+
* The dependency-free `uqr` library encodes `value`; mantle owns the render
|
|
147
|
+
* logic (no headless runtime).
|
|
148
148
|
*
|
|
149
149
|
* `Root` and `Overlay` support the `asChild` prop for polymorphic composition.
|
|
150
150
|
* `Frame` and `Pattern` render fixed SVG elements and do not.
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
import { n as WithAsChild } from "./as-child-D23mGo4f.js";
|
|
2
|
+
import { t as WithDataSlot } from "./data-slot-CChfb_cv.js";
|
|
3
|
+
import { r as ButtonProps } from "./button-PoIujPMX.js";
|
|
4
|
+
import { ComponentProps, ReactNode, Ref } from "react";
|
|
5
|
+
//#region src/components/sandbar/sandbar.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* The imperative surface of a {@link Sandbar}, exposed via `Sandbar.Root`'s
|
|
8
|
+
* `handleRef` prop. `shake()` is a compound feedback action — it wiggles the
|
|
9
|
+
* panel (skipped under `prefers-reduced-motion`) and always pushes an
|
|
10
|
+
* assertive live-region announcement, so blocked navigation is perceivable
|
|
11
|
+
* with or without motion.
|
|
12
|
+
*
|
|
13
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarhandle
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const sandbarHandle = useRef<SandbarHandle>(null);
|
|
18
|
+
*
|
|
19
|
+
* // hand this to your router's guard — it owns the blocking, the bar reacts
|
|
20
|
+
* const onNavigationBlocked = () => sandbarHandle.current?.shake();
|
|
21
|
+
*
|
|
22
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
23
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
24
|
+
* <Sandbar.Actions>
|
|
25
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
26
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
27
|
+
* </Sandbar.Actions>
|
|
28
|
+
* </Sandbar.Root>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
type SandbarHandle = {
|
|
32
|
+
/**
|
|
33
|
+
* Signal that an action (usually navigation) was blocked because changes
|
|
34
|
+
* are still pending: wiggles the panel and announces assertively.
|
|
35
|
+
*
|
|
36
|
+
* The default announcement is save-flavored; pass `announcement` for other
|
|
37
|
+
* intents (e.g. "Publish or discard your pending items before leaving.").
|
|
38
|
+
*/
|
|
39
|
+
shake: (options?: {
|
|
40
|
+
announcement?: string;
|
|
41
|
+
}) => void;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Props for `Sandbar.Root`. Extends `<div>` props — which target the visible
|
|
45
|
+
* panel, not the fixed-position wrapper — with the controlled `open` flag and
|
|
46
|
+
* the imperative `handleRef`. Deliberately no `asChild`: Root renders four
|
|
47
|
+
* nodes, so single-child polymorphism has no coherent meaning.
|
|
48
|
+
*
|
|
49
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarroot
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```tsx
|
|
53
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
54
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
55
|
+
* <Sandbar.Actions>
|
|
56
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
57
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
58
|
+
* </Sandbar.Actions>
|
|
59
|
+
* </Sandbar.Root>
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
type SandbarRootProps = ComponentProps<"div"> & WithDataSlot & {
|
|
63
|
+
/**
|
|
64
|
+
* Whether the sandbar is showing. Controlled-only — the pending state
|
|
65
|
+
* (e.g. a form's dirty flag) lives in your app, not in the component.
|
|
66
|
+
*
|
|
67
|
+
* `Sandbar.Root` must stay mounted and be toggled with `open`, never
|
|
68
|
+
* conditionally mounted (`{isDirty && <Sandbar.Root …>}`). The
|
|
69
|
+
* screen-reader announcement only works when the internal live regions
|
|
70
|
+
* exist in the tree before the message appears. The exit animation needs
|
|
71
|
+
* the panel alive to play. A Root that mounts with `open` already true
|
|
72
|
+
* does not announce (mounting is not a live-region change).
|
|
73
|
+
*/
|
|
74
|
+
open: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Receives the imperative {@link SandbarHandle}. Kept separate from `ref`
|
|
77
|
+
* (which stays the panel's DOM element, like every other mantle part) —
|
|
78
|
+
* wire it to your navigation guard's blocked callback.
|
|
79
|
+
*/
|
|
80
|
+
handleRef?: Ref<SandbarHandle>;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* The always-mounted shell of the sandbar. Renders a private viewport-fixed
|
|
84
|
+
* wrapper, two persistent visually-hidden live regions (a polite `status`
|
|
85
|
+
* announcer and an assertive `alert` announcer), and the visible panel — a
|
|
86
|
+
* floating island with `role="group"`, named by `Sandbar.Message` via
|
|
87
|
+
* `aria-labelledby` (a consumer `aria-label` wins when passed).
|
|
88
|
+
*
|
|
89
|
+
* The panel is an `invert-theme` island: it renders in the opposite theme of
|
|
90
|
+
* the page (light ⇄ dark, light-high-contrast ⇄ dark-high-contrast), and so
|
|
91
|
+
* does everything composed inside it — the blessed buttons and any custom
|
|
92
|
+
* children.
|
|
93
|
+
*
|
|
94
|
+
* `ref`, `className`, and all other props target the panel. Render it in
|
|
95
|
+
* place at the end of the form it saves — `position: fixed` changes paint
|
|
96
|
+
* position, not tab order, so Tab from the last field lands on the actions.
|
|
97
|
+
* Escape is intentionally inert (a one-keypress discard would destroy data).
|
|
98
|
+
*
|
|
99
|
+
* Data attributes stamped on the panel:
|
|
100
|
+
*
|
|
101
|
+
* | Data Attribute | Value | Description |
|
|
102
|
+
* | -------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
103
|
+
* | `data-slot` | `"sandbar"` | Stable styling hook for the panel. Survives `className` overrides. |
|
|
104
|
+
* | `data-state` | `"open"` \| `"closed"` | `"open"` only at the resting open pose — `"closed"` covers the pre-enter frame and the exit transition, which is what drives them. |
|
|
105
|
+
*
|
|
106
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarroot
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```tsx
|
|
110
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
111
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
112
|
+
* <Sandbar.Actions>
|
|
113
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
114
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
115
|
+
* </Sandbar.Actions>
|
|
116
|
+
* </Sandbar.Root>
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
declare const Root: ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, children, className, "data-slot": dataSlot, handleRef, onTransitionEnd, open, ref, ...props }: SandbarRootProps) => import("react").JSX.Element;
|
|
120
|
+
/**
|
|
121
|
+
* Props for `Sandbar.Message`. Extends `<p>` props with `asChild`. An `id` is
|
|
122
|
+
* generated when none is passed — it is what `Sandbar.Root` points
|
|
123
|
+
* `aria-labelledby` at, so passing your own keeps that wiring intact.
|
|
124
|
+
*
|
|
125
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarmessage
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```tsx
|
|
129
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
130
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
131
|
+
* <Sandbar.Actions>
|
|
132
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
133
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
134
|
+
* </Sandbar.Actions>
|
|
135
|
+
* </Sandbar.Root>
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
type SandbarMessageProps = ComponentProps<"p"> & WithAsChild & WithDataSlot;
|
|
139
|
+
/**
|
|
140
|
+
* The visible pending-state text (e.g. "You have unsaved changes"). A plain
|
|
141
|
+
* paragraph: `Sandbar.Root`'s persistent announcer owns the live-region
|
|
142
|
+
* announcement, not this node. The panel's accessible name points here via
|
|
143
|
+
* `aria-labelledby`, so the group name always matches the visible text.
|
|
144
|
+
*
|
|
145
|
+
* Data attributes:
|
|
146
|
+
*
|
|
147
|
+
* | Data Attribute | Value | Description |
|
|
148
|
+
* | -------------- | -------------------- | ----------------------------------------------------------------- |
|
|
149
|
+
* | `data-slot` | `"sandbar-message"` | Stable styling hook. Survives `className` overrides and `asChild`. |
|
|
150
|
+
*
|
|
151
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarmessage
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```tsx
|
|
155
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
156
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
157
|
+
* <Sandbar.Actions>
|
|
158
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
159
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
160
|
+
* </Sandbar.Actions>
|
|
161
|
+
* </Sandbar.Root>
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
declare const Message: ({ asChild, className, "data-slot": dataSlot, id: propId, ref, ...props }: SandbarMessageProps) => import("react").JSX.Element;
|
|
165
|
+
/**
|
|
166
|
+
* Props for `Sandbar.Actions`. Extends `<div>` props with `asChild`.
|
|
167
|
+
*
|
|
168
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbaractions
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```tsx
|
|
172
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
173
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
174
|
+
* <Sandbar.Actions>
|
|
175
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
176
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
177
|
+
* </Sandbar.Actions>
|
|
178
|
+
* </Sandbar.Root>
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
type SandbarActionsProps = ComponentProps<"div"> & WithAsChild & WithDataSlot;
|
|
182
|
+
/**
|
|
183
|
+
* The action-button row. A plain flex container — deliberately not
|
|
184
|
+
* `role="toolbar"`, which the ARIA APG reserves for 3+ controls with roving
|
|
185
|
+
* tab stops.
|
|
186
|
+
*
|
|
187
|
+
* Data attributes:
|
|
188
|
+
*
|
|
189
|
+
* | Data Attribute | Value | Description |
|
|
190
|
+
* | -------------- | -------------------- | ----------------------------------------------------------------- |
|
|
191
|
+
* | `data-slot` | `"sandbar-actions"` | Stable styling hook. Survives `className` overrides and `asChild`. |
|
|
192
|
+
*
|
|
193
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbaractions
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```tsx
|
|
197
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
198
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
199
|
+
* <Sandbar.Actions>
|
|
200
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
201
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
202
|
+
* </Sandbar.Actions>
|
|
203
|
+
* </Sandbar.Root>
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
declare const Actions: ({ asChild, className, "data-slot": dataSlot, ...props }: SandbarActionsProps) => import("react").JSX.Element;
|
|
207
|
+
/**
|
|
208
|
+
* Props for `Sandbar.SaveButton`. Extends `Button`'s props — including
|
|
209
|
+
* `asChild` and `isLoading` — but makes `appearance` and `intent` optional
|
|
210
|
+
* with save-flavored defaults, and `children` required.
|
|
211
|
+
*
|
|
212
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarsavebutton
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```tsx
|
|
216
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
217
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
218
|
+
* <Sandbar.Actions>
|
|
219
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
220
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
221
|
+
* </Sandbar.Actions>
|
|
222
|
+
* </Sandbar.Root>
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
type SandbarSaveButtonProps = Omit<ButtonProps, "appearance" | "children" | "intent"> & WithDataSlot & {
|
|
226
|
+
/**
|
|
227
|
+
* The visual style of the button.
|
|
228
|
+
* @default "filled"
|
|
229
|
+
*/
|
|
230
|
+
appearance?: ButtonProps["appearance"];
|
|
231
|
+
/**
|
|
232
|
+
* The tone of the button.
|
|
233
|
+
* @default "neutral"
|
|
234
|
+
*/
|
|
235
|
+
intent?: ButtonProps["intent"];
|
|
236
|
+
/**
|
|
237
|
+
* The visible action label (e.g. "Save"). Required — action labels are app
|
|
238
|
+
* voice and must be visible at the call site.
|
|
239
|
+
*/
|
|
240
|
+
children: ReactNode;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* The primary (save) action. A mantle `Button` defaulting to the system
|
|
244
|
+
* primary style (`appearance="filled" intent="neutral"`), wired into
|
|
245
|
+
* `Sandbar.Root`: while `isLoading`, Root announces "Saving changes…" via the
|
|
246
|
+
* polite live region and catches the focus drop caused by the button going
|
|
247
|
+
* natively disabled while focused.
|
|
248
|
+
*
|
|
249
|
+
* Defaults to `type="button"`; pass `type="submit"` when rendered inside the
|
|
250
|
+
* form it saves. Announce the save's resolution yourself (e.g. a success
|
|
251
|
+
* Toast) — the bar exits silently.
|
|
252
|
+
*
|
|
253
|
+
* Data attributes:
|
|
254
|
+
*
|
|
255
|
+
* | Data Attribute | Value | Description |
|
|
256
|
+
* | ----------------- | ------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
257
|
+
* | `data-slot` | `"sandbar-save-button"` | Stable styling hook. Replaces `Button`'s own `data-slot="button"`, as every Button wrapper does. |
|
|
258
|
+
* | `data-appearance` | the resolved `appearance` | Stamped by `Button`. |
|
|
259
|
+
* | `data-intent` | the resolved `intent` | Stamped by `Button`. |
|
|
260
|
+
* | `data-size` | the resolved `size` | Stamped by `Button`; absent for `appearance="link"`. |
|
|
261
|
+
* | `data-disabled` | `"true"` \| `"false"` | Stamped by `Button`. Value-based, not presence-based — match on the value. |
|
|
262
|
+
* | `data-loading` | `"true"` \| `"false"` | Stamped by `Button`, tracking `isLoading`. Value-based, not presence-based. |
|
|
263
|
+
*
|
|
264
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarsavebutton
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```tsx
|
|
268
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
269
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
270
|
+
* <Sandbar.Actions>
|
|
271
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
272
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
273
|
+
* </Sandbar.Actions>
|
|
274
|
+
* </Sandbar.Root>
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
declare const SaveButton: ({ appearance, children, "data-slot": dataSlot, intent, isLoading, ref, ...props }: SandbarSaveButtonProps) => import("react").JSX.Element;
|
|
278
|
+
/**
|
|
279
|
+
* Props for `Sandbar.DiscardButton`. Extends `Button`'s props — including
|
|
280
|
+
* `asChild` — but makes `appearance` and `intent` optional with
|
|
281
|
+
* secondary-action defaults, and `children` required.
|
|
282
|
+
*
|
|
283
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbardiscardbutton
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```tsx
|
|
287
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
288
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
289
|
+
* <Sandbar.Actions>
|
|
290
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
291
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
292
|
+
* </Sandbar.Actions>
|
|
293
|
+
* </Sandbar.Root>
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
type SandbarDiscardButtonProps = Omit<ButtonProps, "appearance" | "children" | "intent"> & WithDataSlot & {
|
|
297
|
+
/**
|
|
298
|
+
* The visual style of the button.
|
|
299
|
+
* @default "outlined"
|
|
300
|
+
*/
|
|
301
|
+
appearance?: ButtonProps["appearance"];
|
|
302
|
+
/**
|
|
303
|
+
* The tone of the button.
|
|
304
|
+
* @default "neutral"
|
|
305
|
+
*/
|
|
306
|
+
intent?: ButtonProps["intent"];
|
|
307
|
+
/**
|
|
308
|
+
* The visible action label (e.g. "Discard"). Required — action labels are
|
|
309
|
+
* app voice and must be visible at the call site.
|
|
310
|
+
*/
|
|
311
|
+
children: ReactNode;
|
|
312
|
+
};
|
|
313
|
+
/**
|
|
314
|
+
* The secondary (discard/reset) action. A mantle `Button` defaulting to
|
|
315
|
+
* `appearance="outlined" intent="neutral"`. Discard is destructive-ish: for
|
|
316
|
+
* large forms, consider confirming before discarding.
|
|
317
|
+
*
|
|
318
|
+
* Data attributes:
|
|
319
|
+
*
|
|
320
|
+
* | Data Attribute | Value | Description |
|
|
321
|
+
* | ----------------- | -------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
322
|
+
* | `data-slot` | `"sandbar-discard-button"` | Stable styling hook. Replaces `Button`'s own `data-slot="button"`, as every Button wrapper does. |
|
|
323
|
+
* | `data-appearance` | the resolved `appearance` | Stamped by `Button`. |
|
|
324
|
+
* | `data-intent` | the resolved `intent` | Stamped by `Button`. |
|
|
325
|
+
* | `data-size` | the resolved `size` | Stamped by `Button`; absent for `appearance="link"`. |
|
|
326
|
+
* | `data-disabled` | `"true"` \| `"false"` | Stamped by `Button`. Value-based, not presence-based — match on the value. |
|
|
327
|
+
* | `data-loading` | `"true"` \| `"false"` | Stamped by `Button`, tracking `isLoading`. Value-based, not presence-based. |
|
|
328
|
+
*
|
|
329
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbardiscardbutton
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```tsx
|
|
333
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
334
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
335
|
+
* <Sandbar.Actions>
|
|
336
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
337
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
338
|
+
* </Sandbar.Actions>
|
|
339
|
+
* </Sandbar.Root>
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
declare const DiscardButton: ({ appearance, children, "data-slot": dataSlot, intent, ...props }: SandbarDiscardButtonProps) => import("react").JSX.Element;
|
|
343
|
+
/**
|
|
344
|
+
* A persistent, decision-bearing bar that floats near the bottom edge of the
|
|
345
|
+
* viewport. It surfaces pending state — primarily a form's unsaved ("dirty")
|
|
346
|
+
* changes — and stays until the user resolves it. Toast announces something
|
|
347
|
+
* that already happened and leaves on its own; a Sandbar waits for the user's
|
|
348
|
+
* decision. When the message needs no decision, reach for Toast or Alert.
|
|
349
|
+
*
|
|
350
|
+
* The name: a sandbar is a bar that blocks navigation — exactly what this
|
|
351
|
+
* component does while changes are pending. Pair it with your app's
|
|
352
|
+
* navigation guard and call `shake()` on the {@link SandbarHandle} when a
|
|
353
|
+
* navigation attempt is blocked.
|
|
354
|
+
*
|
|
355
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* Composition:
|
|
359
|
+
* ```
|
|
360
|
+
* Sandbar.Root
|
|
361
|
+
* ├── Sandbar.Message
|
|
362
|
+
* └── Sandbar.Actions
|
|
363
|
+
* ├── Sandbar.DiscardButton
|
|
364
|
+
* └── Sandbar.SaveButton
|
|
365
|
+
* ```
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```tsx
|
|
369
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
370
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
371
|
+
* <Sandbar.Actions>
|
|
372
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
373
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
374
|
+
* </Sandbar.Actions>
|
|
375
|
+
* </Sandbar.Root>
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
declare const Sandbar: {
|
|
379
|
+
/**
|
|
380
|
+
* The always-mounted shell: fixed positioning, persistent live-region
|
|
381
|
+
* announcers, presence-managed panel. Toggle with the controlled `open`
|
|
382
|
+
* prop — never conditionally mount it.
|
|
383
|
+
*
|
|
384
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarroot
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```tsx
|
|
388
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
389
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
390
|
+
* <Sandbar.Actions>
|
|
391
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
392
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
393
|
+
* </Sandbar.Actions>
|
|
394
|
+
* </Sandbar.Root>
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
readonly Root: typeof Root;
|
|
398
|
+
/**
|
|
399
|
+
* The visible pending-state text. Also names the panel (via
|
|
400
|
+
* `aria-labelledby`) and feeds the polite screen-reader announcement.
|
|
401
|
+
*
|
|
402
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarmessage
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```tsx
|
|
406
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
407
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
408
|
+
* <Sandbar.Actions>
|
|
409
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
410
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
411
|
+
* </Sandbar.Actions>
|
|
412
|
+
* </Sandbar.Root>
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
readonly Message: typeof Message;
|
|
416
|
+
/**
|
|
417
|
+
* The action-button row. Holds `Sandbar.DiscardButton` and
|
|
418
|
+
* `Sandbar.SaveButton`, or your own composed `Button`s.
|
|
419
|
+
*
|
|
420
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbaractions
|
|
421
|
+
*
|
|
422
|
+
* @example
|
|
423
|
+
* ```tsx
|
|
424
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
425
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
426
|
+
* <Sandbar.Actions>
|
|
427
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
428
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
429
|
+
* </Sandbar.Actions>
|
|
430
|
+
* </Sandbar.Root>
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
readonly Actions: typeof Actions;
|
|
434
|
+
/**
|
|
435
|
+
* The primary (save) action — announces "Saving changes…" and keeps focus
|
|
436
|
+
* from being lost while the save is pending.
|
|
437
|
+
*
|
|
438
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbarsavebutton
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```tsx
|
|
442
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
443
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
444
|
+
* <Sandbar.Actions>
|
|
445
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
446
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
447
|
+
* </Sandbar.Actions>
|
|
448
|
+
* </Sandbar.Root>
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
readonly SaveButton: typeof SaveButton;
|
|
452
|
+
/**
|
|
453
|
+
* The secondary (discard/reset) action.
|
|
454
|
+
*
|
|
455
|
+
* @see https://mantle.ngrok.com/components/feedback/sandbar#sandbardiscardbutton
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* ```tsx
|
|
459
|
+
* <Sandbar.Root open={isDirty} handleRef={sandbarHandle}>
|
|
460
|
+
* <Sandbar.Message>You have unsaved changes</Sandbar.Message>
|
|
461
|
+
* <Sandbar.Actions>
|
|
462
|
+
* <Sandbar.DiscardButton onClick={reset}>Discard</Sandbar.DiscardButton>
|
|
463
|
+
* <Sandbar.SaveButton onClick={save} isLoading={isPending}>Save</Sandbar.SaveButton>
|
|
464
|
+
* </Sandbar.Actions>
|
|
465
|
+
* </Sandbar.Root>
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
readonly DiscardButton: typeof DiscardButton;
|
|
469
|
+
};
|
|
470
|
+
//#endregion
|
|
471
|
+
export { Sandbar, type SandbarActionsProps, type SandbarDiscardButtonProps, type SandbarHandle, type SandbarMessageProps, type SandbarRootProps, type SandbarSaveButtonProps };
|
package/dist/sandbar.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./use-isomorphic-layout-effect-DdTRtMY-.js";import{n as t}from"./compose-refs-BdBMM9ga.js";import{t as n}from"./cx-IiQEAKf5.js";import{t as r}from"./data-slot-EM6s2WEU.js";import{t as i}from"./slot-DpNWVci8.js";import{t as a}from"./button-Cnja9Bsr.js";import{t as o}from"./use-prefers-reduced-motion-Bk5zAfHY.js";import{createContext as s,useCallback as c,useContext as l,useEffect as u,useId as d,useImperativeHandle as f,useMemo as p,useRef as m,useState as h}from"react";import g from"tiny-invariant";import{jsx as _,jsxs as v}from"react/jsx-runtime";const y=`Unsaved changes`,b=1e3,ee=[{transform:`translateX(0)`},{transform:`translateX(-8px)`},{transform:`translateX(8px)`},{transform:`translateX(-6px)`},{transform:`translateX(6px)`},{transform:`translateX(-3px)`},{transform:`translateX(3px)`},{transform:`translateX(0)`}];function x(e,t){let n=t.current?`${e}\u00A0`:e;return t.current=!t.current,n}const S=s(null);function C(e){let t=l(S);return g(t,`${e} must be rendered as a child of <Sandbar.Root>.`),t}const w={Root:({"aria-label":e,"aria-labelledby":i,children:a,className:s,"data-slot":l,handleRef:d,onTransitionEnd:g,open:C,ref:w,...T})=>{let[E,D]=h(C?`open`:`closed`),[O,k]=h(``),[A,j]=h(``),[M,N]=h(null),P=m(null),F=m(null),I=t(F,w),L=m(null),R=m(void 0),z=m(void 0),B=m(void 0),te=m(!1),ne=m(!1),V=m(!1),H=m(null),U=m(null),W=m(C),G=m(E),K=c(e=>{k(x(e,te)),window.clearTimeout(R.current),R.current=window.setTimeout(()=>{k(``)},b)},[]),q=c(e=>{j(x(e,ne)),window.clearTimeout(z.current),z.current=window.setTimeout(()=>{j(``)},b)},[]),J=c(e=>(N(e),()=>{N(t=>t===e?null:t)}),[]),Y=c(({button:e,isSaving:t})=>{let n=V.current;if(V.current=t,!t||n||(K(`Saving changes…`),e==null||!e.matches(`:disabled`)))return;let r=document.activeElement,i=r===e,a=r===document.body&&U.current===e;(i||a)&&F.current?.focus({preventScroll:!0})},[K]),X=c(e=>{q(e?.announcement??`You have unsaved changes. Save or discard them before leaving.`);let t=F.current;if(t==null||o()||typeof t.animate!=`function`)return;L.current!=null&&(L.current.cancel(),L.current=null);let n=t.animate(ee,{duration:400,easing:`ease-in-out`});n.onfinish=()=>{L.current=null},L.current=n},[q]);f(d,()=>({shake:X}),[X]);let re=p(()=>({announceAssertive:q,registerMessage:J,reportSaving:Y}),[q,J,Y]);u(()=>{D(C?e=>e===`open`||e===`opening`?e:e===`closing`?`open`:`opening`:e=>e===`closed`?e:e===`opening`?`closed`:`closing`)},[C]),u(()=>{if(E!==`opening`)return;let e=window.requestAnimationFrame(()=>{e=window.requestAnimationFrame(()=>{D(e=>e===`opening`?`open`:e)})});return()=>{window.cancelAnimationFrame(e)}},[E]),u(()=>{if(E!==`closing`)return;let e=window.setTimeout(()=>{D(`closed`)},400);return()=>{window.clearTimeout(e)}},[E]),u(()=>{let t=W.current;if(W.current=C,!C){t&&(window.clearTimeout(R.current),k(``));return}if(t)return;let n=(F.current?.querySelector(`[data-slot~="sandbar-message"]`))?.textContent?.trim();K(n||e||y)},[K,e,C]),u(()=>{let e=G.current;if(G.current=E,E!==`closed`||e!==`closing`)return;let t=P.current,n=document.activeElement,r=t!=null&&n instanceof HTMLElement&&t.contains(n),i=n===document.body&&U.current!=null,a=H.current;H.current=null,U.current=null,!(!r&&!i)&&(a==null||!a.isConnected||a.matches(`:disabled`)||typeof a.checkVisibility==`function`&&!a.checkVisibility()||a.focus())},[E]),u(()=>()=>{window.clearTimeout(R.current),window.clearTimeout(z.current),B.current!=null&&window.cancelAnimationFrame(B.current),L.current?.cancel()},[]);let ie=e=>{let t=P.current,n=e.relatedTarget;t!=null&&(n==null||!t.contains(n))&&n instanceof HTMLElement&&(H.current=n),e.target instanceof HTMLElement&&(U.current=e.target)},Z=e=>{if(e.relatedTarget!=null){let t=P.current;(t==null||!t.contains(e.relatedTarget))&&(U.current=null);return}let t=e.target;t instanceof HTMLElement&&(B.current!=null&&window.cancelAnimationFrame(B.current),B.current=window.requestAnimationFrame(()=>{B.current=void 0;let e=F.current;e==null||e.hidden||(!t.isConnected||t.matches(`:disabled`)||t.hidden)&&document.activeElement===document.body&&e.focus({preventScroll:!0})}))},ae=e=>{g?.(e),e.target===e.currentTarget&&(e.propertyName===`opacity`||e.propertyName===`translate`)&&D(e=>e===`closing`?`closed`:e)},Q=i??(e==null&&M!=null?M:void 0),oe=Q==null?e??y:e,$=E===`closed`,se=$||E===`closing`;return _(S.Provider,{value:re,children:v(`div`,{className:`pointer-events-none fixed inset-x-0 bottom-4 z-50 flex justify-center px-4 sm:bottom-10`,onBlur:Z,onFocus:ie,ref:P,children:[_(`div`,{"aria-atomic":`true`,"aria-live":`polite`,className:`sr-only`,role:`status`,children:O}),_(`div`,{className:`sr-only`,role:`alert`,children:A}),_(`div`,{className:n(`invert-theme pointer-events-auto flex w-fit max-w-full flex-wrap items-center justify-center gap-x-4 gap-y-2 rounded-2xl border border-transparent bg-base px-4 py-3 text-strong shadow-lg`,`max-h-[60svh] overflow-y-auto`,`focus:outline-hidden`,`[&[hidden]]:hidden`,`transition-[opacity,translate] ease-out`,`data-state-open:duration-400`,`data-state-closed:pointer-events-none data-state-closed:opacity-0 data-state-closed:duration-200 motion-safe:data-state-closed:translate-y-[calc(100%+2.5rem)]`,`data-state-closed:ease-[cubic-bezier(0.4,0,1,1),cubic-bezier(0,0,0.2,1)]`,s),...T,"aria-label":oe,"aria-labelledby":Q,"data-slot":r(l,`sandbar`),"data-state":E===`open`?`open`:`closed`,hidden:$?!0:void 0,inert:se?!0:void 0,onTransitionEnd:ae,ref:I,role:`group`,tabIndex:-1,children:a})]})})},Message:({asChild:a,className:o,"data-slot":s,id:c,ref:l,...u})=>{let{registerMessage:f}=C(`Sandbar.Message`),p=d(),h=c??p,g=m(null),v=t(g,l);return e(()=>f(g.current?.id||h),[h,f]),_(a?i:`p`,{className:n(`text-sm font-sans`,o),id:h,...u,"data-slot":r(s,`sandbar-message`),ref:v})},Actions:({asChild:e,className:t,"data-slot":a,...o})=>_(e?i:`div`,{className:n(`flex flex-wrap items-center justify-center gap-2`,t),...o,"data-slot":r(a,`sandbar-actions`)}),SaveButton:({appearance:n=`filled`,children:i,"data-slot":o,intent:s=`neutral`,isLoading:c=!1,ref:l,...u})=>{let{reportSaving:d}=C(`Sandbar.SaveButton`),f=m(null),p=t(f,l);return e(()=>{d({button:f.current,isSaving:!!c})},[c,d]),e(()=>()=>{d({button:null,isSaving:!1})},[d]),_(a,{appearance:n,intent:s,isLoading:c,ref:p,...u,"data-slot":r(o,`sandbar-save-button`),children:i})},DiscardButton:({appearance:e=`outlined`,children:t,"data-slot":n,intent:i=`neutral`,...o})=>_(a,{appearance:e,intent:i,...o,"data-slot":r(n,`sandbar-discard-button`),children:t})};export{w as Sandbar};
|
|
@@ -3,13 +3,11 @@ import { ComponentProps, MouseEventHandler } from "react";
|
|
|
3
3
|
//#region src/components/sandboxed-on-click/sandboxed-on-click.d.ts
|
|
4
4
|
type BaseProps = {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* control whether or not the default behavior of the click event should be
|
|
9
|
-
* allowed.
|
|
6
|
+
* Whether the click keeps its default behavior. When `false`, the `onClick`
|
|
7
|
+
* handler calls `event.preventDefault()`; propagation stops either way.
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Set it to `true` for links or buttons that must navigate or run an action
|
|
10
|
+
* on click.
|
|
13
11
|
*
|
|
14
12
|
* @default false
|
|
15
13
|
*/
|