@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
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ElementLike } from '../../utils/dom/domUtils';
|
|
2
|
+
import { ChangeHandler, SetControllableState } from '../a11y/useControllableState';
|
|
3
|
+
/** Why a layer opened or closed — every component built on this reports the same four. */
|
|
4
|
+
export type PopoverReason = 'trigger' | 'escape' | 'outside-pointer' | 'imperative';
|
|
5
|
+
/** What a trigger says it opens, which is the one thing the layers built on this disagree about. */
|
|
6
|
+
export type PopoverHasPopup = 'dialog' | 'menu';
|
|
7
|
+
/** The DOM attributes a trigger has to carry, whatever it opens. */
|
|
8
|
+
export interface PopoverTriggerAttributes<THasPopup extends PopoverHasPopup = PopoverHasPopup> {
|
|
9
|
+
'aria-expanded': boolean;
|
|
10
|
+
'aria-haspopup': THasPopup;
|
|
11
|
+
'aria-controls'?: string;
|
|
12
|
+
/** Only where the browser has no Popover API. On the platform path `popovertarget` does the toggling. */
|
|
13
|
+
onClick?(event: React.MouseEvent): void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* What the trigger has to carry: spread it onto the control itself, not a wrapper. The `ref` is what the
|
|
17
|
+
* layer is anchored to, so it is not optional decoration.
|
|
18
|
+
*
|
|
19
|
+
* It must be a **button**: `popovertarget` is what the browser toggles on, and it only reads it off one.
|
|
20
|
+
*/
|
|
21
|
+
export interface PopoverTrigger<THasPopup extends PopoverHasPopup = PopoverHasPopup> {
|
|
22
|
+
ref: React.RefCallback<HTMLElement>;
|
|
23
|
+
props: PopoverTriggerAttributes<THasPopup>;
|
|
24
|
+
}
|
|
25
|
+
export interface PopoverLayerOptions<THasPopup extends PopoverHasPopup = PopoverHasPopup, TReason extends string = never> {
|
|
26
|
+
/** What the trigger says it opens. */
|
|
27
|
+
haspopup: THasPopup;
|
|
28
|
+
/** A consumer id wins over the generated one, and everything that names the layer follows it. */
|
|
29
|
+
id?: string;
|
|
30
|
+
/** What the generated id is prefixed with, so a debugger says which component it belongs to. */
|
|
31
|
+
idPrefix: string;
|
|
32
|
+
open?: boolean;
|
|
33
|
+
defaultOpen?: boolean;
|
|
34
|
+
onOpenChange?: ChangeHandler<boolean, PopoverReason | TReason>;
|
|
35
|
+
/**
|
|
36
|
+
* The layer is open *and* laid out — the only moment focus can be moved into it. On the platform path
|
|
37
|
+
* this is the `toggle` event rather than an effect on the open state, and the difference is not
|
|
38
|
+
* academic: `beforetoggle` fires before the panel opens and a discrete event is flushed synchronously,
|
|
39
|
+
* so an effect would run while `:popover-open` still does not match, which is `display: none` — and
|
|
40
|
+
* focusing a hidden element does nothing at all (measured).
|
|
41
|
+
*/
|
|
42
|
+
onOpened?(panel: HTMLElement): void;
|
|
43
|
+
/**
|
|
44
|
+
* It is about to close, and `heldFocus` says whether it has focus. Fired from `beforetoggle`, while the
|
|
45
|
+
* panel is still shown, because that is the last moment focus can be moved without a flash: the platform
|
|
46
|
+
* returns focus to whatever had it before the **outermost** layer opened, and for a nested one it does
|
|
47
|
+
* nothing at all — focus drops to `<body>` (both measured in Chrome 152). A submenu puts focus back on
|
|
48
|
+
* its own item from here.
|
|
49
|
+
*/
|
|
50
|
+
onClosing?(heldFocus: boolean, trigger: HTMLElement | null): void;
|
|
51
|
+
/**
|
|
52
|
+
* More elements a press inside counts as inside, for the portal path only: a nested layer is portalled
|
|
53
|
+
* out of the DOM it was declared in, so nothing about the tree says a press in it is a press in this
|
|
54
|
+
* one. The platform answers that question itself, by the popover nesting rules.
|
|
55
|
+
*/
|
|
56
|
+
inside?: readonly ElementLike[];
|
|
57
|
+
}
|
|
58
|
+
export interface PopoverLayer<THasPopup extends PopoverHasPopup = PopoverHasPopup, TReason extends string = never> {
|
|
59
|
+
/** Whether the browser's Popover API is placing the layer in the top layer. `false` is the portal path. */
|
|
60
|
+
platform: boolean;
|
|
61
|
+
isOpen: boolean;
|
|
62
|
+
setOpen: SetControllableState<boolean, PopoverReason | TReason>;
|
|
63
|
+
id: string;
|
|
64
|
+
/** What the trigger render prop is handed. */
|
|
65
|
+
trigger: PopoverTrigger<THasPopup>;
|
|
66
|
+
triggerElement: HTMLElement | null;
|
|
67
|
+
/**
|
|
68
|
+
* The trigger's id — what names a layer after the control that opened it, which is what APG asks of a
|
|
69
|
+
* menu. One is written onto the trigger when it has none of its own.
|
|
70
|
+
*/
|
|
71
|
+
triggerId?: string;
|
|
72
|
+
/** Goes on the panel, on both paths. */
|
|
73
|
+
setPanel: React.RefCallback<HTMLElement>;
|
|
74
|
+
panelElement: HTMLElement | null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* One conversation with the Popover API, for every component that opens a layer on it: the open state,
|
|
78
|
+
* the platform mirror, the dismissal reasons, and the two primitives that stand in where the browser has
|
|
79
|
+
* no popover at all. `Popover` is this plus a `role="dialog"` panel; `Menu` is this three times over —
|
|
80
|
+
* once for the menu and once per open submenu.
|
|
81
|
+
*
|
|
82
|
+
* What the platform owns, measured in Chrome 152: the top layer, light dismiss (Escape and an outside
|
|
83
|
+
* press), the toggle from a `popovertarget` trigger — including the press that closes an open layer from
|
|
84
|
+
* its own trigger, with no reopen — and focus return for the outermost layer. What it does not own is
|
|
85
|
+
* focus *into* the layer, focus return for a nested one, and any of the ARIA.
|
|
86
|
+
*/
|
|
87
|
+
export default function usePopoverLayer<THasPopup extends PopoverHasPopup, TReason extends string = never>(options: PopoverLayerOptions<THasPopup, TReason>): PopoverLayer<THasPopup, TReason>;
|
package/react-shared.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require("./rolldown-runtime.cjs");let t=require("react");t=e.t(t,1);let n=require("@box-kite/core");function r({classNames:e,styleElements:t},r){return{className:(0,n.classNames)(e,r).join(` `),styles:t}}var i={selected:`aria-selected`},a;(function(e){function t(e,t,n){e!==void 0&&e!=null&&(n[i[t]??t]=Array.isArray(e)?e[0]:e)}e.assignBooleanProp=t})(a||={});var o=a;function s(e,t){let r=e,{props:i,className:a,disabled:s,required:c,checked:l,selected:u}=r,d={...i,className:(0,n.classNames)(t,a).join(` `)};return o.assignBooleanProp(s,`disabled`,d),o.assignBooleanProp(c,`required`,d),o.assignBooleanProp(l,`checked`,d),o.assignBooleanProp(u,`selected`,d),`style`in r&&(d.style=r.style),`id`in r&&(d.id=r.id),d}var c=parseInt(t.default.version,10)>=19,l=!1;function u(e){if(e?.length)return!c&&!l&&(l=!0,console.warn(`[box-kite] Box.configure({ sink: 'element' }) needs React 19 to hoist and dedupe its <style> elements; React ${t.default.version} will render them inline. Use the default sink instead.`)),e.map(({href:e,css:n,precedence:r})=>t.default.createElement(`style`,{key:e,href:e,precedence:r,dangerouslySetInnerHTML:{__html:n}}))}function d(e,t){let{classNames:r,signature:i,styleElements:a}=(0,n.getDefaultEngine)().resolveClassNames(e,t);return{classNames:r,signature:i,styleElements:u(a)}}var f=`__boxAttributesInProps`;function p(e){return e[f]=!0,e}function m(e){return typeof e==`function`||typeof e==`object`&&e?e[f]===!0:!1}var h=[`role`,`aria-label`,`aria-labelledby`,`aria-hidden`];function g(e,t){if(e!==void 0)return{role:`img`,"aria-label":e};if(!(t&&h.some(e=>e in t)))return{"aria-hidden":!0}}var _;(function(e){function t(e){return e.charAt(0).toUpperCase()+e.slice(1)}e.capitalize=t})(_||={});var v=_,y;(function(e){function t(e,t,n){let r={...e},i=r.props||{};return t.forEach(e=>{e in r&&(i[e]=r[e],delete r[e])}),n&&Object.entries(n).forEach(([e,t])=>{i[e]=t}),r.props=i,r}e.buildProps=t;function n(e){return!!e&&typeof e==`object`}e.isObject=n;function r(e,t){return e in t}e.isKeyOf=r})(y||={});var b=y;Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return
|
|
1
|
+
const e=require("./rolldown-runtime.cjs");let t=require("react");t=e.t(t,1);let n=require("@box-kite/core");function r({classNames:e,styleElements:t},r){return{className:(0,n.classNames)(e,r).join(` `),styles:t}}var i={selected:`aria-selected`},a;(function(e){function t(e,t,n){e!==void 0&&e!=null&&(n[i[t]??t]=Array.isArray(e)?e[0]:e)}e.assignBooleanProp=t})(a||={});var o=a;function s(e,t){let r=e,{props:i,className:a,disabled:s,required:c,checked:l,selected:u}=r,d={...i,className:(0,n.classNames)(t,a).join(` `)};return o.assignBooleanProp(s,`disabled`,d),o.assignBooleanProp(c,`required`,d),o.assignBooleanProp(l,`checked`,d),o.assignBooleanProp(u,`selected`,d),`style`in r&&(d.style=r.style),`id`in r&&(d.id=r.id),d}var c=parseInt(t.default.version,10)>=19,l=!1;function u(e){if(e?.length)return!c&&!l&&(l=!0,console.warn(`[box-kite] Box.configure({ sink: 'element' }) needs React 19 to hoist and dedupe its <style> elements; React ${t.default.version} will render them inline. Use the default sink instead.`)),e.map(({href:e,css:n,precedence:r})=>t.default.createElement(`style`,{key:e,href:e,precedence:r,dangerouslySetInnerHTML:{__html:n}}))}function d(e,t){let{classNames:r,signature:i,styleElements:a}=(0,n.getDefaultEngine)().resolveClassNames(e,t);return{classNames:r,signature:i,styleElements:u(a)}}var f=`__boxAttributesInProps`;function p(e){return e[f]=!0,e}function m(e){return typeof e==`function`||typeof e==`object`&&e?e[f]===!0:!1}var h=[`role`,`aria-label`,`aria-labelledby`,`aria-hidden`];function g(e,t){if(e!==void 0)return{role:`img`,"aria-label":e};if(!(t&&h.some(e=>e in t)))return{"aria-hidden":!0}}var _;(function(e){function t(e){return e.charAt(0).toUpperCase()+e.slice(1)}e.capitalize=t})(_||={});var v=_,y;(function(e){function t(e,t,n){let r={...e},i=r.props||{};return t.forEach(e=>{e in r&&(i[e]=r[e],delete r[e])}),n&&Object.entries(n).forEach(([e,t])=>{i[e]=t}),r.props=i,r}e.buildProps=t;function n(e){return!!e&&typeof e==`object`}e.isObject=n;function r(e,t){return e in t}e.isKeyOf=r})(y||={});var b=y,x=[`sky-500`,`emerald-500`,`amber-500`,`violet-500`,`rose-500`,`cyan-500`],S=[`sky-400`,`emerald-400`,`amber-400`,`violet-400`,`rose-400`,`cyan-400`];function C(e){return e.reduce((e,t,n)=>(e[`chart-${n+1}`]=t,e),{})}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return u}});
|
package/react-shared.mjs
CHANGED
|
@@ -100,6 +100,23 @@ var v = _, y;
|
|
|
100
100
|
}
|
|
101
101
|
e.isKeyOf = r;
|
|
102
102
|
})(y ||= {});
|
|
103
|
-
var b = y
|
|
103
|
+
var b = y, x = [
|
|
104
|
+
"sky-500",
|
|
105
|
+
"emerald-500",
|
|
106
|
+
"amber-500",
|
|
107
|
+
"violet-500",
|
|
108
|
+
"rose-500",
|
|
109
|
+
"cyan-500"
|
|
110
|
+
], S = [
|
|
111
|
+
"sky-400",
|
|
112
|
+
"emerald-400",
|
|
113
|
+
"amber-400",
|
|
114
|
+
"violet-400",
|
|
115
|
+
"rose-400",
|
|
116
|
+
"cyan-400"
|
|
117
|
+
];
|
|
118
|
+
function C(e) {
|
|
119
|
+
return e.reduce((e, t, n) => (e[`chart-${n + 1}`] = t, e), {});
|
|
120
|
+
}
|
|
104
121
|
//#endregion
|
|
105
|
-
export {
|
|
122
|
+
export { v as a, p as c, s as d, r as f, b as i, d as l, x as n, g as o, C as r, m as s, S as t, u };
|
package/rsc.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./rolldown-runtime.cjs"),t=require("./react-shared.cjs");let n=require("react");n=e.t(n,1);let r=require("@box-kite/core");(0,r.getDefaultEngine)().configure({sink:`element`});function i(e){let{tag:r=`div`,children:i}=e;if(typeof i==`function`)throw Error(`[box-kite] Box children as a function track hover state, which needs the client Box — render it from a 'use client' module.`);let{classNames:a,styleElements:o}=t.
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./rolldown-runtime.cjs"),t=require("./react-shared.cjs");let n=require("react");n=e.t(n,1);let r=require("@box-kite/core");(0,r.getDefaultEngine)().configure({sink:`element`});function i(e){let{tag:r=`div`,children:i}=e;if(typeof i==`function`)throw Error(`[box-kite] Box children as a function track hover state, which needs the client Box — render it from a 'use client' module.`);let{classNames:a,styleElements:o}=t.l(e,r===`svg`);return n.default.createElement(n.default.Fragment,null,o,n.default.createElement(r,t.d(e,a),i))}var a=i;a.extend=r.BoxExtends.extend,a.components=r.BoxExtends.components,a.keyframes=r.BoxExtends.keyframes,a.spring=r.Springs.spring,a.getVariableValue=e=>(0,r.getDefaultEngine)().getVariableValue(e),a.configure=e=>(0,r.getDefaultEngine)().configure(e);function o(e,n){return t.f(t.l(e,n?.svg===!0),e.className)}exports.default=a,exports.useClassNames=o;
|
package/rsc.mjs
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** What a key means in an accordion: a step to the neighbouring header, or a jump to an end. */
|
|
2
|
+
export interface AccordionMove {
|
|
3
|
+
kind: 'step' | 'edge';
|
|
4
|
+
/** `1` forward, `-1` back — and on an `edge` move, the first header or the last. */
|
|
5
|
+
delta: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Which header an accordion's keyboard lands on, and which panels stand open, read off the DOM rather
|
|
9
|
+
* than out of a registry — B4's and B5's precedent, for the same reason: a header a consumer wrapped,
|
|
10
|
+
* rendered from a list or put behind a condition is in the order it was written.
|
|
11
|
+
*/
|
|
12
|
+
declare namespace AccordionUtils {
|
|
13
|
+
/** The root marks itself so a header can say which accordion it belongs to. Neither attribute is ARIA: an accordion has no container role. */
|
|
14
|
+
const ROOT_ATTRIBUTE = "data-accordion";
|
|
15
|
+
const TRIGGER_ATTRIBUTE = "data-accordion-trigger";
|
|
16
|
+
const ROOT_SELECTOR = "[data-accordion]";
|
|
17
|
+
const TRIGGER_SELECTOR = "[data-accordion-trigger]";
|
|
18
|
+
/** Whether this accordion is the one a node belongs to, rather than one nested in a panel inside it. */
|
|
19
|
+
function owns(root: Element, node: EventTarget | null): boolean;
|
|
20
|
+
/** The headers an accordion owns, in DOM order. A nested accordion's headers belong to their own root. */
|
|
21
|
+
function triggers(root: Element | null | undefined): HTMLElement[];
|
|
22
|
+
/**
|
|
23
|
+
* A header nothing can open. Skipped by every movement, the way a disabled tab is: it is the `disabled`
|
|
24
|
+
* attribute here, so the browser has already taken it out of the tab sequence, and arrows that still
|
|
25
|
+
* stopped on it would be reaching what Tab cannot.
|
|
26
|
+
*/
|
|
27
|
+
function isDisabled(trigger: Element): boolean;
|
|
28
|
+
/** The next enabled header in a direction, or `undefined` when there is none. */
|
|
29
|
+
function step(triggers: readonly HTMLElement[], from: number, delta: number, loop: boolean): HTMLElement | undefined;
|
|
30
|
+
/** The first (`delta: 1`) or last (`delta: -1`) enabled header. */
|
|
31
|
+
function edge(triggers: readonly HTMLElement[], delta: number): HTMLElement | undefined;
|
|
32
|
+
/** Where the header a move starts from sits, or `-1` when the node is not one of them. */
|
|
33
|
+
function indexOf(triggers: readonly HTMLElement[], node: EventTarget | null): number;
|
|
34
|
+
/**
|
|
35
|
+
* What a key does on an accordion header. An accordion stacks along the block axis whatever the reading
|
|
36
|
+
* order is, so unlike a tablist there is nothing here to mirror — and the sideways pair is left to the
|
|
37
|
+
* page, which is where APG leaves it.
|
|
38
|
+
*/
|
|
39
|
+
function moveFor(key: string): AccordionMove | undefined;
|
|
40
|
+
/** The header a move arrives at, or `undefined` when there is nowhere to go. */
|
|
41
|
+
function target(triggers: readonly HTMLElement[], from: number, move: AccordionMove, loop: boolean): HTMLElement | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* The panels standing open after one header is pressed. Closing the open one is always allowed, so a
|
|
44
|
+
* single-panel accordion needs no second prop to say whether it can stand empty.
|
|
45
|
+
*/
|
|
46
|
+
function toggle(open: readonly string[], value: string, multiple: boolean): string[];
|
|
47
|
+
}
|
|
48
|
+
export default AccordionUtils;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a floating layer goes, twice over: as the `position-area` the browser reads, and as the
|
|
3
|
+
* coordinates a browser without anchor positioning has to be given. One model rather than two, so the
|
|
4
|
+
* two paths cannot drift — the fallback is the CSS placement worked out by hand.
|
|
5
|
+
*
|
|
6
|
+
* Framework-free and layout-free: every function here takes rectangles and returns numbers, which is what
|
|
7
|
+
* makes the flip and the shift testable without a DOM (jsdom computes no layout at all).
|
|
8
|
+
*/
|
|
9
|
+
/** Which side of the anchor the layer sits on. `top`/`bottom` are the block axis, `start`/`end` the inline one. */
|
|
10
|
+
export type AnchorSide = 'top' | 'bottom' | 'start' | 'end';
|
|
11
|
+
/** Where along that side the layer lines up. */
|
|
12
|
+
export type AnchorAlign = 'start' | 'center' | 'end';
|
|
13
|
+
/** A box in viewport coordinates — what `getBoundingClientRect()` gives, narrowed to what placing needs. */
|
|
14
|
+
export interface AnchorRect {
|
|
15
|
+
top: number;
|
|
16
|
+
left: number;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
}
|
|
20
|
+
export interface PlacementOptions {
|
|
21
|
+
side: AnchorSide;
|
|
22
|
+
align: AnchorAlign;
|
|
23
|
+
/** The gap between anchor and layer, in pixels. */
|
|
24
|
+
offset: number;
|
|
25
|
+
/** Whether a side that does not fit may be swapped for its opposite. */
|
|
26
|
+
flip: boolean;
|
|
27
|
+
/** Whether the inline axis runs right to left, which is what `start`/`end` mean. */
|
|
28
|
+
rtl: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The twelve `position-area` values a side and an alignment can name — spelled out rather than derived,
|
|
32
|
+
* so the compiler can see that each one is a value the `positionArea` prop takes.
|
|
33
|
+
*/
|
|
34
|
+
export type AnchorArea = 'block-start span-all' | 'block-start span-inline-start' | 'block-start span-inline-end' | 'block-end span-all' | 'block-end span-inline-start' | 'block-end span-inline-end' | 'span-all inline-start' | 'span-block-start inline-start' | 'span-block-end inline-start' | 'span-all inline-end' | 'span-block-start inline-end' | 'span-block-end inline-end';
|
|
35
|
+
/** Where the fallback puts the layer: viewport coordinates, and the side it settled on after flipping. */
|
|
36
|
+
export interface Placement {
|
|
37
|
+
top: number;
|
|
38
|
+
left: number;
|
|
39
|
+
side: AnchorSide;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The `position-area` for a side and an alignment — and the counter-intuitive half of the grammar, since
|
|
43
|
+
* an alignment is written as the span *away* from it: a layer whose start edge meets the anchor's start
|
|
44
|
+
* edge occupies the centre cell and the one after it, which is `span-inline-end`. Measured in Chrome 152.
|
|
45
|
+
*
|
|
46
|
+
* `center` is `span-all` rather than the `center` cell on purpose: the centre cell is only as wide as the
|
|
47
|
+
* anchor, so a layer wider than its anchor overflows it on the cross axis, and an overflow no flip can fix
|
|
48
|
+
* disqualifies every fallback position (the trap `positionTryFallbacks` documents).
|
|
49
|
+
*/
|
|
50
|
+
export declare function areaFor(side: AnchorSide, align: AnchorAlign): AnchorArea;
|
|
51
|
+
/**
|
|
52
|
+
* The side a used `position-area` names — the browser's own answer to "did you flip?", since
|
|
53
|
+
* `getComputedStyle` reports the position it settled on rather than the one that was specified.
|
|
54
|
+
*
|
|
55
|
+
* It is not the value that went in: measured in Chrome 152, a used value is normalised twice over. A
|
|
56
|
+
* `span-all` half is dropped (`block-end span-all` reads back `block-end`), and a value naming both axes
|
|
57
|
+
* comes back in the `start`/`end` shorthand family, where **position** names the axis rather than a
|
|
58
|
+
* keyword — `block-end span-inline-end` reads back `end span-end`, and `start span-end` once it has
|
|
59
|
+
* flipped. So the block axis is the first keyword and the inline axis the second, and the side is
|
|
60
|
+
* whichever of them is not a span.
|
|
61
|
+
*/
|
|
62
|
+
export declare function sideOfArea(area: string): AnchorSide | null;
|
|
63
|
+
/** The two candidate lists, spelled out so the compiler can see each is a value `positionTryFallbacks` takes. */
|
|
64
|
+
export type AnchorFallbacks = 'flip-block, flip-inline, flip-block flip-inline' | 'flip-inline, flip-block, flip-inline flip-block';
|
|
65
|
+
/**
|
|
66
|
+
* The `position-try-fallbacks` for a side: its own axis first, then the alignment's, then both. Three
|
|
67
|
+
* candidates rather than one because a candidate has to fit on **both** axes — so a single `flip-block`
|
|
68
|
+
* does nothing at all for a layer that overflows the *cross* axis, and the browser leaves it against the
|
|
69
|
+
* edge of the viewport (measured in Chrome 152, hand-written CSS). With the list, a menu aligned to its
|
|
70
|
+
* trigger's end mirrors to the other end rather than running off the page.
|
|
71
|
+
*/
|
|
72
|
+
export declare function fallbacksFor(side: AnchorSide): AnchorFallbacks;
|
|
73
|
+
/**
|
|
74
|
+
* The fallback's whole answer: flip to the opposite side when the requested one has no room and the
|
|
75
|
+
* opposite has more, then shift along the cross axis to stay in the viewport. The two are the entire
|
|
76
|
+
* middleware stack a positioning library sells, and they are twelve lines because the browser is doing
|
|
77
|
+
* neither here — this runs only where `anchor-name` is not supported.
|
|
78
|
+
*/
|
|
79
|
+
export declare function place(anchor: AnchorRect, layer: AnchorRect, viewport: AnchorRect, options: PlacementOptions): Placement;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An array whose elements are computed when somebody asks for one. A server row model lays out a million
|
|
3
|
+
* rows and renders fifty of them, so allocating an object per row is the one thing it must not do — and
|
|
4
|
+
* every reader of these lists indexes, slices or binary-searches rather than iterating, which is exactly
|
|
5
|
+
* what a proxy can answer. `length` is reported without the target ever holding that many elements.
|
|
6
|
+
*
|
|
7
|
+
* Iterating one (`map`, `flatMap`, a spread) materializes every element, which is the cost this exists to
|
|
8
|
+
* avoid: do that only where the length is known to be small.
|
|
9
|
+
*/
|
|
10
|
+
export default function lazyList<T>(length: number, at: (index: number) => T): T[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chart palette, on its own so that `boxComponents.ts` can declare it without dragging the rest of
|
|
3
|
+
* `ChartUtils` — the path builders and the trigonometry — into the bundle every Box already pays for.
|
|
4
|
+
* `ChartUtils` re-exports all three, so the public names are unchanged.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The colours a chart uses when nobody names any. Tokens rather than raw colours, so they resolve
|
|
8
|
+
* through the palette; six, because that is as many series as a tile can be told apart in — a seventh
|
|
9
|
+
* wraps round rather than inventing a colour.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PALETTE: readonly ["sky-500", "emerald-500", "amber-500", "violet-500", "rose-500", "cyan-500"];
|
|
12
|
+
/** The same six for a dark background, where a 500 reads as muddy against near-black. */
|
|
13
|
+
export declare const DARK_PALETTE: readonly ["sky-400", "emerald-400", "amber-400", "violet-400", "rose-400", "cyan-400"];
|
|
14
|
+
/** The numbered palette as custom properties: `{ 'chart-1': 'sky-500', … }`. */
|
|
15
|
+
export declare function paletteVariables(palette: readonly string[]): Record<string, string>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as ChartPalette from './chartPalette';
|
|
1
2
|
/**
|
|
2
3
|
* The model behind the chart primitives: every number a `<Sparkline>`, `<ProgressRing>`, `<Gauge>` or
|
|
3
4
|
* `<MiniDonut>` draws with, the palette a `<ChartContainer>` declares, and no React. Components render,
|
|
@@ -78,16 +79,9 @@ declare namespace ChartUtils {
|
|
|
78
79
|
* no segments rather than a division by zero, and a negative value counts as none.
|
|
79
80
|
*/
|
|
80
81
|
function donutSegments(data: readonly number[], length: number): Segment[];
|
|
81
|
-
/**
|
|
82
|
-
* The colours a chart uses when nobody names any. Tokens rather than raw colours, so they resolve
|
|
83
|
-
* through the palette; six, because that is as many series as a tile can be told apart in — a seventh
|
|
84
|
-
* wraps round rather than inventing a colour.
|
|
85
|
-
*/
|
|
86
82
|
const PALETTE: readonly ["sky-500", "emerald-500", "amber-500", "violet-500", "rose-500", "cyan-500"];
|
|
87
|
-
/** The same six for a dark background, where a 500 reads as muddy against near-black. */
|
|
88
83
|
const DARK_PALETTE: readonly ["sky-400", "emerald-400", "amber-400", "violet-400", "rose-400", "cyan-400"];
|
|
89
|
-
|
|
90
|
-
function paletteVariables(palette: readonly string[]): Record<string, string>;
|
|
84
|
+
const paletteVariables: typeof ChartPalette.paletteVariables;
|
|
91
85
|
/**
|
|
92
86
|
* The variables a chart's series read, named as the ecosystem names them: `{ 'color-revenue':
|
|
93
87
|
* 'var(--chart-1)' }`. A list takes the palette in order, which is the whole case for the container — a
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** How a row's text, its identity or its disabled state is read: a key of the row, or a function. */
|
|
2
|
+
export type RowAccessor<TRow, TResult> = (keyof TRow & string) | ((row: TRow) => TResult);
|
|
3
|
+
/**
|
|
4
|
+
* What the combobox needs to know about a row to search it, tell it from its neighbours and refuse it.
|
|
5
|
+
* Only `label` is required — it is the text, and without one nothing could be typed at or read out.
|
|
6
|
+
*/
|
|
7
|
+
export interface ComboboxRowDef<TRow> {
|
|
8
|
+
/** The row's text: displayed, searched, and what a screen reader announces. */
|
|
9
|
+
label: RowAccessor<TRow, string>;
|
|
10
|
+
/** What makes two rows the same row. Defaults to the label, which is what a list of strings has. */
|
|
11
|
+
key?: RowAccessor<TRow, string | number>;
|
|
12
|
+
/** A row that cannot be chosen: skipped by the arrows and inert to a press. */
|
|
13
|
+
disabled?: RowAccessor<TRow, boolean>;
|
|
14
|
+
}
|
|
15
|
+
/** A row of the open listbox. `create` is the offer to make a row out of what was typed. */
|
|
16
|
+
export type ComboboxRow<TRow> = {
|
|
17
|
+
kind: 'option';
|
|
18
|
+
row: TRow;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'create';
|
|
21
|
+
query: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Which rows a query leaves, in the order they will be shown. It takes the whole list rather than one row
|
|
25
|
+
* so that it can rank as well as reject, and it is handed the label reader so that composing it with the
|
|
26
|
+
* built-in one costs nothing.
|
|
27
|
+
*/
|
|
28
|
+
export type ComboboxFilter<TRow> = (rows: TRow[], query: string, labelOf: (row: TRow) => string) => TRow[];
|
|
29
|
+
/**
|
|
30
|
+
* The combobox with no React in it: reading a row, filtering the list, and what the selection becomes when
|
|
31
|
+
* a row is chosen. All of it is a pure function of the data and the query, which is what lets the same
|
|
32
|
+
* answers be tested without a DOM and re-used by a virtualized listbox that renders fifty of ten thousand.
|
|
33
|
+
*/
|
|
34
|
+
declare namespace ComboboxUtils {
|
|
35
|
+
/** Resolve an accessor against a row. A key reads the property; a function is called. */
|
|
36
|
+
function read<TRow, TResult>(accessor: RowAccessor<TRow, TResult>, row: TRow): TResult;
|
|
37
|
+
/** A row's text. Coerced, so a numeric column is a legal label and a missing one is empty rather than `"undefined"`. */
|
|
38
|
+
function labelOf<TRow>(def: ComboboxRowDef<TRow>, row: TRow): string;
|
|
39
|
+
/** A row's identity. The label by default: a list of plain strings is its own set of keys. */
|
|
40
|
+
function keyOf<TRow>(def: ComboboxRowDef<TRow>, row: TRow): string | number;
|
|
41
|
+
function isDisabled<TRow>(def: ComboboxRowDef<TRow>, row: TRow): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* The form a query and a label are compared in: case-folded and stripped of accents, so that `jose`
|
|
44
|
+
* finds `José`. NFD splits a letter from its accent and the mark range is what is then dropped.
|
|
45
|
+
*/
|
|
46
|
+
function normalize(text: string): string;
|
|
47
|
+
/** Whether a label answers a query. A substring rather than a prefix: a surname is worth finding. */
|
|
48
|
+
function matches(label: string, query: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* The filter a combobox uses when its caller names none, exported so that a caller who names one can
|
|
51
|
+
* still start from it — `(rows, query, labelOf) => filterRows(rows, query, labelOf).slice(0, 20)`.
|
|
52
|
+
*/
|
|
53
|
+
function filterRows<TRow>(rows: TRow[], query: string, labelOf: (row: TRow) => string): TRow[];
|
|
54
|
+
/** The selection as a list, whichever shape it arrived in. `null` and `undefined` are "nothing chosen". */
|
|
55
|
+
function toArray<TRow>(value: TRow | TRow[] | null | undefined): TRow[];
|
|
56
|
+
function isSelected<TRow>(selection: TRow[], row: TRow, keyFor: (row: TRow) => string | number): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* What the selection becomes when a row is chosen. Single-select replaces, and choosing what is already
|
|
59
|
+
* chosen keeps it — a combobox has no "unselect by reselecting", since its field would have nothing to
|
|
60
|
+
* show. Multi-select toggles, which is how a chip is removed from the listbox rather than from the chip.
|
|
61
|
+
*/
|
|
62
|
+
function toggle<TRow>(selection: TRow[], row: TRow, multiple: boolean, keyFor: (row: TRow) => string | number): TRow[];
|
|
63
|
+
interface RowsOptions<TRow> {
|
|
64
|
+
data: TRow[];
|
|
65
|
+
def: ComboboxRowDef<TRow>;
|
|
66
|
+
/** What has been typed. Only filters once the caller says it was typed rather than displayed. */
|
|
67
|
+
query: string;
|
|
68
|
+
/** `false` when the data is already filtered — a server that searched, or a caller that did. */
|
|
69
|
+
filter: ComboboxFilter<TRow> | false;
|
|
70
|
+
/** Whether the query is the user's rather than the selection's text. A displayed value filters nothing. */
|
|
71
|
+
typed: boolean;
|
|
72
|
+
/** Turns a query into the row it would create, or `null` for one that should not be offered. */
|
|
73
|
+
createRow?: (query: string) => TRow | null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Every row the listbox shows, in keyboard order. The create row is last and appears only for a query
|
|
77
|
+
* that no row already answers by name — offering "Create Apple" beside Apple is how a list grows twins.
|
|
78
|
+
*/
|
|
79
|
+
function rowsFor<TRow>(options: RowsOptions<TRow>): ComboboxRow<TRow>[];
|
|
80
|
+
/** A row the arrows skip and a press does nothing to. The create row is always available. */
|
|
81
|
+
function isRowDisabled<TRow>(def: ComboboxRowDef<TRow>, row: ComboboxRow<TRow>): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Where the highlight goes when the listbox opens: the selected row whichever key opened it, so that
|
|
84
|
+
* Down or Up from a chosen value moves on from it rather than jumping to an end. With nothing chosen it
|
|
85
|
+
* falls back to the end the key implied. `-1` when nothing can be highlighted — no rows, or all disabled.
|
|
86
|
+
*/
|
|
87
|
+
function activeIndexFor<TRow>(rows: ComboboxRow<TRow>[], selection: TRow[], def: ComboboxRowDef<TRow>, fallback?: 'first' | 'last'): number;
|
|
88
|
+
}
|
|
89
|
+
export default ComboboxUtils;
|
package/utils/dom/domUtils.d.ts
CHANGED
|
@@ -24,3 +24,9 @@ export declare function isEventInside(event: Event, elements: readonly ElementLi
|
|
|
24
24
|
* left to right — which is the initial value anyway.
|
|
25
25
|
*/
|
|
26
26
|
export declare function isRtl(value: ElementLike): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Drop whatever the document has selected. A double press selects the word under it before any handler
|
|
29
|
+
* runs, so a widget that *replaces* what was pressed leaves a range over content that no longer exists —
|
|
30
|
+
* which the browser repairs to the nearest boundary and then paints over whatever is rendered inside it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function clearSelection(element: ElementLike): void;
|
|
@@ -14,5 +14,15 @@ export declare function documentOrNull(): Document | null;
|
|
|
14
14
|
export declare function documentRoot(): Element | null;
|
|
15
15
|
/** The `<head>` to write a stylesheet into, or null when there is nothing to write to. */
|
|
16
16
|
export declare function documentHead(): HTMLHeadElement | null;
|
|
17
|
+
/**
|
|
18
|
+
* Whether the browser has the Popover API, which is what puts a floating layer in the top layer. Asked
|
|
19
|
+
* per mount rather than cached, so a test can install the contract and answer for the browser.
|
|
20
|
+
*/
|
|
21
|
+
export declare function supportsPopover(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the browser owns a `<dialog>`'s light dismiss: `closedby="any"` closes it on a press outside,
|
|
24
|
+
* and `closedby="closerequest"` gives a non-modal one Escape. Asked per mount, like `supportsPopover`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function supportsDialogClosedBy(): boolean;
|
|
17
27
|
/** A media query, or null where `matchMedia` does not exist — a server, or a bare jsdom. */
|
|
18
28
|
export declare function matchMedia(query: string): MediaQueryList | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSV, to RFC 4180, plus the two things a spreadsheet adds to it: a byte-order mark, without which Excel
|
|
3
|
+
* reads UTF-8 as the local code page and mangles every accented name, and the leading characters that
|
|
4
|
+
* make a *value* into a formula.
|
|
5
|
+
*/
|
|
6
|
+
declare namespace CsvUtils {
|
|
7
|
+
interface Options {
|
|
8
|
+
/** The field separator. A semicolon is what a spreadsheet expects in a locale using a decimal comma. */
|
|
9
|
+
delimiter?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Prefix a value beginning `=`, `+`, `-` or `@` with a quote, so a spreadsheet reads it as text.
|
|
12
|
+
* On by default: a cell somebody typed is data, and a downloaded file that runs it is CSV injection.
|
|
13
|
+
*/
|
|
14
|
+
escapeFormulas?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** What Excel needs at the top of the file to read the rest of it as UTF-8. */
|
|
17
|
+
const BOM = "\uFEFF";
|
|
18
|
+
/** One value as a field: quoted when it holds a delimiter, a quote or a newline, and never otherwise. */
|
|
19
|
+
function field(value: unknown, options?: Options): string;
|
|
20
|
+
/** The whole file. Rows are joined with CRLF, which is what the RFC specifies and what Excel writes. */
|
|
21
|
+
function csv(rows: unknown[][], options?: Options): string;
|
|
22
|
+
}
|
|
23
|
+
export default CsvUtils;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Handing a file the page generated to the person looking at it. */
|
|
2
|
+
declare namespace DownloadUtils {
|
|
3
|
+
/**
|
|
4
|
+
* A file the browser saves. There is no API for "save this" — an anchor with a `download` attribute is
|
|
5
|
+
* the mechanism, so the link is created, clicked and removed within the call.
|
|
6
|
+
*
|
|
7
|
+
* The object URL is revoked on the next task rather than immediately: Safari reads it *after* the click
|
|
8
|
+
* handler returns, and a revoke in the same turn leaves the file empty.
|
|
9
|
+
*/
|
|
10
|
+
function download(data: BlobPart, fileName: string, type: string): void;
|
|
11
|
+
}
|
|
12
|
+
export default DownloadUtils;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A spreadsheet, written out. An `.xlsx` is a ZIP of XML parts (ECMA-376), so the whole format is a
|
|
3
|
+
* handful of strings and `ZipUtils` — which is why this library exports Excel with no dependency to
|
|
4
|
+
* install and nothing added to the grid's bundle until somebody presses the button.
|
|
5
|
+
*
|
|
6
|
+
* What is here is the part a table needs: values that keep their type, a number format, a bold header on
|
|
7
|
+
* a fill, column widths, a frozen header row, an auto-filter, and outline levels so a grouped grid opens
|
|
8
|
+
* in Excel grouped. What is not here is everything else a workbook can hold — formulas, several sheets,
|
|
9
|
+
* images, charts, conditional formats. This is a writer for one table, not an Excel implementation.
|
|
10
|
+
*/
|
|
11
|
+
declare namespace XlsxUtils {
|
|
12
|
+
/** What a cell can hold. A `Date` is written as Excel's own serial number, with a date format on it. */
|
|
13
|
+
type Value = string | number | boolean | Date | null | undefined;
|
|
14
|
+
/** One cell's appearance. Each distinct combination becomes one entry in the workbook's style table. */
|
|
15
|
+
interface Style {
|
|
16
|
+
bold?: boolean;
|
|
17
|
+
/** `RRGGBB`, the only colour spelling a workbook has — it carries no theme and no palette. */
|
|
18
|
+
color?: string;
|
|
19
|
+
fill?: string;
|
|
20
|
+
/** An Excel number format, `'#,##0.00'` or `'yyyy-mm-dd'`. */
|
|
21
|
+
format?: string;
|
|
22
|
+
align?: 'left' | 'center' | 'right';
|
|
23
|
+
/** Indent steps, which is how a grouped row is stepped in beside its outline level. */
|
|
24
|
+
indent?: number;
|
|
25
|
+
}
|
|
26
|
+
interface Cell {
|
|
27
|
+
value: Value;
|
|
28
|
+
style?: Style;
|
|
29
|
+
}
|
|
30
|
+
interface Row {
|
|
31
|
+
cells: Cell[];
|
|
32
|
+
/** Outline level: 0 is top level, and a row one deeper is a child of the last row above it. */
|
|
33
|
+
level?: number;
|
|
34
|
+
/** Collapsed out of sight, which is how a collapsed group in the grid opens collapsed in Excel. */
|
|
35
|
+
hidden?: boolean;
|
|
36
|
+
/** On the summary row of a collapsed group — the row carrying the outline's own toggle. */
|
|
37
|
+
collapsed?: boolean;
|
|
38
|
+
}
|
|
39
|
+
interface Sheet {
|
|
40
|
+
name: string;
|
|
41
|
+
/** Column widths in characters, one per column; `undefined` leaves Excel's default. */
|
|
42
|
+
widths: (number | undefined)[];
|
|
43
|
+
rows: Row[];
|
|
44
|
+
/** How many rows stay on screen when the sheet is scrolled. */
|
|
45
|
+
freeze?: number;
|
|
46
|
+
/** Whether the header row gets Excel's filter buttons. A grouped sheet is given none. */
|
|
47
|
+
autoFilter?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Text as XML content. The control characters XML 1.0 has no spelling for are dropped rather than escaped. */
|
|
50
|
+
function escape(text: string): string;
|
|
51
|
+
/** A column's letters, the way a spreadsheet names one: 0 is `A`, 26 is `AA`. */
|
|
52
|
+
function columnName(index: number): string;
|
|
53
|
+
/** The six parts of a one-sheet workbook, zipped. */
|
|
54
|
+
function workbook(sheet: Sheet): Uint8Array<ArrayBuffer>;
|
|
55
|
+
}
|
|
56
|
+
export default XlsxUtils;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The half of the ZIP format a spreadsheet needs. An `.xlsx` is a ZIP of XML parts, and the format allows
|
|
3
|
+
* an entry to be *stored* rather than deflated — so a workbook can be written with no compressor and no
|
|
4
|
+
* dependency at all. Excel reads a stored archive exactly as it reads a compressed one; the file is
|
|
5
|
+
* larger, and it is XML, so a gzipped download is the same size either way.
|
|
6
|
+
*
|
|
7
|
+
* Every entry carries the same DOS timestamp (1980-01-01, the epoch the format starts at), which is what
|
|
8
|
+
* makes the same table export byte-for-byte the same file twice.
|
|
9
|
+
*/
|
|
10
|
+
declare namespace ZipUtils {
|
|
11
|
+
/** One file in the archive: the path it is stored under, and its bytes. */
|
|
12
|
+
interface Entry {
|
|
13
|
+
name: string;
|
|
14
|
+
data: Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
/** The checksum every ZIP entry carries, in both of its headers. */
|
|
17
|
+
function crc32(data: Uint8Array): number;
|
|
18
|
+
/** A part's text as the bytes it is stored as. Every part of a workbook is UTF-8 XML. */
|
|
19
|
+
function utf8(text: string): Uint8Array;
|
|
20
|
+
/**
|
|
21
|
+
* The archive, as one buffer: every entry's local header and bytes, then a central directory repeating
|
|
22
|
+
* those headers with each entry's offset, then the record saying where the directory starts.
|
|
23
|
+
*/
|
|
24
|
+
function zip(entries: Entry[]): Uint8Array<ArrayBuffer>;
|
|
25
|
+
}
|
|
26
|
+
export default ZipUtils;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-to-find over a list, which every APG list pattern asks for and all of them the same way: a
|
|
3
|
+
* listbox, a menu, a tree. Framework-free, so the buffer is a plain object the caller keeps.
|
|
4
|
+
*/
|
|
5
|
+
declare namespace TypeaheadUtils {
|
|
6
|
+
/** How long a buffer stays open, per APG. */
|
|
7
|
+
const TIMEOUT = 1000;
|
|
8
|
+
/** What a caller keeps between keystrokes: what has been typed, and when the last key arrived. */
|
|
9
|
+
interface Buffer {
|
|
10
|
+
query: string;
|
|
11
|
+
at: number;
|
|
12
|
+
}
|
|
13
|
+
/** A key that types a character, rather than one that commands something. */
|
|
14
|
+
function isPrintable(event: {
|
|
15
|
+
key: string;
|
|
16
|
+
ctrlKey?: boolean;
|
|
17
|
+
metaKey?: boolean;
|
|
18
|
+
altKey?: boolean;
|
|
19
|
+
}): boolean;
|
|
20
|
+
/** Whether the buffer is still the one the user is typing into. */
|
|
21
|
+
function isOpen(buffer: Buffer, timeStamp: number): boolean;
|
|
22
|
+
/** Adds a keystroke, starting a fresh buffer when the last one has expired, and returns what to search for. */
|
|
23
|
+
function push(buffer: Buffer, key: string, timeStamp: number): string;
|
|
24
|
+
/**
|
|
25
|
+
* The item a query points at, or `-1`. One character — or the same one repeated, which is how a user
|
|
26
|
+
* cycles through a letter — searches from *after* the current item; a longer buffer is a real prefix
|
|
27
|
+
* and searches from it, so further letters narrow instead of skipping.
|
|
28
|
+
*/
|
|
29
|
+
function target(query: string, from: number, count: number, textOf: (index: number) => string, isDisabled?: (index: number) => boolean): number;
|
|
30
|
+
}
|
|
31
|
+
export default TypeaheadUtils;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which item a menu's keyboard lands on, read off the DOM rather than out of a registry: a menu's items
|
|
3
|
+
* are real focusable elements, and their order is the order they are written in — including the ones a
|
|
4
|
+
* consumer wrapped in a group, put behind a condition, or declared in a submenu of its own.
|
|
5
|
+
*/
|
|
6
|
+
declare namespace MenuUtils {
|
|
7
|
+
/** The three roles an item can carry. A separator, a group and a label are not items. */
|
|
8
|
+
const ITEM_SELECTOR = "[role=\"menuitem\"],[role=\"menuitemcheckbox\"],[role=\"menuitemradio\"]";
|
|
9
|
+
/** Whether this panel is the menu a node belongs to, rather than one nested inside it. */
|
|
10
|
+
function owns(panel: Element, node: EventTarget | null): boolean;
|
|
11
|
+
/** The items a panel owns, in DOM order. A submenu's items belong to the submenu, however deep it sits. */
|
|
12
|
+
function items(panel: Element): HTMLElement[];
|
|
13
|
+
/**
|
|
14
|
+
* A disabled item, which is `aria-disabled` and never the `disabled` attribute: APG asks that a
|
|
15
|
+
* disabled menu item stay focusable, so that a keyboard user can find out it is there at all.
|
|
16
|
+
*/
|
|
17
|
+
function isDisabled(item: Element): boolean;
|
|
18
|
+
/** An item that opens a submenu. */
|
|
19
|
+
function isSubTrigger(item: Element): boolean;
|
|
20
|
+
/** The next item in a direction, wrapping at the ends the way a menu does. Nothing is skipped — see `isDisabled`. */
|
|
21
|
+
function step(items: readonly HTMLElement[], from: number, delta: number): HTMLElement | undefined;
|
|
22
|
+
/** The item a typed prefix points at, on the shared APG rules. */
|
|
23
|
+
function typeaheadTarget(items: readonly HTMLElement[], query: string, from: number): HTMLElement | undefined;
|
|
24
|
+
}
|
|
25
|
+
export default MenuUtils;
|