@box-kite/react 1.0.0 → 2.0.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/.claude/rules/box-kite-rules.md +23 -1
- package/.claude/skills/box-kite/SKILL.md +134 -427
- package/.claude/skills/box-kite/references/components.md +235 -0
- package/.claude/skills/box-kite/references/extending.md +42 -0
- package/.claude/skills/box-kite/references/patterns.md +27 -0
- package/.claude/skills/box-kite/references/styling.md +222 -0
- package/.cursor/rules/box-kite.mdc +73 -0
- package/AGENTS.md +102 -0
- package/BOX_AI_CONTEXT.md +1124 -153
- package/BOX_KITE_AI_CONTEXT.md +1124 -153
- package/README.md +36 -2
- package/a11y.cjs +1 -1
- package/a11y.d.ts +1 -1
- package/a11y.mjs +3 -2
- package/anchor.cjs +1 -0
- package/anchor.d.ts +14 -0
- package/anchor.mjs +3 -0
- package/behavior.cjs +1 -1
- package/behavior.mjs +118 -136
- package/box.cjs +1 -1
- package/box.mjs +1 -1
- package/client.cjs +1 -1
- package/client.mjs +15 -14
- package/components/accordion.cjs +1 -0
- package/components/accordion.d.ts +114 -0
- package/components/accordion.mjs +251 -0
- package/components/button.cjs +1 -1
- package/components/button.d.ts +3 -0
- package/components/button.mjs +1 -1
- package/components/chart.cjs +1 -1
- package/components/chart.d.ts +2 -1
- package/components/chart.mjs +159 -184
- package/components/checkbox.cjs +1 -1
- package/components/checkbox.d.ts +8 -0
- package/components/checkbox.mjs +1 -1
- package/components/combobox/comboboxChips.d.ts +21 -0
- package/components/combobox/comboboxListbox.d.ts +47 -0
- package/components/combobox.cjs +1 -0
- package/components/combobox.d.ts +121 -0
- package/components/combobox.mjs +563 -0
- package/components/dataGrid/components/dataGridAggregateValue.d.ts +10 -0
- package/components/dataGrid/components/dataGridCell.d.ts +2 -1
- package/components/dataGrid/components/dataGridCellEditor.d.ts +15 -0
- package/components/dataGrid/components/dataGridCellPlaceholder.d.ts +14 -0
- package/components/dataGrid/components/dataGridCellTree.d.ts +16 -0
- package/components/dataGrid/components/dataGridDataSourceError.d.ts +12 -0
- package/components/dataGrid/components/dataGridExport.d.ts +10 -0
- package/components/dataGrid/components/dataGridFooter.d.ts +11 -0
- package/components/dataGrid/contracts/dataGridContract.d.ts +474 -16
- package/components/dataGrid/export/gridExport.d.ts +10 -0
- package/components/dataGrid/gridNavigationContext.d.ts +9 -1
- package/components/dataGrid/models/aggregateCellModel.d.ts +34 -0
- package/components/dataGrid/models/aggregationModel.d.ts +32 -0
- package/components/dataGrid/models/cellKeys.d.ts +11 -0
- package/components/dataGrid/models/cellModel.d.ts +27 -1
- package/components/dataGrid/models/columnModel.d.ts +12 -2
- package/components/dataGrid/models/dataSourceModel.d.ts +177 -0
- package/components/dataGrid/models/editModel.d.ts +77 -0
- package/components/dataGrid/models/exportModel.d.ts +51 -0
- package/components/dataGrid/models/gridModel.d.ts +108 -21
- package/components/dataGrid/models/groupRowCellModel.d.ts +11 -3
- package/components/dataGrid/models/groupRowModel.d.ts +22 -2
- package/components/dataGrid/models/rowModel.d.ts +13 -1
- package/components/dataGrid/models/sourceGroupRowModel.d.ts +45 -0
- package/components/dataGrid/models/sourceLevel.d.ts +71 -0
- package/components/dataGrid/models/sourceRowModel.d.ts +29 -0
- package/components/dataGrid/models/sourceTreeRowModel.d.ts +23 -0
- package/components/dataGrid/models/treeModel.d.ts +94 -0
- package/components/dataGrid/models/treeRow.d.ts +22 -0
- package/components/dataGrid/models/treeRowModel.d.ts +50 -0
- package/components/dataGrid.cjs +1 -1
- package/components/dataGrid.d.ts +4 -2
- package/components/dataGrid.mjs +4484 -2755
- package/components/dialog.cjs +1 -0
- package/components/dialog.d.ts +151 -0
- package/components/dialog.mjs +223 -0
- package/components/dropdown.cjs +1 -1
- package/components/dropdown.d.ts +30 -0
- package/components/dropdown.mjs +256 -244
- package/components/icon.cjs +1 -1
- package/components/icon.d.ts +2 -1
- package/components/icon.mjs +3 -3
- package/components/menu.cjs +1 -0
- package/components/menu.d.ts +234 -0
- package/components/menu.mjs +422 -0
- package/components/overlay.cjs +1 -1
- package/components/overlay.d.ts +30 -26
- package/components/overlay.mjs +45 -75
- package/components/popover.cjs +1 -0
- package/components/popover.d.ts +100 -0
- package/components/popover.mjs +69 -0
- package/components/presence.d.ts +6 -0
- package/components/progress.cjs +1 -0
- package/components/progress.d.ts +47 -0
- package/components/progress.mjs +28 -0
- package/components/radioButton.cjs +1 -1
- package/components/radioButton.d.ts +8 -0
- package/components/radioButton.mjs +1 -1
- package/components/radioGroup.cjs +1 -1
- package/components/radioGroup.d.ts +15 -2
- package/components/radioGroup.mjs +34 -30
- package/components/select.d.ts +9 -0
- package/components/semantics.cjs +1 -1
- package/components/semantics.d.ts +33 -28
- package/components/semantics.mjs +3 -3
- package/components/slider.cjs +1 -0
- package/components/slider.d.ts +98 -0
- package/components/slider.mjs +250 -0
- package/components/svg.cjs +1 -1
- package/components/svg.d.ts +25 -23
- package/components/svg.mjs +3 -3
- package/components/switch.cjs +1 -1
- package/components/switch.d.ts +9 -0
- package/components/switch.mjs +1 -1
- package/components/tabs.cjs +1 -0
- package/components/tabs.d.ts +87 -0
- package/components/tabs.mjs +324 -0
- package/components/textarea.cjs +1 -1
- package/components/textarea.d.ts +14 -0
- package/components/textarea.mjs +1 -1
- package/components/textbox.cjs +1 -1
- package/components/textbox.d.ts +12 -0
- package/components/textbox.mjs +1 -1
- package/components/toaster.cjs +1 -0
- package/components/toaster.d.ts +106 -0
- package/components/toaster.mjs +295 -0
- package/components/tooltip.cjs +1 -1
- package/components/tooltip.d.ts +39 -4
- package/components/tooltip.mjs +56 -54
- package/docs/a11y.md +199 -0
- package/docs/anchor.md +139 -0
- package/docs/components.md +48 -0
- package/docs/index.md +11 -0
- package/docs/props.md +1777 -0
- package/effects.cjs +1 -1
- package/effects.mjs +1 -1
- package/gridExport.cjs +2 -0
- package/gridExport.mjs +244 -0
- package/identity.cjs +1 -0
- package/identity.mjs +12 -0
- package/keyboard.cjs +1 -0
- package/keyboard.mjs +30 -0
- package/layer.cjs +1 -0
- package/layer.mjs +127 -0
- package/motion.cjs +1 -1
- package/motion.mjs +1 -1
- package/package.json +134 -10
- package/placement.cjs +1 -0
- package/placement.mjs +180 -0
- package/platform.cjs +1 -1
- package/platform.mjs +18 -5
- package/react/anchor/useAnchorPosition.d.ts +79 -0
- package/react/dialog/useScrollLock.d.ts +12 -0
- package/react/hooks/usePortalContainer.d.ts +6 -1
- package/react/hooks/useVirtualization.d.ts +24 -35
- package/react/popover/usePopoverLayer.d.ts +87 -0
- package/react-shared.cjs +1 -1
- package/react-shared.mjs +19 -2
- package/rsc.cjs +1 -1
- package/rsc.mjs +1 -1
- package/utils/accordion/accordionUtils.d.ts +48 -0
- package/utils/anchor/anchorUtils.d.ts +79 -0
- package/utils/array/lazyList.d.ts +10 -0
- package/utils/chart/chartPalette.d.ts +15 -0
- package/utils/chart/chartUtils.d.ts +2 -8
- package/utils/combobox/comboboxUtils.d.ts +89 -0
- package/utils/dom/domUtils.d.ts +6 -0
- package/utils/environment/environmentUtils.d.ts +10 -0
- package/utils/export/csvUtils.d.ts +23 -0
- package/utils/export/downloadUtils.d.ts +12 -0
- package/utils/export/xlsxUtils.d.ts +56 -0
- package/utils/export/zipUtils.d.ts +26 -0
- package/utils/keyboard/typeaheadUtils.d.ts +31 -0
- package/utils/menu/menuUtils.d.ts +25 -0
- package/utils/slider/sliderUtils.d.ts +67 -0
- package/utils/tabs/tabsUtils.d.ts +75 -0
- package/utils/toast/toastStore.d.ts +73 -0
- package/utils/virtual/virtualUtils.d.ts +47 -0
- /package/react/{a11y → identity}/useIdentifier.d.ts +0 -0
package/components/icon.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../react-shared.cjs");let t=require("react"),n=require("react/jsx-runtime"),r=require("@box-kite/react");var i=6;function a(a,o){let{children:s,label:c,size:l,props:u,style:d,...f}=a,p=l===void 0?void 0:{width:l,height:l},{className:m,styles:h}=(0,r.useClassNames)({width:i,height:i,...f,...p},{svg:!0});if(!(0,t.isValidElement)(s))throw Error(`[box-kite] <Icon> takes exactly one element — the icon to style. For SVG of your own, use <Svg> from components/svg.`);let g=e.
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../react-shared.cjs");let t=require("react"),n=require("react/jsx-runtime"),r=require("@box-kite/react");var i=6;function a(a,o){let{children:s,label:c,size:l,props:u,style:d,...f}=a,p=l===void 0?void 0:{width:l,height:l},{className:m,styles:h}=(0,r.useClassNames)({width:i,height:i,...f,...p},{svg:!0});if(!(0,t.isValidElement)(s))throw Error(`[box-kite] <Icon> takes exactly one element — the icon to style. For SVG of your own, use <Svg> from components/svg.`);let g=e.s(s.type),_=s.props,v=(g?_.props:s.props)??{},y=g&&_.label!==void 0?{...v,"aria-label":_.label}:v,b={...u,...e.o(c,{...y,...u})},x={...g?{props:{...v,...b}}:b,...d?{style:d}:void 0,ref:o,className:s.props.className?`${s.props.className} ${m}`:m};return(0,n.jsxs)(n.Fragment,{children:[h,(0,t.cloneElement)(s,x)]})}var o=(0,t.forwardRef)(a);o.displayName=`Icon`,exports.default=o;
|
package/components/icon.d.ts
CHANGED
|
@@ -28,7 +28,8 @@ export interface IconProps extends BoxClassNameProps {
|
|
|
28
28
|
label?: string;
|
|
29
29
|
/** Attributes forwarded to the icon's element, over anything the icon writes for itself. */
|
|
30
30
|
props?: TagPropsType<'svg'>;
|
|
31
|
-
/**
|
|
31
|
+
/** Inline styles forwarded to the icon's element. Box props are the way to style it; this is for a
|
|
32
|
+
* value that has to be computed per render, which no class can hold. */
|
|
32
33
|
style?: React.CSSProperties;
|
|
33
34
|
}
|
|
34
35
|
/** Box props on an icon somebody else renders — see `IconProps`. */
|
package/components/icon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as e, s as t } from "../react-shared.mjs";
|
|
2
2
|
import { cloneElement as n, forwardRef as r, isValidElement as i } from "react";
|
|
3
3
|
import { Fragment as a, jsxs as o } from "react/jsx-runtime";
|
|
4
4
|
import { useClassNames as s } from "@box-kite/react";
|
|
@@ -15,12 +15,12 @@ function l(r, l) {
|
|
|
15
15
|
...g
|
|
16
16
|
}, { svg: !0 });
|
|
17
17
|
if (!i(u)) throw Error("[box-kite] <Icon> takes exactly one element — the icon to style. For SVG of your own, use <Svg> from components/svg.");
|
|
18
|
-
let y =
|
|
18
|
+
let y = t(u.type), b = u.props, x = (y ? b.props : u.props) ?? {}, S = y && b.label !== void 0 ? {
|
|
19
19
|
...x,
|
|
20
20
|
"aria-label": b.label
|
|
21
21
|
} : x, C = {
|
|
22
22
|
...p,
|
|
23
|
-
...
|
|
23
|
+
...e(d, {
|
|
24
24
|
...S,
|
|
25
25
|
...p
|
|
26
26
|
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../platform.cjs"),t=require("../effects.cjs"),n=require("../box.cjs"),r=require("../behavior.cjs"),i=require("../identity.cjs"),a=require("../keyboard.cjs"),o=require("../placement.cjs"),s=require("./overlay.cjs"),c=require("../layer.cjs");let l=require("react"),u=require("react/jsx-runtime");var d;(function(e){let t=e.ITEM_SELECTOR=`[role="menuitem"],[role="menuitemcheckbox"],[role="menuitemradio"]`;function n(e,t){return t instanceof Element&&t.closest(`[role="menu"]`)===e}e.owns=n;function r(e){return[...e.querySelectorAll(t)].filter(t=>n(e,t))}e.items=r;function i(e){return e.getAttribute(`aria-disabled`)===`true`}e.isDisabled=i;function o(e){return e.getAttribute(`aria-haspopup`)===`menu`}e.isSubTrigger=o;function s(e,t,n){if(e.length!==0)return e[t<0?n>0?0:e.length-1:((t+n)%e.length+e.length)%e.length]}e.step=s;function c(e,t,n){let r=a.t.target(t,n,e.length,t=>e[t].textContent??``);return r<0?void 0:e[r]}e.typeaheadTarget=c})(d||={});var f=d,p=(0,l.createContext)(null);function m(e){let t=(0,l.useContext)(p);if(!t)throw Error(`<${e}> must be rendered inside a <Menu>.`);return t}function h(e){let[t,n]=(0,l.useState)([]);return{panels:t,registerLayer:(0,l.useCallback)(t=>{n(e=>[...e,t]);let r=e?.(t);return()=>{n(e=>e.filter(e=>e!==t)),r?.()}},[e])}}var g=(0,l.createContext)(null);function _(e){let{trigger:t,children:n,open:i,defaultOpen:a=!1,onOpenChange:o,label:s,labelledBy:d,side:m=`bottom`,align:g=`start`,offset:_=1,flip:y=!0,matchWidth:b=!1,props:x,...S}=e,C=(0,l.useRef)(`first`),w=r.a(e=>{let t=f.items(e),n=C.current===`last`?t[t.length-1]:t[0];C.current=`first`,(n??e).focus()}),{panels:T,registerLayer:E}=h(),D=c.t({haspopup:`menu`,idPrefix:`menu`,id:S.id,open:i,defaultOpen:a,onOpenChange:o,onOpened:w,inside:T}),O=r.a(e=>{(e.key===`ArrowDown`||e.key===`ArrowUp`)&&(e.preventDefault(),C.current=e.key===`ArrowUp`?`last`:`first`,D.isOpen&&D.panelElement?w(D.panelElement):D.setOpen(!0,{reason:`trigger`,event:e}))}),k=(0,l.useMemo)(()=>({ref:D.trigger.ref,props:{...D.trigger.props,onKeyDown:O}}),[O,D.trigger]),A=r.a(e=>D.setOpen(!1,e)),j=(0,l.useMemo)(()=>({open:D.isOpen,closeAll:A,registerLayer:E}),[A,D.isOpen,E]);return(0,u.jsxs)(u.Fragment,{children:[t(k),(0,u.jsx)(p.Provider,{value:j,children:(0,u.jsx)(v,{layer:D,side:m,align:g,offset:_,flip:y,matchWidth:b,label:s,labelledBy:d??(s?void 0:D.triggerId),contentProps:x,...S,children:n})})]})}function v(t){let{layer:i,side:c,align:d,offset:p,flip:h,matchWidth:g,label:_,labelledBy:v,contentProps:y,closeSelf:b,children:x,...S}=t,{platform:C,isOpen:w,id:T,triggerElement:E,setPanel:D}=i,O=m(`Menu`),k=(0,l.useRef)({query:``,at:0}),A=o.t({side:c,align:d,offset:p,flip:h,matchWidth:g,anchor:E}),j={role:`menu`,"aria-label":_,"aria-labelledby":v,tabIndex:-1,onKeyDown:r.a(t=>{let n=t.currentTarget;if(!f.owns(n,t.target))return;let r=f.items(n),i=r.indexOf(document.activeElement),o=e=>{e&&(t.preventDefault(),e.focus())},s=e.u(n)?`ArrowRight`:`ArrowLeft`;switch(t.key){case`ArrowDown`:return o(f.step(r,i,1));case`ArrowUp`:return o(f.step(r,i,-1));case`Home`:return o(r[0]);case`End`:return o(r[r.length-1]);case`Tab`:return O.closeAll({reason:`tab`,event:t});case s:return b?(t.preventDefault(),b({reason:`trigger`,event:t})):void 0}if(a.t.isPrintable(t)){let e=a.t.push(k.current,t.key,t.timeStamp);o(f.typeaheadTarget(r,e,i))}}),onPointerOver:r.a(e=>{let t=e.currentTarget,n=e.target?.closest(f.ITEM_SELECTOR);!n||!f.owns(t,n)||n===document.activeElement||n.focus()}),...y};return C?(0,u.jsx)(n.default,{ref:D,component:`menu`,...S,variant:[S.variant,{topLayer:!0}],...A.layerProps,id:T,props:{popover:`auto`,...j},children:x}):w?(0,u.jsx)(s.default,{contentRef:D,anchor:E,side:c,align:d,offset:p,flip:h,matchWidth:g,component:`menu`,...S,id:T,props:j,children:x}):null}function y(a){let{label:o,children:s,disabled:d,side:g=`end`,align:_=`start`,offset:y=0,flip:b=!0,itemProps:x,props:S,...C}=a,w=m(`Menu.Sub`),T=i.t(`menu-sub`),E=r.a(e=>{(f.items(e)[0]??e).focus()}),D=r.a((e,t)=>{e&&t?.focus()}),{panels:O,registerLayer:k}=h(w.registerLayer),A=c.t({haspopup:`menu`,idPrefix:`menu-sub`,id:C.id,onOpened:E,onClosing:D,inside:O});t.n(()=>{if(!(!A.isOpen||!A.panelElement))return w.registerLayer(A.panelElement)},[A.isOpen,A.panelElement,w.registerLayer]),t.n(()=>{w.open||A.setOpen(!1,{reason:`imperative`})},[w.open,A.setOpen]),t.n(()=>{if(!A.isOpen)return;let t=new AbortController;return document.addEventListener(`focusin`,t=>{e.l(t,[A.panelElement,A.triggerElement])||A.setOpen(!1,{reason:`imperative`,event:t})},{signal:t.signal}),()=>t.abort()},[A.isOpen,A.panelElement,A.triggerElement,A.setOpen]);let j=r.a(t=>{let n=e.u(t.currentTarget)?`ArrowLeft`:`ArrowRight`;d||t.key!==n||(t.preventDefault(),A.setOpen(!0,{reason:`trigger`,event:t}))}),M=r.a(e=>{d||A.setOpen(!0,{reason:`trigger`,event:e})}),N=(0,l.useMemo)(()=>({open:A.isOpen,closeAll:w.closeAll,registerLayer:k}),[A.isOpen,w.closeAll,k]);return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsxs)(n.default,{tag:`button`,ref:d?void 0:A.trigger.ref,component:`menu.item`,variant:{sub:!0},id:T,props:{type:`button`,role:`menuitem`,tabIndex:-1,"aria-disabled":d||void 0,...A.trigger.props,...x,onKeyDown:j,onPointerEnter:M},children:[o,(0,u.jsx)(n.default,{component:`menu.arrow`})]}),!d&&(0,u.jsx)(p.Provider,{value:N,children:(0,u.jsx)(v,{layer:A,side:g,align:_,offset:y,flip:b,matchWidth:!1,labelledBy:T,contentProps:S,closeSelf:e=>A.setOpen(!1,e),...C,children:s})})]})}function b(e){let{onSelect:t,disabled:i,closeOnSelect:a=!0,props:o,children:s,...c}=e,l=m(`Menu.Item`),d=r.a(e=>{i||(t?.(e),a&&l.closeAll({reason:`select`,event:e}))});return(0,u.jsx)(n.default,{tag:`button`,component:`menu.item`,...c,props:{type:`button`,role:`menuitem`,tabIndex:-1,"aria-disabled":i||void 0,...o,onClick:d},children:s})}function x(e){let{checked:t,defaultChecked:i=!1,onCheckedChange:a,disabled:o,closeOnSelect:s=!1,props:c,children:l,...d}=e,f=m(`Menu.CheckboxItem`),[p,h]=r.i({value:t,defaultValue:i,onChange:a}),g=r.a(e=>{o||(h(!p,{reason:`select`,event:e}),s&&f.closeAll({reason:`select`,event:e}))});return(0,u.jsxs)(n.default,{tag:`button`,component:`menu.item`,...d,props:{type:`button`,role:`menuitemcheckbox`,tabIndex:-1,"aria-checked":p,"aria-disabled":o||void 0,...c,onClick:g},children:[(0,u.jsx)(n.default,{component:`menu.indicator`,children:p&&(0,u.jsx)(n.default,{component:`menu.check`})}),l]})}function S(e){let{label:t,value:n,defaultValue:i=``,onValueChange:a,children:o,...s}=e,[c,d]=r.i({value:n,defaultValue:i,onChange:a}),f=r.a((e,t)=>d(e,{reason:`select`,event:t})),p=(0,l.useMemo)(()=>({value:c,select:f}),[c,f]);return(0,u.jsx)(g.Provider,{value:p,children:(0,u.jsx)(w,{label:t,...s,children:o})})}function C(e){let{value:t,disabled:i,closeOnSelect:a=!1,props:o,children:s,...c}=e,d=m(`Menu.RadioItem`),f=(0,l.useContext)(g);if(!f)throw Error(`<Menu.RadioItem> must be rendered inside a <Menu.RadioGroup>.`);let p=f.value===t,h=r.a(e=>{i||(f.select(t,e),a&&d.closeAll({reason:`select`,event:e}))});return(0,u.jsxs)(n.default,{tag:`button`,component:`menu.item`,...c,props:{type:`button`,role:`menuitemradio`,tabIndex:-1,"aria-checked":p,"aria-disabled":i||void 0,...o,onClick:h},children:[(0,u.jsx)(n.default,{component:`menu.indicator`,children:p&&(0,u.jsx)(n.default,{component:`menu.dot`})}),s]})}function w(e){let{label:t,props:r,children:a,...o}=e,s=i.t(`menu-group`);return(0,u.jsxs)(n.default,{component:`menu.group`,...o,props:{role:`group`,"aria-labelledby":t?s:void 0,...r},children:[t&&(0,u.jsx)(n.default,{component:`menu.label`,id:s,props:{role:`presentation`},children:t}),a]})}function T(e){let{props:t,...r}=e;return(0,u.jsx)(n.default,{component:`menu.separator`,...r,props:{role:`separator`,...t}})}b.displayName=`Menu.Item`,x.displayName=`Menu.CheckboxItem`,S.displayName=`Menu.RadioGroup`,C.displayName=`Menu.RadioItem`,w.displayName=`Menu.Group`,T.displayName=`Menu.Separator`,y.displayName=`Menu.Sub`;var E=Object.assign(_,{Item:b,CheckboxItem:x,RadioGroup:S,RadioItem:C,Group:w,Separator:T,Sub:y});_.displayName=`Menu`,exports.default=E;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { BoxProps } from '../box';
|
|
2
|
+
import { ChangeHandler } from '../react/a11y/useControllableState';
|
|
3
|
+
import { PopoverReason, PopoverTrigger as LayerTrigger, PopoverTriggerAttributes as LayerTriggerAttributes } from '../react/popover/usePopoverLayer';
|
|
4
|
+
import { ComponentsAndVariants } from '@box-kite/core/types';
|
|
5
|
+
import { AnchorAlign, AnchorSide } from '../utils/anchor/anchorUtils';
|
|
6
|
+
/**
|
|
7
|
+
* Why the menu opened or closed. The four a layer always reports, plus the two only a menu has: `select`
|
|
8
|
+
* is an item being chosen — the transition a consumer most often wants to tell apart — and `tab` is focus
|
|
9
|
+
* leaving the menu, which APG asks should close it.
|
|
10
|
+
*/
|
|
11
|
+
export type MenuReason = PopoverReason | 'select' | 'tab';
|
|
12
|
+
/** The DOM attributes the trigger has to carry, `onKeyDown` included: the arrow keys open a menu too. */
|
|
13
|
+
export interface MenuTriggerAttributes extends LayerTriggerAttributes<'menu'> {
|
|
14
|
+
onKeyDown(event: React.KeyboardEvent): void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* What the trigger has to carry: spread it onto the control itself, not a wrapper. The `ref` is what the
|
|
18
|
+
* menu is anchored to, so it is not optional decoration.
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* {(trigger) => <Button {...trigger}>Actions</Button>} // a Box component
|
|
22
|
+
* {(trigger) => <button ref={trigger.ref} {...trigger.props}>} // a plain element
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* It must be a **button**: `popovertarget` is what the browser toggles on, and it only reads it off one.
|
|
26
|
+
*/
|
|
27
|
+
export interface MenuTrigger extends Omit<LayerTrigger<'menu'>, 'props'> {
|
|
28
|
+
props: MenuTriggerAttributes;
|
|
29
|
+
}
|
|
30
|
+
type MenuBoxProps<TKey extends keyof ComponentsAndVariants> = Omit<BoxProps<'div', TKey>, 'open' | 'flip'>;
|
|
31
|
+
export interface MenuProps<TKey extends keyof ComponentsAndVariants = 'menu'> extends MenuBoxProps<TKey> {
|
|
32
|
+
/** The trigger, handed the ref and props that wire it to the menu. It has to be a button. */
|
|
33
|
+
trigger: (trigger: MenuTrigger) => React.ReactNode;
|
|
34
|
+
/** The items: `Menu.Item`, `Menu.CheckboxItem`, `Menu.RadioGroup`, `Menu.Group`, `Menu.Separator`, `Menu.Sub`. */
|
|
35
|
+
children?: React.ReactNode;
|
|
36
|
+
/** Controlled open state. Leave it out and the menu owns it. */
|
|
37
|
+
open?: boolean;
|
|
38
|
+
/** Whether it starts open, when the menu owns its own state. */
|
|
39
|
+
defaultOpen?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Fires with the new state and why it changed — `'select'`, `'trigger'`, `'escape'`, `'outside-pointer'`,
|
|
42
|
+
* `'tab'` or `'imperative'`. A close cannot be refused: the browser has already done it by then.
|
|
43
|
+
*/
|
|
44
|
+
onOpenChange?: ChangeHandler<boolean, MenuReason>;
|
|
45
|
+
/** The menu's accessible name. Left out, it takes its trigger's, which is what APG asks for. */
|
|
46
|
+
label?: string;
|
|
47
|
+
/** Names the menu after an element already on the page, instead of `label`. */
|
|
48
|
+
labelledBy?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Which side of the trigger the menu sits on — `top`/`bottom` are the block axis, `start`/`end` the
|
|
51
|
+
* inline one, so a menu beside its trigger mirrors in a right-to-left page. Default `'bottom'`.
|
|
52
|
+
*/
|
|
53
|
+
side?: AnchorSide;
|
|
54
|
+
/** Which of the trigger's edges to line the menu up with along the other axis. Default `'start'`. */
|
|
55
|
+
align?: AnchorAlign;
|
|
56
|
+
/** The gap between trigger and menu, on the ÷4 spacing scale. Default 1 — 4px. */
|
|
57
|
+
offset?: number;
|
|
58
|
+
/** Whether a side with no room may be swapped for its opposite. Default `true`. */
|
|
59
|
+
flip?: boolean;
|
|
60
|
+
/** Whether the menu is at least as wide as its trigger. Default `false` — a menu sizes to its content. */
|
|
61
|
+
matchWidth?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** Every Box prop, on the `<button>` that is one item. */
|
|
64
|
+
export interface MenuItemProps<TKey extends keyof ComponentsAndVariants = 'menu.item'> extends Omit<BoxProps<'button', TKey>, 'disabled' | 'children'> {
|
|
65
|
+
/** The item's content. */
|
|
66
|
+
children?: React.ReactNode;
|
|
67
|
+
/** The item was chosen — by a press, or by Enter or Space, which the browser turns into one. */
|
|
68
|
+
onSelect?(event: React.MouseEvent): void;
|
|
69
|
+
/**
|
|
70
|
+
* Focusable, announced, and not activatable. `aria-disabled` rather than the `disabled` attribute,
|
|
71
|
+
* because APG asks that a disabled item stay reachable: a keyboard user has to be able to find out it
|
|
72
|
+
* is there at all.
|
|
73
|
+
*/
|
|
74
|
+
disabled?: boolean;
|
|
75
|
+
/** Whether choosing it closes the whole menu. Default `true`. */
|
|
76
|
+
closeOnSelect?: boolean;
|
|
77
|
+
}
|
|
78
|
+
/** Every Box prop, on the `<button>` that is one checkbox item. */
|
|
79
|
+
export interface MenuCheckboxItemProps<TKey extends keyof ComponentsAndVariants = 'menu.item'> extends Omit<MenuItemProps<TKey>, 'onSelect' | 'checked'> {
|
|
80
|
+
/** Controlled checked state. Leave it out and the item owns it. */
|
|
81
|
+
checked?: boolean;
|
|
82
|
+
/** Whether it starts checked, when the item owns its own state. */
|
|
83
|
+
defaultChecked?: boolean;
|
|
84
|
+
/** Fires with the new state, and the press behind it. */
|
|
85
|
+
onCheckedChange?: ChangeHandler<boolean, 'select'>;
|
|
86
|
+
/** Whether toggling it closes the menu. Default `false`, so several boxes can be ticked in one visit. */
|
|
87
|
+
closeOnSelect?: boolean;
|
|
88
|
+
}
|
|
89
|
+
/** Every Box prop, on the `role="group"` holding a set of radio items. */
|
|
90
|
+
export interface MenuRadioGroupProps<TKey extends keyof ComponentsAndVariants = 'menu.group'> extends Omit<BoxProps<'div', TKey>, 'children'> {
|
|
91
|
+
/** The radio items. */
|
|
92
|
+
children?: React.ReactNode;
|
|
93
|
+
/** The group's heading. It names the group, so a screen reader reads the items as a set. */
|
|
94
|
+
label?: React.ReactNode;
|
|
95
|
+
/** Controlled value — the `value` of whichever item is checked. */
|
|
96
|
+
value?: string;
|
|
97
|
+
/** The value checked to start with, when the group owns its own state. */
|
|
98
|
+
defaultValue?: string;
|
|
99
|
+
/** Fires with the value chosen, and the press behind it. */
|
|
100
|
+
onValueChange?: ChangeHandler<string, 'select'>;
|
|
101
|
+
}
|
|
102
|
+
/** Every Box prop, on the `<button>` that is one radio item. */
|
|
103
|
+
export interface MenuRadioItemProps<TKey extends keyof ComponentsAndVariants = 'menu.item'> extends Omit<MenuItemProps<TKey>, 'onSelect' | 'checked'> {
|
|
104
|
+
/** What this item stands for, compared against the group's `value`. */
|
|
105
|
+
value: string;
|
|
106
|
+
/** Whether choosing it closes the menu. Default `false`, the way a checkbox item behaves. */
|
|
107
|
+
closeOnSelect?: boolean;
|
|
108
|
+
}
|
|
109
|
+
/** Every Box prop, on the `role="group"` around a titled section of the menu. */
|
|
110
|
+
export interface MenuGroupProps<TKey extends keyof ComponentsAndVariants = 'menu.group'> extends Omit<BoxProps<'div', TKey>, 'children'> {
|
|
111
|
+
/** The items in the section. */
|
|
112
|
+
children?: React.ReactNode;
|
|
113
|
+
/**
|
|
114
|
+
* The section's heading. It names the group rather than standing alone: a heading nothing is labelled by
|
|
115
|
+
* is read as a stray line of text, which is why there is no separate label part.
|
|
116
|
+
*/
|
|
117
|
+
label?: React.ReactNode;
|
|
118
|
+
}
|
|
119
|
+
/** Every Box prop, on the `role="separator"` between two sections. */
|
|
120
|
+
export type MenuSeparatorProps<TKey extends keyof ComponentsAndVariants = 'menu.separator'> = BoxProps<'div', TKey>;
|
|
121
|
+
export interface MenuSubProps<TKey extends keyof ComponentsAndVariants = 'menu'> extends MenuBoxProps<TKey> {
|
|
122
|
+
/** The item's own content — what the submenu is called. */
|
|
123
|
+
label: React.ReactNode;
|
|
124
|
+
/** The submenu's items. */
|
|
125
|
+
children?: React.ReactNode;
|
|
126
|
+
/** Focusable, announced, and it opens nothing. `aria-disabled`, for the reason `Menu.Item` gives. */
|
|
127
|
+
disabled?: boolean;
|
|
128
|
+
/** Which side of its item the submenu sits on. Default `'end'` — beside it, on the side the text runs towards. */
|
|
129
|
+
side?: AnchorSide;
|
|
130
|
+
/** Which of the item's edges to line the submenu up with. Default `'start'`. */
|
|
131
|
+
align?: AnchorAlign;
|
|
132
|
+
/** The gap between item and submenu, on the ÷4 spacing scale. Default 0 — a submenu abuts its menu. */
|
|
133
|
+
offset?: number;
|
|
134
|
+
/** Whether a side with no room may be swapped for its opposite. Default `true`. */
|
|
135
|
+
flip?: boolean;
|
|
136
|
+
/** Attributes for the item that opens it — the submenu's own go in `props`. */
|
|
137
|
+
itemProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A menu button and its menu, on the platform's own Popover API: `<Menu>` is the button's menu, and every
|
|
141
|
+
* `Menu.Sub` inside it is another popover nested in that one.
|
|
142
|
+
*
|
|
143
|
+
* ```tsx
|
|
144
|
+
* <Menu trigger={(t) => <Button {...t}>Actions</Button>}>
|
|
145
|
+
* <Menu.Item onSelect={duplicate}>Duplicate</Menu.Item>
|
|
146
|
+
* <Menu.Item disabled>Move</Menu.Item>
|
|
147
|
+
* <Menu.Separator />
|
|
148
|
+
* <Menu.CheckboxItem checked={compact} onCheckedChange={setCompact}>Compact rows</Menu.CheckboxItem>
|
|
149
|
+
* <Menu.Sub label="Share">
|
|
150
|
+
* <Menu.Item onSelect={copyLink}>Copy link</Menu.Item>
|
|
151
|
+
* </Menu.Sub>
|
|
152
|
+
* </Menu>
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* **What the platform owns**, measured in Chrome 152: the top layer, so the menu paints over every
|
|
156
|
+
* stacking context and outside every clipped ancestor with no portal and no `z-index`; light dismiss, so
|
|
157
|
+
* Escape and a press outside close it — Escape closing the innermost submenu first, one layer per press;
|
|
158
|
+
* the toggle from the trigger, including the press that closes an open menu from its own button; and
|
|
159
|
+
* focus return to whatever had focus before the menu opened. A submenu is a popover nested inside its
|
|
160
|
+
* menu, so opening one leaves its parent open, and closing the menu closes every submenu with it.
|
|
161
|
+
*
|
|
162
|
+
* **What is written here** is the ARIA, the arrow keys, typeahead, and the focus the platform does not
|
|
163
|
+
* move: into the first item on open, and back onto a submenu's own item when that submenu closes — the
|
|
164
|
+
* platform returns focus for the outermost layer only.
|
|
165
|
+
*
|
|
166
|
+
* **The menu is always rendered**, and closed means `display: none`, which is what lets the browser own
|
|
167
|
+
* show and hide; the exit is therefore a CSS transition rather than a `<Presence>`. A menu whose items are
|
|
168
|
+
* expensive should be gated by the consumer: `{open && <Items />}`.
|
|
169
|
+
*
|
|
170
|
+
* **The menu keeps the side it chose when it opened**, since Chrome never re-evaluates
|
|
171
|
+
* `position-try-fallbacks` for an element in the top layer (measured in 152) — so a menu left open while
|
|
172
|
+
* the page scrolls does not flip. Every open picks the right side.
|
|
173
|
+
*
|
|
174
|
+
* Where the browser has no Popover API each layer is an `Overlay` — a portal — with `useDismiss` and
|
|
175
|
+
* `useFocusReturn` supplying what the platform otherwise would.
|
|
176
|
+
*
|
|
177
|
+
* @a11y `role="menu"` on the panel, named by `label`, `labelledBy` or its trigger; `menuitem`,
|
|
178
|
+
* `menuitemcheckbox` and `menuitemradio` on the items, with `aria-checked` on the last two, `role="group"`
|
|
179
|
+
* around a titled section and `role="separator"` between them.
|
|
180
|
+
* @a11y The trigger carries `aria-haspopup="menu"`, `aria-expanded` and `aria-controls`; a submenu's item
|
|
181
|
+
* carries the same three, which is what says it opens another menu.
|
|
182
|
+
* @a11y A disabled item is `aria-disabled` and stays focusable, as APG asks — the `disabled` attribute
|
|
183
|
+
* would take it out of the keyboard's reach and silence it.
|
|
184
|
+
* @keyboard Enter / Space — On the trigger, opens the menu with the first item focused. On an item,
|
|
185
|
+
* chooses it. On a submenu's item, opens the submenu.
|
|
186
|
+
* @keyboard Down / Up — On the trigger, opens the menu at its first or last item. Inside, moves between
|
|
187
|
+
* items and wraps at the ends.
|
|
188
|
+
* @keyboard Home / End — The first or the last item.
|
|
189
|
+
* @keyboard Right / Left — Opens the submenu of the item that has focus, and closes the submenu focus is
|
|
190
|
+
* in — the other way round in a right-to-left menu, which is APG's rule and the reading order's.
|
|
191
|
+
* @keyboard Escape — Closes the innermost menu and puts focus back on what opened it.
|
|
192
|
+
* @keyboard Tab — Closes the menu and moves focus on, as APG asks.
|
|
193
|
+
* @keyboard A printable character — Typeahead: jumps to the item whose text starts with what was typed,
|
|
194
|
+
* and the same letter again cycles through the items starting with it.
|
|
195
|
+
* @pattern https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/
|
|
196
|
+
*/
|
|
197
|
+
declare function MenuImpl<TKey extends keyof ComponentsAndVariants = 'menu'>(props: MenuProps<TKey>): import("react").JSX.Element;
|
|
198
|
+
/**
|
|
199
|
+
* A submenu: an item that opens a menu of its own. The panel is declared beside its item, *inside* the
|
|
200
|
+
* menu it belongs to, which is what makes it a nested popover — so opening it leaves the parent menu open
|
|
201
|
+
* and a press inside it is a press inside the parent (measured in Chrome 152).
|
|
202
|
+
*
|
|
203
|
+
* @a11y The item carries `role="menuitem"`, `aria-haspopup="menu"` and `aria-expanded`, and the submenu is
|
|
204
|
+
* named after it.
|
|
205
|
+
* @keyboard Right / Enter / Space — Opens it and focuses its first item. Left in a right-to-left menu,
|
|
206
|
+
* which is the reading order rather than the key.
|
|
207
|
+
* @keyboard Left / Escape — Closes it and puts focus back on the item that opened it.
|
|
208
|
+
*/
|
|
209
|
+
declare function MenuSub<TKey extends keyof ComponentsAndVariants = 'menu'>(props: MenuSubProps<TKey>): import("react").JSX.Element;
|
|
210
|
+
/** One command in the menu. A `<button>`, so Enter and Space are the browser's. */
|
|
211
|
+
declare function MenuItem<TKey extends keyof ComponentsAndVariants = 'menu.item'>(props: MenuItemProps<TKey>): import("react").JSX.Element;
|
|
212
|
+
/** An item that carries a state of its own: `role="menuitemcheckbox"` and a tick. */
|
|
213
|
+
declare function MenuCheckboxItem<TKey extends keyof ComponentsAndVariants = 'menu.item'>(props: MenuCheckboxItemProps<TKey>): import("react").JSX.Element;
|
|
214
|
+
/** A set of items of which one is chosen: `role="group"`, and `menuitemradio` inside it. */
|
|
215
|
+
declare function MenuRadioGroup<TKey extends keyof ComponentsAndVariants = 'menu.group'>(props: MenuRadioGroupProps<TKey>): import("react").JSX.Element;
|
|
216
|
+
/** One of a radio group's choices. Checked when its `value` is the group's. */
|
|
217
|
+
declare function MenuRadioItem<TKey extends keyof ComponentsAndVariants = 'menu.item'>(props: MenuRadioItemProps<TKey>): import("react").JSX.Element;
|
|
218
|
+
/**
|
|
219
|
+
* A titled section. The heading names the group through `aria-labelledby`, so the items inside are read
|
|
220
|
+
* as a set rather than as a run of unrelated commands.
|
|
221
|
+
*/
|
|
222
|
+
declare function MenuGroup<TKey extends keyof ComponentsAndVariants = 'menu.group'>(props: MenuGroupProps<TKey>): import("react").JSX.Element;
|
|
223
|
+
/** The line between two sections. `role="separator"`, which a menu is allowed to own. */
|
|
224
|
+
declare function MenuSeparator<TKey extends keyof ComponentsAndVariants = 'menu.separator'>(props: MenuSeparatorProps<TKey>): import("react").JSX.Element;
|
|
225
|
+
declare const Menu: typeof MenuImpl & {
|
|
226
|
+
Item: typeof MenuItem;
|
|
227
|
+
CheckboxItem: typeof MenuCheckboxItem;
|
|
228
|
+
RadioGroup: typeof MenuRadioGroup;
|
|
229
|
+
RadioItem: typeof MenuRadioItem;
|
|
230
|
+
Group: typeof MenuGroup;
|
|
231
|
+
Separator: typeof MenuSeparator;
|
|
232
|
+
Sub: typeof MenuSub;
|
|
233
|
+
};
|
|
234
|
+
export default Menu;
|