@ex-grid/sheet-react 0.0.0-stage → 0.2.0-rc.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/LICENSE +21 -0
- package/README.md +42 -2
- package/dist/index.d.ts +212 -0
- package/dist/index.js +370 -0
- package/dist/index.js.map +7 -0
- package/package.json +32 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AkiraMisawa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,43 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ex-grid/sheet-react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
ExSheet for React: the Framework Adapter of @ex-grid/sheet, which mounts an Excel-like sheet with formulas in a React tree.
|
|
4
|
+
|
|
5
|
+
It is one of the packages of the ExGrid family: Excel-like grids, sheets, pivot tables and charts for reading
|
|
6
|
+
money and risk numbers, published to npm under `@ex-grid` and to NuGet for Blazor, at one shared version.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install @ex-grid/sheet-react react react-dom
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`react` and `react-dom` are peer dependencies: your application provides them, at the versions `package.json` names.
|
|
15
|
+
|
|
16
|
+
It depends on exactly the version of `@ex-grid/react`, `@ex-grid/sheet` and `@ex-grid/sheet-engine` it was built with, so upgrade the family's packages together.
|
|
17
|
+
|
|
18
|
+
What it draws reads these stylesheets. Import them once in your application:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import '@ex-grid/core/styles.css';
|
|
22
|
+
import '@ex-grid/sheet/styles.css';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Documentation
|
|
26
|
+
|
|
27
|
+
[ExSheet on the documentation site](https://akiramisawa.github.io/ex-grid/exsheet), read for React with the switch at the top, and every
|
|
28
|
+
version's [Release Notes](https://akiramisawa.github.io/ex-grid/release-notes).
|
|
29
|
+
|
|
30
|
+
> **The API may still change.** While the version is `0.x`, it may change from one version to the next; each
|
|
31
|
+
> version's Release Notes say what changed. A prerelease, a version with a suffix such as `-rc.1`, passed every
|
|
32
|
+
> test layer in CI on the commit it was built from. A version without a suffix is published only once the
|
|
33
|
+
> project's [Definition of Done](https://github.com/AkiraMisawa/ex-grid/blob/main/docs/definition-of-done.md) is
|
|
34
|
+
> signed off on it.
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
- **Chrome or Edge.** They are the supported browsers; Safari and Firefox are out of scope.
|
|
39
|
+
- **React 19.0.0 or a later 19.x.**
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExSheet for React: the Framework Adapter of `@ex-grid/sheet` (ADR-0370). `ExSheet` renders the element the Sheet
|
|
3
|
+
* draws into and hands the Sheet its props; everything the Sheet shows and decides is `@ex-grid/sheet`'s.
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ExGridChrome } from '@ex-grid/react';
|
|
9
|
+
import { FormatCellsDraft } from '@ex-grid/sheet';
|
|
10
|
+
import { FormatCellsTab } from '@ex-grid/sheet';
|
|
11
|
+
import { FormEvent } from 'react';
|
|
12
|
+
import { KeyboardEvent as KeyboardEvent_2 } from 'react';
|
|
13
|
+
import { MouseEvent as MouseEvent_2 } from 'react';
|
|
14
|
+
import { ReactElement } from 'react';
|
|
15
|
+
import { ReactNode } from 'react';
|
|
16
|
+
import { Ref } from 'react';
|
|
17
|
+
import { SheetFormatCellsSlotContext } from '@ex-grid/sheet';
|
|
18
|
+
import { SheetHandle } from '@ex-grid/sheet';
|
|
19
|
+
import { SheetOptions } from '@ex-grid/sheet';
|
|
20
|
+
import { SheetTabSlotContext } from '@ex-grid/sheet';
|
|
21
|
+
import { SheetToolbarButton } from '@ex-grid/sheet';
|
|
22
|
+
import { SheetToolbarCommand } from '@ex-grid/sheet';
|
|
23
|
+
import { SheetToolbarContent } from '@ex-grid/sheet';
|
|
24
|
+
import { SheetToolbarItemSlotContext } from '@ex-grid/sheet';
|
|
25
|
+
import { SheetToolbarItemView } from '@ex-grid/sheet';
|
|
26
|
+
import { SheetToolbarRowOptions } from '@ex-grid/sheet';
|
|
27
|
+
import { ToolbarChoiceRow } from '@ex-grid/sheet';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* An Excel-like sheet with formulas. The element it renders is the Sheet's, which `@ex-grid/sheet` owns; every
|
|
31
|
+
* render hands the Sheet the props again, and what did not change costs nothing (ADR-0003).
|
|
32
|
+
*/
|
|
33
|
+
export declare function ExSheet(props: ExSheetProps): ReactElement;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* What a ref to an ExSheet reaches: the Sheet's handle, less what the adapter itself does with it (taking the props
|
|
37
|
+
* again, and destroying the Sheet on unmount) and the hold of the Settled Mark, which is for a Framework Adapter whose
|
|
38
|
+
* Chrome arrives later than its slot (ADR-0410 section 5), as ExGrid's ref leaves it out. Asked of a Sheet not mounted,
|
|
39
|
+
* it throws.
|
|
40
|
+
*/
|
|
41
|
+
export declare type ExSheetApi = Omit<SheetHandle, 'update' | 'destroy' | 'holdSettled'>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The Sheet's Chrome slots for React (ADR-0390 section 3), each a render function of what the Sheet hands that slot,
|
|
45
|
+
* rendered through a portal into the slot the Sheet made. A slot not given is drawn by the built-in Chrome. As the
|
|
46
|
+
* Sheet's `chrome` holds its grid's slots, so does this: ExGrid for React's render functions, which the Sheet hands
|
|
47
|
+
* its grid, beside the Sheet's own. A render function is read when its part is drawn, and its answer is rendered as
|
|
48
|
+
* a component of its own: a hook belongs in a component it renders. Hand the same function on every render, declared
|
|
49
|
+
* outside the component or memoised: a new one draws its parts again.
|
|
50
|
+
*/
|
|
51
|
+
export declare interface ExSheetChrome extends ExGridChrome<unknown> {
|
|
52
|
+
/** Each Toolbar Item of the Sheet Toolbar (ADR-0100): `null` leaves the item to the built-in Chrome, as the C#
|
|
53
|
+
* Chrome's `ToolbarItem` answering null does. The view changes through the context's `subscribe`. */
|
|
54
|
+
readonly toolbarItem?: (context: SheetToolbarItemSlotContext) => ReactNode;
|
|
55
|
+
/** Format Cells' contents (ADR-0071), in the grid's popover frame, drawn with {@link useFormatCells}. */
|
|
56
|
+
readonly formatCells?: (context: SheetFormatCellsSlotContext) => ReactNode;
|
|
57
|
+
/** Each Sheet Tab's face (ADR-0331 §4): the Sheet's name as its only text, taking no keyboard and hearing
|
|
58
|
+
* nothing. */
|
|
59
|
+
readonly sheetTab?: (context: SheetTabSlotContext) => ReactNode;
|
|
60
|
+
/** The face of the button that adds a Sheet (ADR-0331 §4), taking no keyboard and hearing nothing. */
|
|
61
|
+
readonly sheetTabAdd?: () => ReactNode;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* An ExSheet's props: the Sheet's options, whole, its `chrome` as render functions and its toolbar buttons' content
|
|
66
|
+
* as React nodes. `locale` is the browser's
|
|
67
|
+
* language where not given, read once when the Sheet is made (ADR-0370 section 10): a Sheet's culture is recorded in
|
|
68
|
+
* its document, so it is never changed on a live Sheet.
|
|
69
|
+
*/
|
|
70
|
+
export declare type ExSheetProps = Omit<SheetOptions, 'chrome' | 'toolbar'> & {
|
|
71
|
+
/** The Sheet Toolbar's rows, top to bottom (ADR-0100), as `SheetOptions.toolbar`; a button's `content` is a React
|
|
72
|
+
* node. */
|
|
73
|
+
readonly toolbar?: readonly (ExSheetToolbarRow | 'default')[] | undefined;
|
|
74
|
+
/** The parts of the Sheet a Chrome of the Consumer's, or a Wrapper's, draws in place of the built-in ones. */
|
|
75
|
+
readonly chrome?: ExSheetChrome;
|
|
76
|
+
/** A ref to what the Consumer may ask of the Sheet it rendered: its Workbook, its commands and reads, undo and
|
|
77
|
+
* redo, its Linked Tables, and opening and saving a file. */
|
|
78
|
+
readonly ref?: Ref<ExSheetApi>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** A Consumer's own action on the Sheet Toolbar (ADR-0100), the Sheet's button with its content as a React node. */
|
|
82
|
+
export declare interface ExSheetToolbarButton extends Omit<SheetToolbarButton, 'content'> {
|
|
83
|
+
/** Its own content, such as an icon, rendered in place of its text, which still names it: the C# `ToolbarButton`'s
|
|
84
|
+
* `ChildContent`. A node that renders nothing (`null`, `false`, `true`, `''`) leaves the text. A function is the
|
|
85
|
+
* Sheet's own `SheetToolbarContent`, handed on as it is. A `chrome.toolbarItem` slot is handed the content as
|
|
86
|
+
* `view().content`, a function that mounts it into an element and answers what takes it away, which a React 19
|
|
87
|
+
* callback ref can be. */
|
|
88
|
+
readonly content?: ReactNode | SheetToolbarContent;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** One element of a Toolbar Row for React: one of ExSheet's own items by its command, a separator, or the
|
|
92
|
+
* Consumer's own button. */
|
|
93
|
+
export declare type ExSheetToolbarEntry = SheetToolbarCommand | 'separator' | ExSheetToolbarButton;
|
|
94
|
+
|
|
95
|
+
/** A Toolbar Row of the Consumer's for React (ADR-0100): the Sheet's, its buttons' content React nodes. */
|
|
96
|
+
export declare interface ExSheetToolbarRow extends Omit<SheetToolbarRowOptions, 'items'> {
|
|
97
|
+
/** What the row holds, in order. */
|
|
98
|
+
readonly items: readonly ExSheetToolbarEntry[];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** What {@link useFormatCells} hands Format Cells' contents. */
|
|
102
|
+
export declare interface FormatCellsHandles {
|
|
103
|
+
/** The tab shown: the one Format Cells opened on, until another is shown. */
|
|
104
|
+
readonly tab: FormatCellsTab;
|
|
105
|
+
/** Shows another tab, and tells the Sheet, so the next opening shows it again. */
|
|
106
|
+
readonly showTab: (tab: FormatCellsTab) => void;
|
|
107
|
+
/** The ref of the control that stands for the tab shown: it takes the keyboard as Format Cells opens. */
|
|
108
|
+
readonly tabControl: (control: HTMLElement | null) => void;
|
|
109
|
+
/** Makes a choice through the draft, and draws the contents again from it. A refusal OK showed goes once its
|
|
110
|
+
* cause has. */
|
|
111
|
+
readonly set: (choose: (draft: FormatCellsDraft) => void) => void;
|
|
112
|
+
/** Why OK set nothing, in the Sheet's words, while that still stands; `undefined` otherwise. */
|
|
113
|
+
readonly refusal: string | undefined;
|
|
114
|
+
/** OK: sets what was touched, and closes Format Cells; one refused leaves it open and `refusal` saying why. */
|
|
115
|
+
readonly ok: () => void;
|
|
116
|
+
/** Cancel: sets nothing, and closes Format Cells. */
|
|
117
|
+
readonly cancel: () => void;
|
|
118
|
+
/** OK as the submit of a `noValidate` form holding every tab, each tab's fields inside an element whose
|
|
119
|
+
* `data-ex-tab` names it: a field the browser holds invalid, such as a number of places out of range, is shown on
|
|
120
|
+
* its tab and reported there, and nothing is set, whichever tab OK was pressed from; otherwise `ok`. */
|
|
121
|
+
readonly submit: (event: FormEvent<HTMLFormElement>) => void;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** What the element that holds a Toolbar Item's choices takes: the keyboard as it is drawn, and the list's keys. */
|
|
125
|
+
export declare interface ToolbarChoiceListProps {
|
|
126
|
+
/** Hands the keyboard to the Focus cell's choice as the list is drawn. */
|
|
127
|
+
readonly ref: (element: HTMLElement | null) => void;
|
|
128
|
+
/** A menu, as the built-in list is. */
|
|
129
|
+
readonly role: 'menu';
|
|
130
|
+
/** The item's name, which names the list. */
|
|
131
|
+
readonly 'aria-label': string;
|
|
132
|
+
/** The list's keys, the built-in list's: none of them reaches the Sheet or the design system's own list. */
|
|
133
|
+
readonly onKeyDown: (event: KeyboardEvent_2) => void;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** What each choice's control takes: its place among the list's keys, whether it is the Focus cell's, and its press.
|
|
137
|
+
* A choice that cannot be made says so in `aria-disabled` and its title, and stays where the pointer can reach it. */
|
|
138
|
+
export declare interface ToolbarChoiceProps {
|
|
139
|
+
/** Its place among the list's keys. */
|
|
140
|
+
readonly ref: (element: HTMLElement | null) => void;
|
|
141
|
+
/** One of the list's radio choices. */
|
|
142
|
+
readonly role: 'menuitemradio';
|
|
143
|
+
/** Whether the Focus cell has it. */
|
|
144
|
+
readonly 'aria-checked': boolean;
|
|
145
|
+
/** Present where it cannot be made now. */
|
|
146
|
+
readonly 'aria-disabled'?: true;
|
|
147
|
+
/** Out of the Tab order: the list's keys move among the choices. */
|
|
148
|
+
readonly tabIndex: -1;
|
|
149
|
+
/** Its click and keys wait their turn behind a commit or an opening (ADR-0240). */
|
|
150
|
+
readonly 'data-ex-claims': 'click keydown';
|
|
151
|
+
/** Its name, with why it cannot be made while it cannot. */
|
|
152
|
+
readonly title: string;
|
|
153
|
+
/** Makes the choice, where it can be made, and closes the list. */
|
|
154
|
+
readonly onClick: () => void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { ToolbarChoiceRow }
|
|
158
|
+
|
|
159
|
+
/** What every control of a Toolbar Item takes: no Tab, no keyboard on a press, its click claimed, and its name and
|
|
160
|
+
* whether it can be pressed as the Sheet says. */
|
|
161
|
+
export declare interface ToolbarControlProps {
|
|
162
|
+
/** Out of the Tab order: the toolbar is one Tab stop, its keys the Sheet's. */
|
|
163
|
+
readonly tabIndex: -1;
|
|
164
|
+
/** Its click waits its turn behind a commit or an opening (ADR-0240). */
|
|
165
|
+
readonly 'data-ex-claims': 'click';
|
|
166
|
+
/** Whether it cannot be pressed now, as the Sheet says. */
|
|
167
|
+
readonly disabled: boolean;
|
|
168
|
+
/** The tooltip, with why it cannot be pressed while it cannot. */
|
|
169
|
+
readonly title: string;
|
|
170
|
+
/** Keeps the keyboard where it is on a pointer press. */
|
|
171
|
+
readonly onMouseDown: (event: MouseEvent_2) => void;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** What {@link useToolbarItem} hands a Toolbar Item's contents. */
|
|
175
|
+
export declare interface ToolbarItemHandles {
|
|
176
|
+
/** The ref of the element that holds the item's controls and its list: a press outside it closes the list. */
|
|
177
|
+
readonly item: (element: HTMLElement | null) => void;
|
|
178
|
+
/** What the item shows now, drawn again whenever it changes. */
|
|
179
|
+
readonly view: SheetToolbarItemView;
|
|
180
|
+
/** Whether the item has a list: a menu or a split control. */
|
|
181
|
+
readonly hasList: boolean;
|
|
182
|
+
/** Whether its list is open: the frame shows the choices while it is. */
|
|
183
|
+
readonly open: boolean;
|
|
184
|
+
/** What each of the item's controls takes. */
|
|
185
|
+
readonly control: ToolbarControlProps;
|
|
186
|
+
/** A press on the control the list opens from: opens it, or closes it while it is open. */
|
|
187
|
+
readonly toggleList: () => void;
|
|
188
|
+
/** What the element holding the choices takes. */
|
|
189
|
+
readonly list: ToolbarChoiceListProps;
|
|
190
|
+
/** The list's rows: the choices one to a row, and the swatches ten across. */
|
|
191
|
+
readonly rows: readonly ToolbarChoiceRow[];
|
|
192
|
+
/** What the control of the choice at `index` takes. */
|
|
193
|
+
readonly choice: (index: number) => ToolbarChoiceProps;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Draws Format Cells from its slot's context (ADR-0071, ADR-0390): the tab shown, what each choice changed, and the
|
|
198
|
+
* refusal an OK met. Draw the tabs from `formatCellsOffer.tabs`, put `tabControl` on the control of the tab shown,
|
|
199
|
+
* read every control's state from `context.draft`, and make every choice through `set`.
|
|
200
|
+
*/
|
|
201
|
+
export declare function useFormatCells(context: SheetFormatCellsSlotContext): FormatCellsHandles;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Draws a Toolbar Item from its slot's context (ADR-0100, ADR-0390): its view, its list's opening and closing, and the
|
|
205
|
+
* list's keys. Put `item` on the element holding the item and its list, spread `control` on every control the item
|
|
206
|
+
* draws, open the list from `toggleList`, and show the choices in an element taking `list` while `open` holds, laid out
|
|
207
|
+
* by `rows`, one control per choice taking `choice(index)`. Keep the list inside that element and the Sheet's, never
|
|
208
|
+
* in a portal, so the Sheet hears its keys.
|
|
209
|
+
*/
|
|
210
|
+
export declare function useToolbarItem(context: SheetToolbarItemSlotContext): ToolbarItemHandles;
|
|
211
|
+
|
|
212
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
// src/ex-sheet.ts
|
|
2
|
+
import { createElement, Fragment, useImperativeHandle, useLayoutEffect, useRef, useSyncExternalStore } from "react";
|
|
3
|
+
import { chromeMounts, useGridPortals } from "@ex-grid/react";
|
|
4
|
+
import {
|
|
5
|
+
createSheet
|
|
6
|
+
} from "@ex-grid/sheet";
|
|
7
|
+
function ExSheet(props) {
|
|
8
|
+
const { ref, chrome, toolbar, ...given } = props;
|
|
9
|
+
const element = useRef(null);
|
|
10
|
+
const portals = useGridPortals();
|
|
11
|
+
const made = useRef({ slots: /* @__PURE__ */ new Map() });
|
|
12
|
+
const contents = useRef({ given: void 0, made: void 0, held: /* @__PURE__ */ new Map() });
|
|
13
|
+
const sheetChrome = sheetChromeOf(chrome, portals, made.current);
|
|
14
|
+
const sheetToolbar = sheetToolbarOf(toolbar, portals, contents.current);
|
|
15
|
+
const sheetOptions = {
|
|
16
|
+
...given,
|
|
17
|
+
...sheetChrome === void 0 ? {} : { chrome: sheetChrome },
|
|
18
|
+
...sheetToolbar === void 0 ? {} : { toolbar: sheetToolbar }
|
|
19
|
+
};
|
|
20
|
+
const sheet = useRef(void 0);
|
|
21
|
+
const browserLocale = useRef(void 0);
|
|
22
|
+
useLayoutEffect(() => {
|
|
23
|
+
const root = element.current;
|
|
24
|
+
if (root === null) throw new Error("Internal: React mounted no element for the Sheet to draw into.");
|
|
25
|
+
browserLocale.current ??= navigator.language;
|
|
26
|
+
const options = sheetOptions.locale === void 0 ? { ...sheetOptions, locale: browserLocale.current } : sheetOptions;
|
|
27
|
+
if (sheet.current === void 0) {
|
|
28
|
+
portals.begin();
|
|
29
|
+
sheet.current = createSheet(root, options);
|
|
30
|
+
} else {
|
|
31
|
+
sheet.current.update(options);
|
|
32
|
+
}
|
|
33
|
+
for (const content of contents.current.held.values()) content.show();
|
|
34
|
+
});
|
|
35
|
+
useImperativeHandle(ref, () => ({
|
|
36
|
+
get workbook() {
|
|
37
|
+
return mounted(sheet.current).workbook;
|
|
38
|
+
},
|
|
39
|
+
get canUndo() {
|
|
40
|
+
return mounted(sheet.current).canUndo;
|
|
41
|
+
},
|
|
42
|
+
get canRedo() {
|
|
43
|
+
return mounted(sheet.current).canRedo;
|
|
44
|
+
},
|
|
45
|
+
get isEditing() {
|
|
46
|
+
return mounted(sheet.current).isEditing;
|
|
47
|
+
},
|
|
48
|
+
do: (sheetOrEdit, edit) => {
|
|
49
|
+
const handle = mounted(sheet.current);
|
|
50
|
+
if (typeof sheetOrEdit === "string") handle.do(sheetOrEdit, defined(edit));
|
|
51
|
+
else handle.do(sheetOrEdit);
|
|
52
|
+
},
|
|
53
|
+
setCellFormat: (change) => mounted(sheet.current).setCellFormat(change),
|
|
54
|
+
setCellFormatFromFocus: (changeOf) => mounted(sheet.current).setCellFormatFromFocus(changeOf),
|
|
55
|
+
readFocusFormat: () => mounted(sheet.current).readFocusFormat(),
|
|
56
|
+
setCellFormatFrom: (reading, change) => mounted(sheet.current).setCellFormatFrom(reading, change),
|
|
57
|
+
openFormatCells: () => mounted(sheet.current).openFormatCells(),
|
|
58
|
+
cellFormatAt: (sheetOrAddress, address) => {
|
|
59
|
+
const handle = mounted(sheet.current);
|
|
60
|
+
return typeof sheetOrAddress === "string" ? handle.cellFormatAt(sheetOrAddress, defined(address)) : handle.cellFormatAt(sheetOrAddress);
|
|
61
|
+
},
|
|
62
|
+
undo: () => mounted(sheet.current).undo(),
|
|
63
|
+
redo: () => mounted(sheet.current).redo(),
|
|
64
|
+
declareLinkedTable: (name, columns, key) => {
|
|
65
|
+
mounted(sheet.current).declareLinkedTable(name, columns, key);
|
|
66
|
+
},
|
|
67
|
+
pushLinkedTable: (name, rows) => {
|
|
68
|
+
mounted(sheet.current).pushLinkedTable(name, rows);
|
|
69
|
+
},
|
|
70
|
+
open: (read) => mounted(sheet.current).open(read),
|
|
71
|
+
save: (write) => mounted(sheet.current).save(write),
|
|
72
|
+
focus: () => {
|
|
73
|
+
mounted(sheet.current).focus();
|
|
74
|
+
},
|
|
75
|
+
inOrder: (handling) => mounted(sheet.current).inOrder(handling),
|
|
76
|
+
tell: (sentence) => {
|
|
77
|
+
mounted(sheet.current).tell(sentence);
|
|
78
|
+
}
|
|
79
|
+
}), []);
|
|
80
|
+
useLayoutEffect(() => () => {
|
|
81
|
+
portals.end();
|
|
82
|
+
sheet.current?.destroy();
|
|
83
|
+
sheet.current = void 0;
|
|
84
|
+
browserLocale.current = void 0;
|
|
85
|
+
}, []);
|
|
86
|
+
return createElement(Fragment, null, createElement("div", { ref: element }), ...portals.elements());
|
|
87
|
+
}
|
|
88
|
+
function sheetChromeOf(given, portals, made) {
|
|
89
|
+
const chrome = given;
|
|
90
|
+
if (chrome === void 0) return void 0;
|
|
91
|
+
if (typeof chrome !== "object" || chrome === null) throw new TypeError("chrome is an object of Chrome slots, each a render function of its context (ADR-0390).");
|
|
92
|
+
if (made.chrome !== void 0 && made.given === given && made.portals === portals) return made.chrome;
|
|
93
|
+
const { toolbarItem, ...slots } = { ...chrome };
|
|
94
|
+
const sheetChrome = { ...chromeMounts(slots, portals, made.slots) };
|
|
95
|
+
if (toolbarItem !== void 0) {
|
|
96
|
+
if (typeof toolbarItem !== "function") throw new TypeError("chrome.toolbarItem is a render function of the slot's context (ADR-0390).");
|
|
97
|
+
sheetChrome["toolbarItem"] = toolbarItemMount(toolbarItem, portals, made);
|
|
98
|
+
}
|
|
99
|
+
made.given = given;
|
|
100
|
+
made.portals = portals;
|
|
101
|
+
made.chrome = sheetChrome;
|
|
102
|
+
return made.chrome;
|
|
103
|
+
}
|
|
104
|
+
function toolbarItemMount(render, portals, made) {
|
|
105
|
+
const before = made.toolbarItem;
|
|
106
|
+
if (before !== void 0 && before.render === render && before.portals === portals) return before.mount;
|
|
107
|
+
const mount = (slot, context) => {
|
|
108
|
+
const node = Reflect.apply(render, void 0, [context]);
|
|
109
|
+
if (node === null || node === void 0 || node === false) return void 0;
|
|
110
|
+
return portals.mount(slot, () => node);
|
|
111
|
+
};
|
|
112
|
+
made.toolbarItem = { render, portals, mount };
|
|
113
|
+
return mount;
|
|
114
|
+
}
|
|
115
|
+
function rendersNothing(node) {
|
|
116
|
+
return node === void 0 || node === null || typeof node === "boolean" || node === "";
|
|
117
|
+
}
|
|
118
|
+
function sheetToolbarOf(given, portals, made) {
|
|
119
|
+
if (given === made.given) return made.made;
|
|
120
|
+
const rows = given;
|
|
121
|
+
const held = /* @__PURE__ */ new Map();
|
|
122
|
+
const sheetRows = !Array.isArray(rows) ? rows : rows.map((row, r) => {
|
|
123
|
+
if (typeof row !== "object" || row === null) return row;
|
|
124
|
+
const { label, keyTip, items } = row;
|
|
125
|
+
const entries = items;
|
|
126
|
+
if (!Array.isArray(entries)) return row;
|
|
127
|
+
const sheetItems = entries.map((entry, i) => {
|
|
128
|
+
if (typeof entry !== "object" || entry === null) return entry;
|
|
129
|
+
const button = entry;
|
|
130
|
+
const { content } = button;
|
|
131
|
+
if (typeof content === "function") return entry;
|
|
132
|
+
const { text, onPress, keyTip: letters, disabledReason } = button;
|
|
133
|
+
if (rendersNothing(content)) return { text, onPress, keyTip: letters, disabledReason };
|
|
134
|
+
const place = `${String(r)}:${String(i)}`;
|
|
135
|
+
const holding = made.held.get(place) ?? heldContent(portals, content);
|
|
136
|
+
holding.next = content;
|
|
137
|
+
held.set(place, holding);
|
|
138
|
+
return { text, onPress, keyTip: letters, disabledReason, content: holding.mount };
|
|
139
|
+
});
|
|
140
|
+
return sheetItems.some((item, i) => item !== entries[i]) ? { label, keyTip, items: sheetItems } : row;
|
|
141
|
+
});
|
|
142
|
+
made.given = given;
|
|
143
|
+
made.held = held;
|
|
144
|
+
made.made = sheetRows;
|
|
145
|
+
return made.made;
|
|
146
|
+
}
|
|
147
|
+
function heldContent(portals, first) {
|
|
148
|
+
let shown = first;
|
|
149
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
150
|
+
const subscribe = (listener) => {
|
|
151
|
+
listeners.add(listener);
|
|
152
|
+
return () => {
|
|
153
|
+
listeners.delete(listener);
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
const read = () => shown;
|
|
157
|
+
const content = {
|
|
158
|
+
next: first,
|
|
159
|
+
mount: (element) => portals.mount(element, () => createElement(HeldNode, { subscribe, read })),
|
|
160
|
+
show: () => {
|
|
161
|
+
if (content.next === shown) return;
|
|
162
|
+
shown = content.next;
|
|
163
|
+
for (const listener of listeners) listener();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
return content;
|
|
167
|
+
}
|
|
168
|
+
function HeldNode({ subscribe, read }) {
|
|
169
|
+
return useSyncExternalStore(subscribe, read);
|
|
170
|
+
}
|
|
171
|
+
function mounted(sheet) {
|
|
172
|
+
if (sheet === void 0) throw new Error("This ExSheet is not mounted: a Sheet not drawn is asked nothing.");
|
|
173
|
+
return sheet;
|
|
174
|
+
}
|
|
175
|
+
function defined(value) {
|
|
176
|
+
if (value === void 0) throw new TypeError("A command or a read that names its Sheet names what it acts on too.");
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/format-cells.ts
|
|
181
|
+
import { useCallback, useLayoutEffect as useLayoutEffect2, useReducer, useRef as useRef2, useState } from "react";
|
|
182
|
+
import { flushSync } from "react-dom";
|
|
183
|
+
import { formatCellsOffer } from "@ex-grid/sheet";
|
|
184
|
+
function useFormatCells(context) {
|
|
185
|
+
const [tab, setTab] = useState(context.tab);
|
|
186
|
+
const [, drawn] = useReducer((count) => count + 1, 0);
|
|
187
|
+
const [refused, setRefused] = useState(false);
|
|
188
|
+
const control = useRef2(null);
|
|
189
|
+
const opened = useRef2(void 0);
|
|
190
|
+
useLayoutEffect2(() => {
|
|
191
|
+
if (opened.current === context) return;
|
|
192
|
+
opened.current = context;
|
|
193
|
+
if (control.current === null) throw new Error("Format Cells drew no control for the tab shown: put useFormatCells' tabControl on it.");
|
|
194
|
+
context.takeKeyboard(control.current);
|
|
195
|
+
}, [context]);
|
|
196
|
+
const showTab = useCallback((shown) => {
|
|
197
|
+
setTab(shown);
|
|
198
|
+
context.tabShown(shown);
|
|
199
|
+
}, [context]);
|
|
200
|
+
const set = useCallback((choose) => {
|
|
201
|
+
choose(context.draft);
|
|
202
|
+
if (context.draft.refusal === void 0) setRefused(false);
|
|
203
|
+
drawn();
|
|
204
|
+
}, [context]);
|
|
205
|
+
const ok = useCallback(() => {
|
|
206
|
+
if (!context.ok()) setRefused(true);
|
|
207
|
+
}, [context]);
|
|
208
|
+
const submit = useCallback((event) => {
|
|
209
|
+
event.preventDefault();
|
|
210
|
+
const invalid = [...event.currentTarget.querySelectorAll("input, select, textarea")].find((field) => (field instanceof HTMLInputElement || field instanceof HTMLSelectElement || field instanceof HTMLTextAreaElement) && !field.checkValidity());
|
|
211
|
+
if (invalid === void 0) {
|
|
212
|
+
ok();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const holding = formatCellsOffer.tabs.find((each) => each === invalid.closest("[data-ex-tab]")?.dataset["exTab"]);
|
|
216
|
+
if (holding === void 0) throw new Error("A field of Format Cells the browser holds invalid stands in no element marked data-ex-tab with its tab.");
|
|
217
|
+
flushSync(() => {
|
|
218
|
+
showTab(holding);
|
|
219
|
+
});
|
|
220
|
+
invalid.reportValidity();
|
|
221
|
+
}, [ok, showTab]);
|
|
222
|
+
const tabControl = useCallback((element) => {
|
|
223
|
+
control.current = element;
|
|
224
|
+
}, []);
|
|
225
|
+
const standing = context.draft.refusal;
|
|
226
|
+
return {
|
|
227
|
+
tab,
|
|
228
|
+
showTab,
|
|
229
|
+
tabControl,
|
|
230
|
+
set,
|
|
231
|
+
ok,
|
|
232
|
+
submit,
|
|
233
|
+
cancel: context.cancel,
|
|
234
|
+
refusal: refused && standing !== void 0 ? context.words.formatCellsRefusal(standing) : void 0
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/toolbar-item.ts
|
|
239
|
+
import {
|
|
240
|
+
useCallback as useCallback2,
|
|
241
|
+
useLayoutEffect as useLayoutEffect3,
|
|
242
|
+
useRef as useRef3,
|
|
243
|
+
useState as useState2,
|
|
244
|
+
useSyncExternalStore as useSyncExternalStore2
|
|
245
|
+
} from "react";
|
|
246
|
+
import {
|
|
247
|
+
toolbarChoiceMovedTo,
|
|
248
|
+
toolbarChoiceRows,
|
|
249
|
+
toolbarChoiceStart
|
|
250
|
+
} from "@ex-grid/sheet";
|
|
251
|
+
function keepKeyboard(event) {
|
|
252
|
+
event.preventDefault();
|
|
253
|
+
}
|
|
254
|
+
function useToolbarItem(context) {
|
|
255
|
+
const view = useSyncExternalStore2(context.subscribe, context.view);
|
|
256
|
+
const [open, setOpen] = useState2(false);
|
|
257
|
+
const answered = useRef3({ open: 0, close: 0 });
|
|
258
|
+
const holder = useRef3(null);
|
|
259
|
+
const listElement = useRef3(null);
|
|
260
|
+
const options = useRef3([]);
|
|
261
|
+
const optionRefs = useRef3([]);
|
|
262
|
+
const shown = useRef3(view);
|
|
263
|
+
shown.current = view;
|
|
264
|
+
const enabled = view.disabledReason === void 0;
|
|
265
|
+
const hasList = context.kind === "menu" || context.kind === "split";
|
|
266
|
+
useLayoutEffect3(() => {
|
|
267
|
+
const seen = answered.current;
|
|
268
|
+
if (view.closeRequest !== seen.close) {
|
|
269
|
+
answered.current = { open: view.openRequest, close: view.closeRequest };
|
|
270
|
+
setOpen(false);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (view.openRequest === seen.open) return;
|
|
274
|
+
answered.current = { open: view.openRequest, close: seen.close };
|
|
275
|
+
if (hasList && enabled && view.choices.length > 0) setOpen(true);
|
|
276
|
+
else context.choicesClosed(void 0, false);
|
|
277
|
+
}, [view, context, hasList, enabled]);
|
|
278
|
+
const focusAt = (index) => {
|
|
279
|
+
if (index !== void 0) options.current[index]?.focus({ preventScroll: true });
|
|
280
|
+
};
|
|
281
|
+
const listRef = useCallback2((element) => {
|
|
282
|
+
listElement.current = element;
|
|
283
|
+
if (element !== null) focusAt(toolbarChoiceStart(shown.current.choices));
|
|
284
|
+
}, []);
|
|
285
|
+
const choose = (index) => {
|
|
286
|
+
context.choicesClosed(index, false);
|
|
287
|
+
setOpen(false);
|
|
288
|
+
};
|
|
289
|
+
const dismiss = useCallback2(() => {
|
|
290
|
+
if (listElement.current?.contains(document.activeElement) === true) context.choicesClosed(void 0, false);
|
|
291
|
+
setOpen(false);
|
|
292
|
+
}, [context]);
|
|
293
|
+
useLayoutEffect3(() => {
|
|
294
|
+
if (!open) return void 0;
|
|
295
|
+
const outside = (event) => {
|
|
296
|
+
if (event.target instanceof Node && holder.current?.contains(event.target) !== true) dismiss();
|
|
297
|
+
};
|
|
298
|
+
document.addEventListener("mousedown", outside, true);
|
|
299
|
+
document.addEventListener("touchstart", outside, true);
|
|
300
|
+
return () => {
|
|
301
|
+
document.removeEventListener("mousedown", outside, true);
|
|
302
|
+
document.removeEventListener("touchstart", outside, true);
|
|
303
|
+
};
|
|
304
|
+
}, [open, dismiss]);
|
|
305
|
+
const item = useCallback2((element) => {
|
|
306
|
+
holder.current = element;
|
|
307
|
+
}, []);
|
|
308
|
+
const optionRef = (index) => {
|
|
309
|
+
const made = optionRefs.current[index];
|
|
310
|
+
if (made !== void 0) return made;
|
|
311
|
+
const ref = (element) => {
|
|
312
|
+
options.current[index] = element;
|
|
313
|
+
};
|
|
314
|
+
optionRefs.current[index] = ref;
|
|
315
|
+
return ref;
|
|
316
|
+
};
|
|
317
|
+
const onKeyDown = (event) => {
|
|
318
|
+
const at = options.current.findIndex((option) => option === document.activeElement);
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
event.stopPropagation();
|
|
321
|
+
if (event.key === "Escape") {
|
|
322
|
+
context.choicesClosed(void 0, true);
|
|
323
|
+
setOpen(false);
|
|
324
|
+
} else if (event.key === "Enter" || event.key === " ") {
|
|
325
|
+
if (at >= 0 && view.choices[at]?.disabledReason === void 0) choose(at);
|
|
326
|
+
} else focusAt(toolbarChoiceMovedTo(event.key, at < 0 ? -1 : at, view.choices));
|
|
327
|
+
};
|
|
328
|
+
return {
|
|
329
|
+
item,
|
|
330
|
+
view,
|
|
331
|
+
hasList,
|
|
332
|
+
open,
|
|
333
|
+
control: {
|
|
334
|
+
tabIndex: -1,
|
|
335
|
+
"data-ex-claims": "click",
|
|
336
|
+
disabled: !enabled,
|
|
337
|
+
title: enabled ? view.tooltip : `${view.tooltip}: ${view.disabledReason}`,
|
|
338
|
+
onMouseDown: keepKeyboard
|
|
339
|
+
},
|
|
340
|
+
toggleList: () => {
|
|
341
|
+
if (open) dismiss();
|
|
342
|
+
else if (enabled && view.choices.length > 0) setOpen(true);
|
|
343
|
+
},
|
|
344
|
+
list: { ref: listRef, role: "menu", "aria-label": view.name, onKeyDown },
|
|
345
|
+
rows: toolbarChoiceRows(view.choices),
|
|
346
|
+
choice: (index) => {
|
|
347
|
+
const choice = view.choices[index];
|
|
348
|
+
if (choice === void 0) throw new RangeError(`The list of ${view.name} has no choice ${String(index)} (ADR-0100).`);
|
|
349
|
+
const disabled = choice.disabledReason !== void 0;
|
|
350
|
+
return {
|
|
351
|
+
ref: optionRef(index),
|
|
352
|
+
role: "menuitemradio",
|
|
353
|
+
"aria-checked": choice.selected,
|
|
354
|
+
...disabled ? { "aria-disabled": true } : {},
|
|
355
|
+
tabIndex: -1,
|
|
356
|
+
"data-ex-claims": "click keydown",
|
|
357
|
+
title: disabled ? `${choice.label}: ${choice.disabledReason}` : choice.label,
|
|
358
|
+
onClick: () => {
|
|
359
|
+
if (!disabled) choose(index);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
export {
|
|
366
|
+
ExSheet,
|
|
367
|
+
useFormatCells,
|
|
368
|
+
useToolbarItem
|
|
369
|
+
};
|
|
370
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/ex-sheet.ts", "../src/format-cells.ts", "../src/toolbar-item.ts"],
|
|
4
|
+
"sourcesContent": ["// The React Framework Adapter of ExSheet (ADR-0370, section 1): it renders the element the Sheet draws into,\n// hands the Sheet its props, and unmounts it. It holds no state of the Sheet's, draws nothing inside the element,\n// and decides nothing: the Workbook, the undo stack and every word are the Sheet's.\n\nimport { createElement, Fragment, useImperativeHandle, useLayoutEffect, useRef, useSyncExternalStore, type ReactElement, type ReactNode, type Ref } from 'react';\nimport { chromeMounts, GridPortals, useGridPortals, type ChromeMountsMade, type ExGridChrome } from '@ex-grid/react';\nimport {\n createSheet, type SheetChrome, type SheetFormatCellsSlotContext, type SheetHandle, type SheetOptions, type SheetTabSlotContext, type SheetToolbarButton,\n type SheetToolbarCommand, type SheetToolbarContent, type SheetToolbarItemSlot, type SheetToolbarItemSlotContext, type SheetToolbarRowOptions,\n} from '@ex-grid/sheet';\nimport type { CellAddress, SheetEdit } from '@ex-grid/sheet-engine';\n\n/**\n * The Sheet's Chrome slots for React (ADR-0390 section 3), each a render function of what the Sheet hands that slot,\n * rendered through a portal into the slot the Sheet made. A slot not given is drawn by the built-in Chrome. As the\n * Sheet's `chrome` holds its grid's slots, so does this: ExGrid for React's render functions, which the Sheet hands\n * its grid, beside the Sheet's own. A render function is read when its part is drawn, and its answer is rendered as\n * a component of its own: a hook belongs in a component it renders. Hand the same function on every render, declared\n * outside the component or memoised: a new one draws its parts again.\n */\nexport interface ExSheetChrome extends ExGridChrome<unknown> {\n /** Each Toolbar Item of the Sheet Toolbar (ADR-0100): `null` leaves the item to the built-in Chrome, as the C#\n * Chrome's `ToolbarItem` answering null does. The view changes through the context's `subscribe`. */\n readonly toolbarItem?: (context: SheetToolbarItemSlotContext) => ReactNode;\n /** Format Cells' contents (ADR-0071), in the grid's popover frame, drawn with {@link useFormatCells}. */\n readonly formatCells?: (context: SheetFormatCellsSlotContext) => ReactNode;\n /** Each Sheet Tab's face (ADR-0331 \u00A74): the Sheet's name as its only text, taking no keyboard and hearing\n * nothing. */\n readonly sheetTab?: (context: SheetTabSlotContext) => ReactNode;\n /** The face of the button that adds a Sheet (ADR-0331 \u00A74), taking no keyboard and hearing nothing. */\n readonly sheetTabAdd?: () => ReactNode;\n}\n\n/** A Consumer's own action on the Sheet Toolbar (ADR-0100), the Sheet's button with its content as a React node. */\nexport interface ExSheetToolbarButton extends Omit<SheetToolbarButton, 'content'> {\n /** Its own content, such as an icon, rendered in place of its text, which still names it: the C# `ToolbarButton`'s\n * `ChildContent`. A node that renders nothing (`null`, `false`, `true`, `''`) leaves the text. A function is the\n * Sheet's own `SheetToolbarContent`, handed on as it is. A `chrome.toolbarItem` slot is handed the content as\n * `view().content`, a function that mounts it into an element and answers what takes it away, which a React 19\n * callback ref can be. */\n readonly content?: ReactNode | SheetToolbarContent;\n}\n\n/** One element of a Toolbar Row for React: one of ExSheet's own items by its command, a separator, or the\n * Consumer's own button. */\nexport type ExSheetToolbarEntry = SheetToolbarCommand | 'separator' | ExSheetToolbarButton;\n\n/** A Toolbar Row of the Consumer's for React (ADR-0100): the Sheet's, its buttons' content React nodes. */\nexport interface ExSheetToolbarRow extends Omit<SheetToolbarRowOptions, 'items'> {\n /** What the row holds, in order. */\n readonly items: readonly ExSheetToolbarEntry[];\n}\n\n/**\n * An ExSheet's props: the Sheet's options, whole, its `chrome` as render functions and its toolbar buttons' content\n * as React nodes. `locale` is the browser's\n * language where not given, read once when the Sheet is made (ADR-0370 section 10): a Sheet's culture is recorded in\n * its document, so it is never changed on a live Sheet.\n */\nexport type ExSheetProps = Omit<SheetOptions, 'chrome' | 'toolbar'> & {\n /** The Sheet Toolbar's rows, top to bottom (ADR-0100), as `SheetOptions.toolbar`; a button's `content` is a React\n * node. */\n readonly toolbar?: readonly (ExSheetToolbarRow | 'default')[] | undefined;\n /** The parts of the Sheet a Chrome of the Consumer's, or a Wrapper's, draws in place of the built-in ones. */\n readonly chrome?: ExSheetChrome;\n /** A ref to what the Consumer may ask of the Sheet it rendered: its Workbook, its commands and reads, undo and\n * redo, its Linked Tables, and opening and saving a file. */\n readonly ref?: Ref<ExSheetApi>;\n};\n\n/**\n * What a ref to an ExSheet reaches: the Sheet's handle, less what the adapter itself does with it (taking the props\n * again, and destroying the Sheet on unmount) and the hold of the Settled Mark, which is for a Framework Adapter whose\n * Chrome arrives later than its slot (ADR-0410 section 5), as ExGrid's ref leaves it out. Asked of a Sheet not mounted,\n * it throws.\n */\nexport type ExSheetApi = Omit<SheetHandle, 'update' | 'destroy' | 'holdSettled'>;\n\n/**\n * An Excel-like sheet with formulas. The element it renders is the Sheet's, which `@ex-grid/sheet` owns; every\n * render hands the Sheet the props again, and what did not change costs nothing (ADR-0003).\n */\nexport function ExSheet(props: ExSheetProps): ReactElement {\n const { ref, chrome, toolbar, ...given } = props;\n const element = useRef<HTMLDivElement>(null);\n const portals = useGridPortals();\n const made = useRef<MadeMounts>({ slots: new Map() });\n const contents = useRef<MadeToolbar>({ given: undefined, made: undefined, held: new Map() });\n const sheetChrome = sheetChromeOf(chrome, portals, made.current);\n const sheetToolbar = sheetToolbarOf(toolbar, portals, contents.current);\n const sheetOptions: SheetOptions = {\n ...given,\n ...(sheetChrome === undefined ? {} : { chrome: sheetChrome }),\n ...(sheetToolbar === undefined ? {} : { toolbar: sheetToolbar }),\n };\n const sheet = useRef<SheetHandle | undefined>(undefined);\n const browserLocale = useRef<string | undefined>(undefined);\n // Drawn before the browser paints, so a mounted Sheet is never seen empty.\n useLayoutEffect(() => {\n const root = element.current;\n if (root === null) throw new Error('Internal: React mounted no element for the Sheet to draw into.');\n browserLocale.current ??= navigator.language;\n const options = sheetOptions.locale === undefined ? { ...sheetOptions, locale: browserLocale.current } : sheetOptions;\n if (sheet.current === undefined) {\n portals.begin();\n sheet.current = createSheet(root, options);\n } else {\n sheet.current.update(options);\n }\n // The content this render handed is what each button's place shows from now on.\n for (const content of contents.current.held.values()) content.show();\n });\n useImperativeHandle(ref, () => ({\n get workbook() {\n return mounted(sheet.current).workbook;\n },\n get canUndo() {\n return mounted(sheet.current).canUndo;\n },\n get canRedo() {\n return mounted(sheet.current).canRedo;\n },\n get isEditing() {\n return mounted(sheet.current).isEditing;\n },\n do: (sheetOrEdit: string | SheetEdit, edit?: SheetEdit) => {\n const handle = mounted(sheet.current);\n if (typeof sheetOrEdit === 'string') handle.do(sheetOrEdit, defined(edit));\n else handle.do(sheetOrEdit);\n },\n setCellFormat: change => mounted(sheet.current).setCellFormat(change),\n setCellFormatFromFocus: changeOf => mounted(sheet.current).setCellFormatFromFocus(changeOf),\n readFocusFormat: () => mounted(sheet.current).readFocusFormat(),\n setCellFormatFrom: (reading, change) => mounted(sheet.current).setCellFormatFrom(reading, change),\n openFormatCells: () => mounted(sheet.current).openFormatCells(),\n cellFormatAt: (sheetOrAddress: string | CellAddress, address?: CellAddress) => {\n const handle = mounted(sheet.current);\n return typeof sheetOrAddress === 'string' ? handle.cellFormatAt(sheetOrAddress, defined(address)) : handle.cellFormatAt(sheetOrAddress);\n },\n undo: () => mounted(sheet.current).undo(),\n redo: () => mounted(sheet.current).redo(),\n declareLinkedTable: (name, columns, key) => { mounted(sheet.current).declareLinkedTable(name, columns, key); },\n pushLinkedTable: (name, rows) => { mounted(sheet.current).pushLinkedTable(name, rows); },\n open: read => mounted(sheet.current).open(read),\n save: write => mounted(sheet.current).save(write),\n focus: () => { mounted(sheet.current).focus(); },\n inOrder: handling => mounted(sheet.current).inOrder(handling),\n tell: sentence => { mounted(sheet.current).tell(sentence); },\n }), []);\n useLayoutEffect(() => () => {\n // The portals go first: the contents the Sheet takes away as it goes ask for no render of a component gone.\n portals.end();\n sheet.current?.destroy();\n sheet.current = undefined;\n browserLocale.current = undefined;\n }, []);\n return createElement(Fragment, null, createElement('div', { ref: element }), ...portals.elements());\n}\n\n// The mount functions made for the render functions handed, by slot: one handed again keeps its mount, so the\n// Sheet does not draw its parts again on every render. Every slot but the Toolbar Item's is made as ExGrid for React\n// makes its own, the grid's and the Sheet's alike, and the Sheet refuses a name it has not.\ninterface MadeMounts {\n readonly slots: ChromeMountsMade;\n toolbarItem?: { readonly render: unknown; readonly portals: GridPortals; readonly mount: SheetToolbarItemSlot };\n // The Sheet's chrome made last, and what it was made from: the same chrome handed again is the same to the Sheet.\n given?: ExSheetChrome | undefined;\n portals?: GridPortals;\n chrome?: SheetChrome;\n}\n\n// The Sheet's Chrome as the Sheet takes it: each render function as a mount function rendering through the portals.\n// Which slots there are is the Sheet's to check; a render function that is not a function is named here.\nfunction sheetChromeOf(given: ExSheetChrome | undefined, portals: GridPortals, made: MadeMounts): SheetChrome | undefined {\n // Checked as a script may hand it, whatever its type says.\n const chrome: unknown = given;\n if (chrome === undefined) return undefined;\n if (typeof chrome !== 'object' || chrome === null) throw new TypeError('chrome is an object of Chrome slots, each a render function of its context (ADR-0390).');\n if (made.chrome !== undefined && made.given === given && made.portals === portals) return made.chrome;\n const { toolbarItem, ...slots }: Record<string, unknown> = { ...chrome };\n const sheetChrome: Record<string, unknown> = { ...chromeMounts<unknown>(slots, portals, made.slots) };\n if (toolbarItem !== undefined) {\n if (typeof toolbarItem !== 'function') throw new TypeError('chrome.toolbarItem is a render function of the slot\\'s context (ADR-0390).');\n sheetChrome['toolbarItem'] = toolbarItemMount(toolbarItem, portals, made);\n }\n made.given = given;\n made.portals = portals;\n // Each slot a mount function made above; the Sheet checks their names.\n made.chrome = sheetChrome;\n return made.chrome;\n}\n\n// A Toolbar Item's mount: the render read as the part is drawn, as the C# Chrome's ToolbarItem is, so nothing drawn\n// leaves it to the built-in.\nfunction toolbarItemMount(render: unknown, portals: GridPortals, made: MadeMounts): SheetToolbarItemSlot {\n const before = made.toolbarItem;\n if (before !== undefined && before.render === render && before.portals === portals) return before.mount;\n const mount: SheetToolbarItemSlot = (slot, context) => {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- checked a function by the caller, the render the slot's type takes\n const node: ReactNode = Reflect.apply(render as (context: SheetToolbarItemSlotContext) => ReactNode, undefined, [context]);\n if (node === null || node === undefined || node === false) return undefined;\n return portals.mount(slot, () => node);\n };\n made.toolbarItem = { render, portals, mount };\n return mount;\n}\n\n// A button's content as the Sheet takes it: one mount function for the button's place, kept across renders, so the\n// Sheet does not draw the face again on each. What a render hands waits in `next` until that render is committed,\n// and the places mounted hear it then, without the portals rendering again.\ninterface HeldContent {\n next: ReactNode;\n readonly mount: SheetToolbarContent;\n readonly show: () => void;\n}\n\n// The toolbar last handed and what the Sheet was handed for it; the content held, by the button's place.\ninterface MadeToolbar {\n given: ExSheetProps['toolbar'];\n made: SheetOptions['toolbar'];\n held: Map<string, HeldContent>;\n}\n\n// Whether a node renders nothing, which leaves the button its text.\nfunction rendersNothing(node: ReactNode): boolean {\n return node === undefined || node === null || typeof node === 'boolean' || node === '';\n}\n\n// The Sheet's toolbar: each button's React content as a mount function rendering through the portals. The same\n// toolbar handed again is the same toolbar to the Sheet, which then draws nothing again; a row or a button with no\n// React content is handed on as it was given.\nfunction sheetToolbarOf(given: ExSheetProps['toolbar'], portals: GridPortals, made: MadeToolbar): SheetOptions['toolbar'] {\n if (given === made.given) return made.made;\n // Checked by the Sheet as a script may hand it: what is not a list of rows is handed on for it to refuse.\n const rows: unknown = given;\n const held = new Map<string, HeldContent>();\n const sheetRows = !Array.isArray(rows) ? rows : rows.map((row: unknown, r): unknown => {\n if (typeof row !== 'object' || row === null) return row;\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- an object row is a Toolbar Row; the Sheet checks its fields by name\n const { label, keyTip, items } = row as ExSheetToolbarRow;\n const entries: unknown = items;\n if (!Array.isArray(entries)) return row;\n const sheetItems = entries.map((entry: unknown, i): unknown => {\n if (typeof entry !== 'object' || entry === null) return entry;\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- an object entry is a button; the Sheet checks its fields by name\n const button = entry as ExSheetToolbarButton;\n const { content } = button;\n if (typeof content === 'function') return entry;\n // Read through the button, as the Sheet reads it, so a button of a class of the Consumer's keeps its fields.\n const { text, onPress, keyTip: letters, disabledReason } = button;\n if (rendersNothing(content)) return { text, onPress, keyTip: letters, disabledReason };\n const place = `${String(r)}:${String(i)}`;\n const holding = made.held.get(place) ?? heldContent(portals, content);\n holding.next = content;\n held.set(place, holding);\n return { text, onPress, keyTip: letters, disabledReason, content: holding.mount };\n });\n return sheetItems.some((item, i) => item !== entries[i]) ? { label, keyTip, items: sheetItems } : row;\n });\n made.given = given;\n made.held = held;\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the rows as given, each button's React content a mount function; the Sheet checks them by name\n made.made = sheetRows as SheetOptions['toolbar'];\n return made.made;\n}\n\nfunction heldContent(portals: GridPortals, first: ReactNode): HeldContent {\n let shown = first;\n const listeners = new Set<() => void>();\n const subscribe = (listener: () => void): (() => void) => {\n listeners.add(listener);\n return () => { listeners.delete(listener); };\n };\n const read = (): ReactNode => shown;\n const content: HeldContent = {\n next: first,\n mount: element => portals.mount(element, () => createElement(HeldNode, { subscribe, read })),\n show: () => {\n if (content.next === shown) return;\n shown = content.next;\n for (const listener of listeners) listener();\n },\n };\n return content;\n}\n\n// A button's content where it is mounted: the node committed last.\nfunction HeldNode({ subscribe, read }: { readonly subscribe: (listener: () => void) => () => void; readonly read: () => ReactNode }): ReactNode {\n return useSyncExternalStore(subscribe, read);\n}\n\nfunction mounted(sheet: SheetHandle | undefined): SheetHandle {\n if (sheet === undefined) throw new Error('This ExSheet is not mounted: a Sheet not drawn is asked nothing.');\n return sheet;\n}\n\n// The second argument a form of an overload naming its Sheet carries, which the handle's own overload refuses to miss.\nfunction defined<T>(value: T | undefined): T {\n if (value === undefined) throw new TypeError('A command or a read that names its Sheet names what it acts on too.');\n return value;\n}\n", "// What a Chrome's Format Cells needs to stay thin (ADR-0390 section 3, ADR-0071): the tab shown, each choice set\n// through the Sheet's draft and drawn again, the refusal an OK met, and the keyboard taken by the tab shown at the\n// opening. What a choice means and what OK sets are the Sheet's; this holds only what the contents show.\n\nimport { useCallback, useLayoutEffect, useReducer, useRef, useState, type FormEvent } from 'react';\nimport { flushSync } from 'react-dom';\nimport { formatCellsOffer, type FormatCellsDraft, type FormatCellsTab, type SheetFormatCellsSlotContext } from '@ex-grid/sheet';\n\n/** What {@link useFormatCells} hands Format Cells' contents. */\nexport interface FormatCellsHandles {\n /** The tab shown: the one Format Cells opened on, until another is shown. */\n readonly tab: FormatCellsTab;\n /** Shows another tab, and tells the Sheet, so the next opening shows it again. */\n readonly showTab: (tab: FormatCellsTab) => void;\n /** The ref of the control that stands for the tab shown: it takes the keyboard as Format Cells opens. */\n readonly tabControl: (control: HTMLElement | null) => void;\n /** Makes a choice through the draft, and draws the contents again from it. A refusal OK showed goes once its\n * cause has. */\n readonly set: (choose: (draft: FormatCellsDraft) => void) => void;\n /** Why OK set nothing, in the Sheet's words, while that still stands; `undefined` otherwise. */\n readonly refusal: string | undefined;\n /** OK: sets what was touched, and closes Format Cells; one refused leaves it open and `refusal` saying why. */\n readonly ok: () => void;\n /** Cancel: sets nothing, and closes Format Cells. */\n readonly cancel: () => void;\n /** OK as the submit of a `noValidate` form holding every tab, each tab's fields inside an element whose\n * `data-ex-tab` names it: a field the browser holds invalid, such as a number of places out of range, is shown on\n * its tab and reported there, and nothing is set, whichever tab OK was pressed from; otherwise `ok`. */\n readonly submit: (event: FormEvent<HTMLFormElement>) => void;\n}\n\n/**\n * Draws Format Cells from its slot's context (ADR-0071, ADR-0390): the tab shown, what each choice changed, and the\n * refusal an OK met. Draw the tabs from `formatCellsOffer.tabs`, put `tabControl` on the control of the tab shown,\n * read every control's state from `context.draft`, and make every choice through `set`.\n */\nexport function useFormatCells(context: SheetFormatCellsSlotContext): FormatCellsHandles {\n const [tab, setTab] = useState<FormatCellsTab>(context.tab);\n const [, drawn] = useReducer((count: number) => count + 1, 0);\n const [refused, setRefused] = useState(false);\n const control = useRef<HTMLElement | null>(null);\n const opened = useRef<SheetFormatCellsSlotContext | undefined>(undefined);\n // The opening's keyboard goes to the tab shown, once per opening, as soon as it is drawn.\n useLayoutEffect(() => {\n if (opened.current === context) return;\n opened.current = context;\n if (control.current === null) throw new Error('Format Cells drew no control for the tab shown: put useFormatCells\\' tabControl on it.');\n context.takeKeyboard(control.current);\n }, [context]);\n const showTab = useCallback((shown: FormatCellsTab) => {\n setTab(shown);\n context.tabShown(shown);\n }, [context]);\n const set = useCallback((choose: (draft: FormatCellsDraft) => void) => {\n choose(context.draft);\n if (context.draft.refusal === undefined) setRefused(false);\n drawn();\n }, [context]);\n const ok = useCallback(() => {\n if (!context.ok()) setRefused(true);\n }, [context]);\n const submit = useCallback((event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const invalid = [...event.currentTarget.querySelectorAll('input, select, textarea')]\n .find((field): field is HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement =>\n (field instanceof HTMLInputElement || field instanceof HTMLSelectElement || field instanceof HTMLTextAreaElement) && !field.checkValidity());\n if (invalid === undefined) {\n ok();\n return;\n }\n const holding = formatCellsOffer.tabs.find(each => each === invalid.closest<HTMLElement>('[data-ex-tab]')?.dataset['exTab']);\n if (holding === undefined) throw new Error('A field of Format Cells the browser holds invalid stands in no element marked data-ex-tab with its tab.');\n flushSync(() => { showTab(holding); });\n invalid.reportValidity();\n }, [ok, showTab]);\n const tabControl = useCallback((element: HTMLElement | null) => { control.current = element; }, []);\n const standing = context.draft.refusal;\n return {\n tab, showTab, tabControl, set, ok, submit, cancel: context.cancel,\n refusal: refused && standing !== undefined ? context.words.formatCellsRefusal(standing) : undefined,\n };\n}\n", "// What a Chrome's Toolbar Item needs to stay thin (ADR-0390 section 3, ADR-0100): the item's view as the Sheet holds\n// it, its list opened and closed on the toolbar's requests, and the list's keys, which are the built-in list's: the\n// arrows, Home and End move among the choices that can be made, swatches ten across; Enter or Space makes one; Tab keeps\n// the keyboard where it is; Escape closes the list. A choice or Escape is handed back while the keyboard is still in the\n// list, and the Sheet takes it on; a press outside the item closes it. What an item means is the Sheet's; a Wrapper\n// draws the controls and the list's frame, and hands every press and key to these handles, so the same keys do the\n// same thing under every Chrome.\n//\n// Every control takes no Tab and no DOM focus on a pointer press, so the toolbar stays one Tab stop and the keyboard\n// stays on the Sheet, as Excel's ribbon leaves it in the cell, and claims its click (data-ex-claims), so a press made\n// while an opening is still being answered waits its turn (ADR-0240).\n\nimport {\n useCallback, useLayoutEffect, useRef, useState, useSyncExternalStore, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent,\n} from 'react';\nimport {\n toolbarChoiceMovedTo, toolbarChoiceRows, toolbarChoiceStart, type SheetToolbarItemSlotContext, type SheetToolbarItemView, type ToolbarChoiceRow,\n} from '@ex-grid/sheet';\n\nexport type { ToolbarChoiceRow } from '@ex-grid/sheet';\n\n/** What every control of a Toolbar Item takes: no Tab, no keyboard on a press, its click claimed, and its name and\n * whether it can be pressed as the Sheet says. */\nexport interface ToolbarControlProps {\n /** Out of the Tab order: the toolbar is one Tab stop, its keys the Sheet's. */\n readonly tabIndex: -1;\n /** Its click waits its turn behind a commit or an opening (ADR-0240). */\n readonly 'data-ex-claims': 'click';\n /** Whether it cannot be pressed now, as the Sheet says. */\n readonly disabled: boolean;\n /** The tooltip, with why it cannot be pressed while it cannot. */\n readonly title: string;\n /** Keeps the keyboard where it is on a pointer press. */\n readonly onMouseDown: (event: ReactMouseEvent) => void;\n}\n\n/** What the element that holds a Toolbar Item's choices takes: the keyboard as it is drawn, and the list's keys. */\nexport interface ToolbarChoiceListProps {\n /** Hands the keyboard to the Focus cell's choice as the list is drawn. */\n readonly ref: (element: HTMLElement | null) => void;\n /** A menu, as the built-in list is. */\n readonly role: 'menu';\n /** The item's name, which names the list. */\n readonly 'aria-label': string;\n /** The list's keys, the built-in list's: none of them reaches the Sheet or the design system's own list. */\n readonly onKeyDown: (event: ReactKeyboardEvent) => void;\n}\n\n/** What each choice's control takes: its place among the list's keys, whether it is the Focus cell's, and its press.\n * A choice that cannot be made says so in `aria-disabled` and its title, and stays where the pointer can reach it. */\nexport interface ToolbarChoiceProps {\n /** Its place among the list's keys. */\n readonly ref: (element: HTMLElement | null) => void;\n /** One of the list's radio choices. */\n readonly role: 'menuitemradio';\n /** Whether the Focus cell has it. */\n readonly 'aria-checked': boolean;\n /** Present where it cannot be made now. */\n readonly 'aria-disabled'?: true;\n /** Out of the Tab order: the list's keys move among the choices. */\n readonly tabIndex: -1;\n /** Its click and keys wait their turn behind a commit or an opening (ADR-0240). */\n readonly 'data-ex-claims': 'click keydown';\n /** Its name, with why it cannot be made while it cannot. */\n readonly title: string;\n /** Makes the choice, where it can be made, and closes the list. */\n readonly onClick: () => void;\n}\n\n/** What {@link useToolbarItem} hands a Toolbar Item's contents. */\nexport interface ToolbarItemHandles {\n /** The ref of the element that holds the item's controls and its list: a press outside it closes the list. */\n readonly item: (element: HTMLElement | null) => void;\n /** What the item shows now, drawn again whenever it changes. */\n readonly view: SheetToolbarItemView;\n /** Whether the item has a list: a menu or a split control. */\n readonly hasList: boolean;\n /** Whether its list is open: the frame shows the choices while it is. */\n readonly open: boolean;\n /** What each of the item's controls takes. */\n readonly control: ToolbarControlProps;\n /** A press on the control the list opens from: opens it, or closes it while it is open. */\n readonly toggleList: () => void;\n /** What the element holding the choices takes. */\n readonly list: ToolbarChoiceListProps;\n /** The list's rows: the choices one to a row, and the swatches ten across. */\n readonly rows: readonly ToolbarChoiceRow[];\n /** What the control of the choice at `index` takes. */\n readonly choice: (index: number) => ToolbarChoiceProps;\n}\n\n/** Keeps the keyboard where it is on a pointer press. */\nfunction keepKeyboard(event: ReactMouseEvent): void {\n event.preventDefault();\n}\n\n/**\n * Draws a Toolbar Item from its slot's context (ADR-0100, ADR-0390): its view, its list's opening and closing, and the\n * list's keys. Put `item` on the element holding the item and its list, spread `control` on every control the item\n * draws, open the list from `toggleList`, and show the choices in an element taking `list` while `open` holds, laid out\n * by `rows`, one control per choice taking `choice(index)`. Keep the list inside that element and the Sheet's, never\n * in a portal, so the Sheet hears its keys.\n */\nexport function useToolbarItem(context: SheetToolbarItemSlotContext): ToolbarItemHandles {\n const view = useSyncExternalStore(context.subscribe, context.view);\n const [open, setOpen] = useState(false);\n const answered = useRef({ open: 0, close: 0 });\n const holder = useRef<HTMLElement | null>(null);\n const listElement = useRef<HTMLElement | null>(null);\n const options = useRef<(HTMLElement | null)[]>([]);\n // One ref per choice for the item's life, so a render while the list is open does not take each away and back.\n const optionRefs = useRef<((element: HTMLElement | null) => void)[]>([]);\n // Read by the list as it is drawn, which is once per opening, whatever the view is then.\n const shown = useRef(view);\n shown.current = view;\n const enabled = view.disabledReason === undefined;\n const hasList = context.kind === 'menu' || context.kind === 'split';\n // The toolbar's requests: one to close reaches the list asked for, or any opened by a press; one to open opens it,\n // or, where it cannot open, is closed at once choosing nothing.\n useLayoutEffect(() => {\n const seen = answered.current;\n if (view.closeRequest !== seen.close) {\n answered.current = { open: view.openRequest, close: view.closeRequest };\n setOpen(false);\n return;\n }\n if (view.openRequest === seen.open) return;\n answered.current = { open: view.openRequest, close: seen.close };\n if (hasList && enabled && view.choices.length > 0) setOpen(true);\n else context.choicesClosed(undefined, false);\n }, [view, context, hasList, enabled]);\n const focusAt = (index: number | undefined): void => { if (index !== undefined) options.current[index]?.focus({ preventScroll: true }); };\n // The list takes the keyboard as it is drawn.\n const listRef = useCallback((element: HTMLElement | null) => {\n listElement.current = element;\n if (element !== null) focusAt(toolbarChoiceStart(shown.current.choices));\n }, []);\n const choose = (index: number): void => {\n context.choicesClosed(index, false);\n setOpen(false);\n };\n // A press elsewhere closes the list; the Sheet hears it only while the list holds the keyboard, which then goes\n // back to it, and a control pressed outside keeps the keyboard it took.\n const dismiss = useCallback(() => {\n if (listElement.current?.contains(document.activeElement) === true) context.choicesClosed(undefined, false);\n setOpen(false);\n }, [context]);\n // A press outside the item closes its list, heard from the commit that opens it, so a press made at once after\n // the opening is heard too (the spine's sixth rule).\n useLayoutEffect(() => {\n if (!open) return undefined;\n const outside = (event: Event): void => {\n if (event.target instanceof Node && holder.current?.contains(event.target) !== true) dismiss();\n };\n document.addEventListener('mousedown', outside, true);\n document.addEventListener('touchstart', outside, true);\n return () => {\n document.removeEventListener('mousedown', outside, true);\n document.removeEventListener('touchstart', outside, true);\n };\n }, [open, dismiss]);\n const item = useCallback((element: HTMLElement | null) => { holder.current = element; }, []);\n const optionRef = (index: number): ((element: HTMLElement | null) => void) => {\n const made = optionRefs.current[index];\n if (made !== undefined) return made;\n const ref = (element: HTMLElement | null): void => { options.current[index] = element; };\n optionRefs.current[index] = ref;\n return ref;\n };\n const onKeyDown = (event: ReactKeyboardEvent): void => {\n const at = options.current.findIndex(option => option === document.activeElement);\n event.preventDefault();\n event.stopPropagation();\n if (event.key === 'Escape') {\n context.choicesClosed(undefined, true);\n setOpen(false);\n } else if (event.key === 'Enter' || event.key === ' ') {\n if (at >= 0 && view.choices[at]?.disabledReason === undefined) choose(at);\n } else focusAt(toolbarChoiceMovedTo(event.key, at < 0 ? -1 : at, view.choices));\n };\n return {\n item, view, hasList, open,\n control: {\n tabIndex: -1, 'data-ex-claims': 'click', disabled: !enabled, title: enabled ? view.tooltip : `${view.tooltip}: ${view.disabledReason}`,\n onMouseDown: keepKeyboard,\n },\n toggleList: () => {\n if (open) dismiss();\n else if (enabled && view.choices.length > 0) setOpen(true);\n },\n list: { ref: listRef, role: 'menu', 'aria-label': view.name, onKeyDown },\n rows: toolbarChoiceRows(view.choices),\n choice: index => {\n const choice = view.choices[index];\n if (choice === undefined) throw new RangeError(`The list of ${view.name} has no choice ${String(index)} (ADR-0100).`);\n const disabled = choice.disabledReason !== undefined;\n return {\n ref: optionRef(index), role: 'menuitemradio', 'aria-checked': choice.selected,\n ...(disabled ? { 'aria-disabled': true } : {}), tabIndex: -1, 'data-ex-claims': 'click keydown',\n title: disabled ? `${choice.label}: ${choice.disabledReason}` : choice.label,\n onClick: () => { if (!disabled) choose(index); },\n };\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["useLayoutEffect", "useRef", "useCallback", "useLayoutEffect", "useRef", "useState", "useSyncExternalStore"]
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ex-grid/sheet-react",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0-rc.1",
|
|
4
|
+
"description": "ExSheet for React: the Framework Adapter of @ex-grid/sheet, which mounts an Excel-like sheet with formulas in a React tree.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/AkiraMisawa/ex-grid.git",
|
|
9
|
+
"directory": "packages/sheet-react"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://akiramisawa.github.io/ex-grid/",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/index.js",
|
|
22
|
+
"dist/index.js.map",
|
|
23
|
+
"dist/index.d.ts"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@ex-grid/react": "0.2.0-rc.1",
|
|
27
|
+
"@ex-grid/sheet": "0.2.0-rc.1",
|
|
28
|
+
"@ex-grid/sheet-engine": "0.2.0-rc.1"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"react": "^19.0.0",
|
|
32
|
+
"react-dom": "^19.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|