@ex-grid/pivot-mui 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 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,44 @@
1
- # Temporary Holding Version
1
+ # @ex-grid/pivot-mui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ExPivot for MUI: the design-system Wrapper that draws ExPivot for React's Chrome — the Field List, the Pivot Toolbar, its menus and panels, the report filters, the Stale Report's notice and Show Details' tabs and dialog — with MUI's components, and maps the pivot's Visual Tokens onto a MUI theme made with CSS variables.
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/pivot-mui @emotion/react @emotion/styled @mui/material react react-dom
12
+ ```
13
+
14
+ `@emotion/react`, `@emotion/styled`, `@mui/material`, `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/mui`, `@ex-grid/pivot` and `@ex-grid/pivot-react` 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, after the design system's own:
19
+
20
+ ```ts
21
+ import '@ex-grid/core/styles.css';
22
+ import '@ex-grid/pivot/styles.css';
23
+ import '@ex-grid/pivot-mui/styles.css';
24
+ ```
25
+
26
+ ## Documentation
27
+
28
+ [ExPivot on the documentation site](https://akiramisawa.github.io/ex-grid/expivot), read for React with the switch at the top, and every
29
+ version's [Release Notes](https://akiramisawa.github.io/ex-grid/release-notes).
30
+
31
+ > **The API may still change.** While the version is `0.x`, it may change from one version to the next; each
32
+ > version's Release Notes say what changed. A prerelease, a version with a suffix such as `-rc.1`, passed every
33
+ > test layer in CI on the commit it was built from. A version without a suffix is published only once the
34
+ > project's [Definition of Done](https://github.com/AkiraMisawa/ex-grid/blob/main/docs/definition-of-done.md) is
35
+ > signed off on it.
36
+
37
+ ## Requirements
38
+
39
+ - **Chrome or Edge.** They are the supported browsers; Safari and Firefox are out of scope.
40
+ - **React 19.2.0 or a later 19.x.**
41
+
42
+ ## License
43
+
44
+ MIT
@@ -0,0 +1,45 @@
1
+ /**
2
+ * ExPivot for MUI (ADR-0390): `MuiPivot` draws ExPivot for React inside MUI's Paper, and `muiPivotChrome` draws its
3
+ * Chrome slots — the Field List, the Pivot Toolbar, its menus and panels, the report filters, the Stale Report's notice
4
+ * and Show Details' tabs and dialog — in MUI's components. Its stylesheet, `@ex-grid/pivot-mui/styles.css`, maps the
5
+ * pivot's Visual Tokens onto a MUI theme made with CSS variables, so a change of scheme recolours the pivot with no
6
+ * render.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+
11
+ import { CSSProperties } from 'react';
12
+ import { ExPivotChrome } from '@ex-grid/pivot-react';
13
+ import { ExPivotProps } from '@ex-grid/pivot-react';
14
+ import { ReactElement } from 'react';
15
+
16
+ /**
17
+ * Excel's PivotTable drawn with MUI: the Chrome slots a `chrome` prop leaves, the pivot's and its report grid's, are
18
+ * MUI's, and the colours follow the theme and its scheme with no render. Needs a ThemeProvider above it whose theme is
19
+ * made with `createTheme({ cssVariables: true })`, and `@ex-grid/mui/styles.css` and `@ex-grid/pivot-mui/styles.css`
20
+ * loaded.
21
+ */
22
+ export declare function MuiPivot(props: MuiPivotProps): ReactElement;
23
+
24
+ /** ExPivot's Chrome drawn in MUI's components, but for the report's grid: what {@link MuiPivot} hands its pivot, with
25
+ * `@ex-grid/mui`'s `muiChrome` as `grid`. A Consumer that frames an ExPivot itself spreads both into its `chrome`,
26
+ * inside an element of the classes `mui-ex-grid mui-ex-pivot` and a MuiSizeContext, under a ThemeProvider whose theme
27
+ * was made with `cssVariables: true`, as MuiPivot draws them. */
28
+ export declare const muiPivotChrome: Omit<ExPivotChrome, 'grid'>;
29
+
30
+ /** A MuiPivot's props: an ExPivot's, and the words MUI's Paper takes. */
31
+ export declare type MuiPivotProps = ExPivotProps & {
32
+ /** The Paper's variant: `outlined`, the default here, draws a border; `elevation` a shadow, and under the dark
33
+ * scheme MUI lightens an elevated Paper with an overlay the report's own ground does not take. */
34
+ readonly variant?: 'elevation' | 'outlined';
35
+ /** The Paper's shadow depth, 0 to 24, under the `elevation` variant. */
36
+ readonly elevation?: number;
37
+ /** Square corners: the Paper's own word, and the pivot set square in it with no inset. */
38
+ readonly square?: boolean;
39
+ /** A class for the Paper. */
40
+ readonly className?: string;
41
+ /** A style for the Paper. */
42
+ readonly style?: CSSProperties;
43
+ };
44
+
45
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,604 @@
1
+ // src/pivot-chrome.ts
2
+ import { createElement, useContext, useId } from "react";
3
+ import {
4
+ Alert,
5
+ Button,
6
+ Checkbox,
7
+ CircularProgress,
8
+ FormControlLabel,
9
+ IconButton,
10
+ ListItemButton,
11
+ Paper,
12
+ Radio,
13
+ RadioGroup,
14
+ Tab,
15
+ Tabs,
16
+ TextField,
17
+ Typography
18
+ } from "@mui/material";
19
+ import { useTheme } from "@mui/material/styles";
20
+ import { MuiSizeContext, refuseThemeWithoutVariables } from "@ex-grid/mui";
21
+ import { usePivotMenu, usePivotPanel, usePivotPart } from "@ex-grid/pivot-react";
22
+ var claimsClick = { "data-ex-claims": "click" };
23
+ var claimsNothing = { "data-ex-claims": "" };
24
+ function useMuiSize() {
25
+ refuseThemeWithoutVariables(useTheme(), "muiPivotChrome");
26
+ return useContext(MuiSizeContext);
27
+ }
28
+ var textVariant = (size) => size === "small" ? "body2" : "body1";
29
+ var opens = createElement("span", { "aria-hidden": true, className: "mui-ex-pivot-opens" }, "\u25BE");
30
+ var cross = createElement(
31
+ "svg",
32
+ { viewBox: "0 0 16 16", width: 12, height: 12, "aria-hidden": true, focusable: false },
33
+ createElement("path", { d: "M4 4l8 8M12 4l-8 8", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round" })
34
+ );
35
+ function labelled(label, control, size, claims, key) {
36
+ return createElement(FormControlLabel, {
37
+ ...key === void 0 ? {} : { key },
38
+ label,
39
+ control,
40
+ className: "mui-ex-pivot-label",
41
+ ...claims,
42
+ slotProps: { typography: { variant: textVariant(size) } }
43
+ });
44
+ }
45
+ function MuiPivotMenu({ context }) {
46
+ const { view, item } = usePivotMenu(context);
47
+ const { size, dense } = useMuiSize();
48
+ return createElement("div", { role: "none", className: "mui-ex-pivot-menu" }, ...view.commands.flatMap((command, index) => [
49
+ command.heading === void 0 ? null : createElement(Typography, {
50
+ key: `${command.id}-heading`,
51
+ component: "div",
52
+ variant: textVariant(size),
53
+ color: "textSecondary",
54
+ className: "mui-ex-pivot-menu-heading",
55
+ "aria-hidden": true
56
+ }, command.heading),
57
+ createElement(
58
+ ListItemButton,
59
+ {
60
+ ...item(index),
61
+ key: command.id,
62
+ component: "button",
63
+ type: "button",
64
+ dense,
65
+ className: "mui-ex-pivot-menu-item"
66
+ },
67
+ // The mark is the stylesheet's, read off aria-checked, so the command's words stay the pivot's.
68
+ command.checked === void 0 ? null : createElement("span", { className: "mui-ex-pivot-menu-mark", "aria-hidden": true }),
69
+ command.label
70
+ )
71
+ ]));
72
+ }
73
+ function panelActions(context, size, canApply = true) {
74
+ return createElement(
75
+ "div",
76
+ { className: "mui-ex-pivot-actions" },
77
+ createElement(Button, { type: "button", size, variant: "contained", disabled: !canApply, onClick: context.apply, ...claimsClick }, context.label("ok")),
78
+ createElement(Button, { type: "button", size, variant: "outlined", onClick: context.cancel, ...claimsClick }, context.label("cancel"))
79
+ );
80
+ }
81
+ function offered(value, choices, what) {
82
+ const found = choices.find((choice) => choice.value === value);
83
+ if (found === void 0) throw new RangeError(`'${value}' is not ${what} the panel offers.`);
84
+ return found.value;
85
+ }
86
+ function nativeSelect(label, value, options, change, size) {
87
+ return createElement(TextField, {
88
+ size,
89
+ select: true,
90
+ label,
91
+ value,
92
+ fullWidth: true,
93
+ ...claimsNothing,
94
+ slotProps: { select: { native: true }, inputLabel: { shrink: true } },
95
+ onChange: (event) => {
96
+ change(event.target.value);
97
+ }
98
+ }, ...options.map((option) => createElement("option", { key: option.value, value: option.value, disabled: option.disabled === true }, option.label)));
99
+ }
100
+ function MuiFieldSettings({ context }) {
101
+ const { view, first } = usePivotPanel(context);
102
+ const { size } = useMuiSize();
103
+ const subtotals = useId();
104
+ return createElement(
105
+ "div",
106
+ { className: "mui-ex-pivot-panel" },
107
+ createElement(Typography, { component: "div", variant: textVariant(size), className: "mui-ex-pivot-strong" }, view.caption),
108
+ createElement(
109
+ RadioGroup,
110
+ {
111
+ name: subtotals,
112
+ value: view.subtotals ? "automatic" : "none",
113
+ onChange: (_event, value) => {
114
+ context.setSubtotals(value === "automatic");
115
+ }
116
+ },
117
+ labelled(context.label("subtotals-automatic"), createElement(Radio, { size, value: "automatic", slotProps: { input: { ref: first } } }), size, claimsNothing),
118
+ labelled(context.label("subtotals-none"), createElement(Radio, { size, value: "none" }), size, claimsNothing)
119
+ ),
120
+ nativeSelect(
121
+ context.label("sort-order"),
122
+ String(view.sort),
123
+ view.sorts.map((sort, index) => ({ value: String(index), label: sort })),
124
+ (value) => {
125
+ context.setSort(Number(value));
126
+ },
127
+ size
128
+ ),
129
+ panelActions(context, size)
130
+ );
131
+ }
132
+ function MuiValueFieldSettings({ context }) {
133
+ const { view, first } = usePivotPanel(context);
134
+ const { size } = useMuiSize();
135
+ return createElement(
136
+ "div",
137
+ { className: "mui-ex-pivot-panel" },
138
+ createElement(Typography, { component: "div", variant: textVariant(size), color: "textSecondary" }, view.sourceCaption),
139
+ createElement(TextField, {
140
+ size,
141
+ inputRef: first,
142
+ label: context.label("custom-name"),
143
+ value: view.caption,
144
+ fullWidth: true,
145
+ ...claimsNothing,
146
+ slotProps: { inputLabel: { shrink: true } },
147
+ onChange: (event) => {
148
+ context.setCaption(event.target.value);
149
+ }
150
+ }),
151
+ nativeSelect(
152
+ context.label("summarize-by"),
153
+ view.aggregation,
154
+ view.aggregations.map((choice) => ({ value: choice.value, label: choice.label, disabled: !choice.enabled })),
155
+ (value) => {
156
+ context.setAggregation(offered(value, view.aggregations, "an Aggregation"));
157
+ },
158
+ size
159
+ ),
160
+ nativeSelect(
161
+ context.label("show-values-as"),
162
+ view.showValuesAs,
163
+ view.showValuesAsChoices,
164
+ (value) => {
165
+ context.setShowValuesAs(offered(value, view.showValuesAsChoices, "a Show Values As"));
166
+ },
167
+ size
168
+ ),
169
+ createElement(TextField, {
170
+ size,
171
+ label: context.label("number-format"),
172
+ value: view.numberFormat,
173
+ placeholder: view.numberFormatPlaceholder,
174
+ fullWidth: true,
175
+ ...claimsNothing,
176
+ slotProps: { inputLabel: { shrink: true } },
177
+ onChange: (event) => {
178
+ context.setNumberFormat(event.target.value);
179
+ }
180
+ }),
181
+ createElement(Typography, { component: "div", variant: textVariant(size), color: "textSecondary", className: "mui-ex-pivot-sample" }, view.sample),
182
+ view.refusal === void 0 ? null : createElement(Alert, { severity: "error" }, view.refusal),
183
+ panelActions(context, size)
184
+ );
185
+ }
186
+ function MuiItemFilter({ context }) {
187
+ const { view, first } = usePivotPanel(context);
188
+ const { size } = useMuiSize();
189
+ const list = view.unavailable !== void 0 ? createElement(Alert, { severity: "warning" }, view.unavailable) : view.loading ? createElement(
190
+ "div",
191
+ { className: "mui-ex-pivot-loading" },
192
+ createElement(CircularProgress, { size: size === "small" ? 16 : 20 }),
193
+ createElement(Typography, { component: "span", variant: textVariant(size) }, context.label("loading"))
194
+ ) : createElement(
195
+ "div",
196
+ { role: "group", "aria-label": view.caption, "aria-busy": view.updating, className: "mui-ex-pivot-items" },
197
+ view.items.length === 0 ? null : labelled(context.label("select-all"), createElement(Checkbox, {
198
+ size,
199
+ checked: view.allTicked === true,
200
+ indeterminate: view.allTicked === void 0,
201
+ onChange: (event) => {
202
+ context.setAll(event.target.checked);
203
+ }
204
+ }), size, claimsNothing),
205
+ ...view.items.map((item, at) => labelled(item.label, createElement(Checkbox, {
206
+ size,
207
+ checked: item.ticked,
208
+ onChange: (event) => {
209
+ context.setTicked(item.key, event.target.checked);
210
+ }
211
+ }), size, claimsNothing, at)),
212
+ view.note === void 0 ? null : createElement(Typography, { component: "div", variant: textVariant(size), color: "textSecondary" }, view.note)
213
+ );
214
+ return createElement(
215
+ "div",
216
+ { className: "mui-ex-pivot-panel" },
217
+ createElement(TextField, {
218
+ size,
219
+ inputRef: first,
220
+ type: "search",
221
+ placeholder: context.label("search-items"),
222
+ value: view.search,
223
+ fullWidth: true,
224
+ ...claimsNothing,
225
+ slotProps: { htmlInput: { "aria-label": context.label("search-items") } },
226
+ onChange: (event) => {
227
+ context.setSearch(event.target.value);
228
+ }
229
+ }),
230
+ list,
231
+ view.refusal === void 0 ? null : createElement(Alert, { severity: "error" }, view.refusal),
232
+ panelActions(context, size, view.canApply)
233
+ );
234
+ }
235
+ function MuiPivotToolbar({ context }) {
236
+ const view = usePivotPart(context);
237
+ const { size } = useMuiSize();
238
+ return createElement(
239
+ "div",
240
+ { className: "mui-ex-pivot-toolbar-frame" },
241
+ createElement(
242
+ "div",
243
+ { role: "toolbar", className: "mui-ex-pivot-toolbar" },
244
+ createElement("div", { ref: context.anchor("report-filters"), className: "mui-ex-pivot-toolbar-start" }),
245
+ createElement(
246
+ "div",
247
+ { className: "mui-ex-pivot-toolbar-end" },
248
+ createElement("span", { ref: context.anchor("layout"), className: "mui-ex-pivot-anchor" }, createElement(Button, {
249
+ ref: context.control("layout"),
250
+ type: "button",
251
+ size,
252
+ variant: view.layoutMenu.open ? "outlined" : "text",
253
+ className: "mui-ex-pivot-button",
254
+ endIcon: opens,
255
+ "aria-haspopup": "menu",
256
+ "aria-expanded": view.layoutMenu.open,
257
+ onClick: context.toggleLayoutMenu,
258
+ ...claimsClick
259
+ }, view.layoutMenu.label)),
260
+ view.refresh === void 0 ? null : createElement(Button, {
261
+ ref: context.control("refresh"),
262
+ type: "button",
263
+ size,
264
+ variant: "text",
265
+ className: "mui-ex-pivot-button",
266
+ onClick: context.refresh,
267
+ ...claimsClick
268
+ }, view.refresh.label),
269
+ createElement(Button, {
270
+ ref: context.control("field-list"),
271
+ type: "button",
272
+ size,
273
+ variant: view.fieldList.shown ? "outlined" : "text",
274
+ className: "mui-ex-pivot-button",
275
+ "aria-pressed": view.fieldList.shown,
276
+ onClick: context.toggleFieldList,
277
+ ...claimsClick
278
+ }, view.fieldList.label)
279
+ )
280
+ ),
281
+ view.refusal === void 0 ? null : createElement(Alert, { severity: "error", className: "mui-ex-pivot-refusal" }, view.refusal)
282
+ );
283
+ }
284
+ function MuiReportFilters({ context }) {
285
+ const view = usePivotPart(context);
286
+ const { size } = useMuiSize();
287
+ return createElement("div", { className: "mui-ex-pivot-filters" }, ...view.filters.map((filter) => createElement(
288
+ "div",
289
+ {
290
+ key: filter.field,
291
+ className: "mui-ex-pivot-filter"
292
+ },
293
+ createElement(Typography, { component: "span", variant: textVariant(size), className: "mui-ex-pivot-strong mui-ex-pivot-filter-caption" }, filter.caption),
294
+ createElement("span", { ref: context.anchor(filter.field), className: "mui-ex-pivot-anchor mui-ex-pivot-filter-anchor" }, createElement(Button, {
295
+ ref: context.control(filter.field),
296
+ type: "button",
297
+ size,
298
+ variant: "outlined",
299
+ color: filter.filtered ? "primary" : "inherit",
300
+ fullWidth: true,
301
+ className: "mui-ex-pivot-button mui-ex-pivot-caption-button",
302
+ endIcon: opens,
303
+ "aria-label": filter.buttonLabel,
304
+ "aria-haspopup": "dialog",
305
+ "aria-expanded": filter.open,
306
+ onClick: () => {
307
+ context.toggle(filter.field);
308
+ },
309
+ ...claimsClick
310
+ }, createElement("span", { className: "mui-ex-pivot-caption" }, filter.summary)))
311
+ )));
312
+ }
313
+ function MuiStaleReport({ context }) {
314
+ const view = usePivotPart(context);
315
+ const { size } = useMuiSize();
316
+ return createElement(Alert, {
317
+ severity: "warning",
318
+ className: "mui-ex-pivot-stale",
319
+ action: createElement(Button, {
320
+ ref: context.control("retry"),
321
+ type: "button",
322
+ size,
323
+ color: "inherit",
324
+ disabled: !view.retry.enabled,
325
+ onClick: context.retry,
326
+ ...claimsClick
327
+ }, view.retry.label)
328
+ }, view.message);
329
+ }
330
+ function dropTarget(context, area, index) {
331
+ return {
332
+ onDragOver: (event) => {
333
+ context.dragOver(event.nativeEvent);
334
+ },
335
+ onDragEnter: (event) => {
336
+ event.stopPropagation();
337
+ context.dragEnter(area, index);
338
+ },
339
+ onDrop: (event) => {
340
+ event.stopPropagation();
341
+ context.drop(event.nativeEvent, area, index);
342
+ }
343
+ };
344
+ }
345
+ function MuiFieldList({ context }) {
346
+ const view = usePivotPart(context);
347
+ const { size } = useMuiSize();
348
+ const variant = textVariant(size);
349
+ const fields = view.fields.map((field) => createElement(
350
+ "div",
351
+ {
352
+ key: field.name,
353
+ role: "listitem",
354
+ draggable: true,
355
+ className: field.dragging ? "mui-ex-pivot-field mui-ex-pivot-dragging" : "mui-ex-pivot-field",
356
+ onDragStart: (event) => {
357
+ context.startDrag(event.nativeEvent, { field: field.name });
358
+ },
359
+ onDragEnd: context.endDrag
360
+ },
361
+ labelled(field.caption, createElement(Checkbox, {
362
+ size,
363
+ slotProps: { input: { ref: context.control(field.key) } },
364
+ checked: field.placed,
365
+ onChange: () => {
366
+ context.toggle(field.name);
367
+ }
368
+ }), size, claimsClick),
369
+ field.filtered ? createElement(Typography, { component: "span", variant, color: "textSecondary", "aria-hidden": true }, "\u23F7") : null,
370
+ field.count === void 0 ? null : createElement(Typography, {
371
+ component: "span",
372
+ variant,
373
+ color: "textSecondary",
374
+ title: field.countLabel,
375
+ "aria-label": field.countLabel
376
+ }, field.count)
377
+ ));
378
+ const areas = view.areas.map((area) => createElement(
379
+ Paper,
380
+ {
381
+ key: area.area,
382
+ ref: context.area(area.area),
383
+ variant: "outlined",
384
+ ...dropTarget(context, area.area, area.end),
385
+ className: area.dropTarget ? "mui-ex-pivot-area mui-ex-pivot-drop-target" : "mui-ex-pivot-area"
386
+ },
387
+ createElement(Typography, { component: "div", variant, className: "mui-ex-pivot-strong mui-ex-pivot-area-title" }, area.title),
388
+ area.overCap === void 0 ? null : createElement(Typography, { component: "div", variant, color: "error", role: "status" }, area.overCap),
389
+ createElement("div", { role: "list", "aria-label": area.title, className: "mui-ex-pivot-entries" }, ...area.entries.map((entry) => {
390
+ const classes = ["mui-ex-pivot-entry"];
391
+ if (entry.dragging) classes.push("mui-ex-pivot-dragging");
392
+ if (entry.dropBefore) classes.push("mui-ex-pivot-drop-before");
393
+ return createElement("div", {
394
+ key: entry.key,
395
+ role: "listitem",
396
+ ref: context.anchor(entry.key),
397
+ draggable: !entry.menuOpen,
398
+ className: classes.join(" "),
399
+ ...dropTarget(context, area.area, entry.dropIndex),
400
+ onDragStart: (event) => {
401
+ context.startDrag(event.nativeEvent, { entry: entry.entry });
402
+ },
403
+ onDragEnd: context.endDrag
404
+ }, createElement(Button, {
405
+ ref: context.control(entry.key),
406
+ type: "button",
407
+ size,
408
+ variant: "outlined",
409
+ color: entry.filtered ? "primary" : "inherit",
410
+ fullWidth: true,
411
+ className: "mui-ex-pivot-button mui-ex-pivot-caption-button",
412
+ endIcon: opens,
413
+ "aria-label": entry.menuLabel,
414
+ "aria-haspopup": "menu",
415
+ "aria-expanded": entry.menuOpen,
416
+ onClick: () => {
417
+ context.openMenu(entry.entry);
418
+ },
419
+ ...claimsClick
420
+ }, createElement("span", { className: "mui-ex-pivot-caption" }, entry.caption)));
421
+ }))
422
+ ));
423
+ return createElement(
424
+ "div",
425
+ { className: "mui-ex-pivot-pane" },
426
+ createElement(
427
+ "div",
428
+ { className: "mui-ex-pivot-pane-heading" },
429
+ createElement(Typography, { component: "span", variant, className: "mui-ex-pivot-strong mui-ex-pivot-pane-title" }, view.title),
430
+ createElement(IconButton, { size: "small", "aria-label": view.hideLabel, title: view.hideLabel, onClick: context.close, ...claimsClick }, cross)
431
+ ),
432
+ createElement(
433
+ "div",
434
+ { className: "mui-ex-pivot-pane-body" },
435
+ createElement(Typography, { component: "div", variant, color: "textSecondary" }, view.chooseCaption),
436
+ createElement(TextField, {
437
+ inputRef: context.control("search"),
438
+ size,
439
+ type: "search",
440
+ placeholder: view.searchLabel,
441
+ value: view.search,
442
+ fullWidth: true,
443
+ ...claimsNothing,
444
+ slotProps: { htmlInput: { "aria-label": view.searchLabel } },
445
+ onChange: (event) => {
446
+ context.setSearch(event.target.value);
447
+ }
448
+ }),
449
+ createElement("div", {
450
+ role: "list",
451
+ "aria-label": view.title,
452
+ className: view.dropRemoves ? "mui-ex-pivot-fields mui-ex-pivot-drop-remove" : "mui-ex-pivot-fields",
453
+ onDragOver: (event) => {
454
+ context.dragOver(event.nativeEvent);
455
+ },
456
+ onDrop: (event) => {
457
+ context.dropOnList(event.nativeEvent);
458
+ }
459
+ }, ...fields),
460
+ createElement(Typography, { component: "div", variant, color: "textSecondary" }, view.dragCaption),
461
+ createElement("div", { className: "mui-ex-pivot-areas" }, ...areas)
462
+ ),
463
+ createElement(
464
+ "div",
465
+ { className: "mui-ex-pivot-defer" },
466
+ labelled(view.defer.label, createElement(Checkbox, {
467
+ size,
468
+ checked: view.defer.ticked,
469
+ onChange: (event) => {
470
+ context.setDeferred(event.target.checked);
471
+ }
472
+ }), size, claimsClick),
473
+ createElement(Button, {
474
+ type: "button",
475
+ size,
476
+ variant: "outlined",
477
+ disabled: !view.update.enabled,
478
+ onClick: context.update,
479
+ ...claimsClick
480
+ }, view.update.label)
481
+ )
482
+ );
483
+ }
484
+ function MuiDetailsTabs({ context }) {
485
+ const view = usePivotPart(context);
486
+ const { dense } = useMuiSize();
487
+ const selected = view.tabs.find((tab) => tab.selected);
488
+ return createElement(Tabs, {
489
+ // oxlint-disable-next-line ex-grid/no-stand-in-default -- MUI's Tabs say "no tab" as false; the pivot always selects one
490
+ value: selected?.key ?? false,
491
+ variant: "scrollable",
492
+ scrollButtons: "auto",
493
+ "aria-label": view.name,
494
+ // MUI's tabs take no size: a compact pivot's are drawn as low as its other parts by the stylesheet.
495
+ className: dense ? "mui-ex-pivot-tabs mui-ex-pivot-tabs-dense" : "mui-ex-pivot-tabs",
496
+ onChange: (_, key) => {
497
+ if (typeof key !== "string") throw new TypeError("A sheet's tab is named by its key.");
498
+ context.select(key);
499
+ }
500
+ }, ...view.tabs.map((tab) => createElement(Tab, {
501
+ key: tab.key,
502
+ value: tab.key,
503
+ ref: context.control(tab.key),
504
+ className: "mui-ex-pivot-tab",
505
+ ...claimsClick,
506
+ ...tab.panelId === void 0 ? {} : { "aria-controls": tab.panelId },
507
+ // Delete closes the sheet the keyboard is on, as the close mark does under the pointer.
508
+ ...tab.closeLabel === void 0 ? {} : {
509
+ "aria-keyshortcuts": "Delete",
510
+ onKeyDown: (event) => {
511
+ if (event.key === "Delete") {
512
+ event.preventDefault();
513
+ context.close(tab.key);
514
+ }
515
+ }
516
+ },
517
+ // The title in a box of its own, which ends in an ellipsis when it is too long, so the close mark after it stays whole.
518
+ label: createElement(
519
+ "span",
520
+ { className: "mui-ex-pivot-tab-label" },
521
+ createElement("span", { className: "mui-ex-pivot-tab-title" }, tab.title),
522
+ tab.closeLabel === void 0 ? null : (
523
+ // A span, not a button: a button inside the tab's button is not one the browser lets stand. Off the Tab
524
+ // order: Delete closes from the keyboard, and a stop inside each tab's button would be one the tabs' own
525
+ // roving order does not know.
526
+ createElement(IconButton, {
527
+ component: "span",
528
+ role: "button",
529
+ size: "small",
530
+ tabIndex: -1,
531
+ "aria-label": tab.closeLabel,
532
+ title: tab.closeLabel,
533
+ className: "mui-ex-pivot-tab-close",
534
+ ...claimsClick,
535
+ onClick: (event) => {
536
+ event.stopPropagation();
537
+ context.close(tab.key);
538
+ }
539
+ }, cross)
540
+ )
541
+ )
542
+ })));
543
+ }
544
+ function MuiDetailsDialog({ context }) {
545
+ const view = usePivotPart(context);
546
+ const { size } = useMuiSize();
547
+ return createElement(
548
+ "div",
549
+ { className: "mui-ex-pivot-dialog" },
550
+ createElement(Typography, { component: "div", variant: "h6", className: "mui-ex-pivot-dialog-title" }, view.title),
551
+ createElement("div", { ref: context.anchor("records"), className: "mui-ex-pivot-dialog-records" }),
552
+ createElement(
553
+ "div",
554
+ { className: "mui-ex-pivot-actions" },
555
+ createElement(Button, { ref: context.control("close"), type: "button", size, variant: "contained", onClick: context.close, ...claimsClick }, view.closeLabel)
556
+ )
557
+ );
558
+ }
559
+ var menu = (context) => createElement(MuiPivotMenu, { context });
560
+ var muiPivotChrome = Object.freeze({
561
+ menu,
562
+ itemFilter: (context) => createElement(MuiItemFilter, { context }),
563
+ fieldSettings: (context) => createElement(MuiFieldSettings, { context }),
564
+ valueFieldSettings: (context) => createElement(MuiValueFieldSettings, { context }),
565
+ toolbar: (context) => createElement(MuiPivotToolbar, { context }),
566
+ reportFilters: (context) => createElement(MuiReportFilters, { context }),
567
+ staleReport: (context) => createElement(MuiStaleReport, { context }),
568
+ fieldList: (context) => createElement(MuiFieldList, { context }),
569
+ detailsTabs: (context) => createElement(MuiDetailsTabs, { context }),
570
+ detailsDialog: (context) => createElement(MuiDetailsDialog, { context })
571
+ });
572
+
573
+ // src/mui-pivot.ts
574
+ import { createElement as createElement2, useMemo } from "react";
575
+ import { Paper as Paper2 } from "@mui/material";
576
+ import { useTheme as useTheme2 } from "@mui/material/styles";
577
+ import { MuiSizeContext as MuiSizeContext2, muiChrome, muiSizeOf, refuseThemeWithoutVariables as refuseThemeWithoutVariables2 } from "@ex-grid/mui";
578
+ import { ExPivot, layPivotChrome } from "@ex-grid/pivot-react";
579
+ function MuiPivot(props) {
580
+ const { variant = "outlined", elevation, square, className, style, chrome, ...pivot } = props;
581
+ refuseThemeWithoutVariables2(useTheme2(), "MuiPivot");
582
+ const density = pivot.density ?? "compact";
583
+ const slots = useMemo(() => layPivotChrome(muiPivotChrome, muiChrome, chrome), [chrome]);
584
+ const classes = ["mui-ex-grid", "mui-ex-pivot"];
585
+ if (square === true) classes.push("mui-ex-grid-square");
586
+ if (pivot.viewportWidth === void 0 || pivot.viewportWidth === "stretch") classes.push("mui-ex-grid-stretch");
587
+ if (className !== void 0) classes.push(className);
588
+ return createElement2(Paper2, {
589
+ className: classes.join(" "),
590
+ variant,
591
+ ...style === void 0 ? {} : { style },
592
+ ...elevation === void 0 ? {} : { elevation },
593
+ ...square === void 0 ? {} : { square }
594
+ }, createElement2(
595
+ MuiSizeContext2.Provider,
596
+ { value: muiSizeOf(density) },
597
+ createElement2(ExPivot, { ...pivot, chrome: slots })
598
+ ));
599
+ }
600
+ export {
601
+ MuiPivot,
602
+ muiPivotChrome
603
+ };
604
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pivot-chrome.ts", "../src/mui-pivot.ts"],
4
+ "sourcesContent": ["// ExPivot's Chrome under MUI (ADR-0061, ADR-0390): MUI's components in each of ExPivot for React's slots \u2014 the Field\n// List, the Pivot Toolbar and its report filters, the menus and panels that open from them, the Stale Report's notice\n// and Show Details' tabs and dialog \u2014 at the size the pivot's density maps onto. What a part shows, what a command or\n// a drop does, what OK applies and why it is refused, where a frame stands and what closes it are the pivot's; these\n// components draw what it hands them and call back. The frames are the pivot's, so MUI's own Menu, MenuList, Popover,\n// Dialog and Select, which position, dismiss or answer keys themselves, are not used; a select is MUI's native one,\n// whose list is the browser's and opens no popup of the page's own.\n\nimport { createElement, useContext, useId, type DragEvent, type KeyboardEvent, type ReactElement, type ReactNode } from 'react';\nimport {\n Alert, Button, Checkbox, CircularProgress, FormControlLabel, IconButton, ListItemButton, Paper, Radio, RadioGroup, Tab, Tabs, TextField, Typography,\n} from '@mui/material';\nimport { useTheme } from '@mui/material/styles';\nimport { MuiSizeContext, refuseThemeWithoutVariables, type MuiExGridSize } from '@ex-grid/mui';\nimport type {\n PivotDetailsDialogSlotContext, PivotDetailsTabsSlotContext, PivotFieldListSlotContext, PivotFieldSettingsSlotContext, PivotItemFilterSlotContext,\n PivotMenuSlotContext, PivotPanelSlotContext, PivotReportFiltersSlotContext, PivotStaleReportSlotContext, PivotToolbarSlotContext,\n PivotValueFieldSettingsSlotContext,\n} from '@ex-grid/pivot';\nimport { usePivotMenu, usePivotPanel, usePivotPart, type ExPivotChrome } from '@ex-grid/pivot-react';\n\n/** A press the pivot takes in its Input Order, as its own buttons claim theirs (ADR-0240). */\nconst claimsClick = { 'data-ex-claims': 'click' } as const;\n/** A field typed in claims nothing: its keys are the field's (ADR-0240). */\nconst claimsNothing = { 'data-ex-claims': '' } as const;\n\n/** One of the Field List's four areas, as the pivot names it. */\ntype PivotArea = Parameters<PivotFieldListSlotContext['area']>[0];\n\n/** The size the pivot's density maps onto, read where the theme has been found readable: every slot's contents call it,\n * so a Consumer that frames the pivot itself under a theme without CSS variables is refused by name whichever slots it\n * keeps (ADR-0390 section 5). */\nfunction useMuiSize(): MuiExGridSize {\n refuseThemeWithoutVariables(useTheme(), 'muiPivotChrome');\n return useContext(MuiSizeContext);\n}\n\n/** The body text MUI sets beside a control of `size`. */\nconst textVariant = (size: MuiExGridSize['size']): 'body2' | 'body1' => (size === 'small' ? 'body2' : 'body1');\n\n/** A \u25BE after a button's words, read by no assistive technology: the button's `aria-haspopup` says it. */\nconst opens = createElement('span', { 'aria-hidden': true, className: 'mui-ex-pivot-opens' }, '\u25BE');\n\n/** The close mark's cross, drawn rather than written, so a control's text is its name alone. */\nconst cross = createElement('svg', { viewBox: '0 0 16 16', width: 12, height: 12, 'aria-hidden': true, focusable: false },\n createElement('path', { d: 'M4 4l8 8M12 4l-8 8', stroke: 'currentColor', strokeWidth: 1.75, strokeLinecap: 'round' }));\n\n/** A control with its label, in the body text of the size, its press or its keys claimed as `claims` says. */\nfunction labelled(label: string, control: ReactElement, size: MuiExGridSize['size'], claims: typeof claimsClick | typeof claimsNothing, key?: string | number): ReactElement {\n return createElement(FormControlLabel, {\n ...(key === undefined ? {} : { key }), label, control, className: 'mui-ex-pivot-label', ...claims,\n slotProps: { typography: { variant: textVariant(size) } },\n });\n}\n\n/** A menu, a placed field's or the Layout menu: one of MUI's list item buttons per command, as its Menu draws an item,\n * a choice marked while it is the current one, and a group's heading where one starts. MUI's MenuItem is not used: it\n * stands only inside MUI's MenuList, which answers the arrows itself. */\nfunction MuiPivotMenu({ context }: { readonly context: PivotMenuSlotContext }): ReactElement {\n const { view, item } = usePivotMenu(context);\n const { size, dense } = useMuiSize();\n return createElement('div', { role: 'none', className: 'mui-ex-pivot-menu' }, ...view.commands.flatMap((command, index) => [\n command.heading === undefined ? null : createElement(Typography<'div'>, {\n key: `${command.id}-heading`, component: 'div', variant: textVariant(size), color: 'textSecondary', className: 'mui-ex-pivot-menu-heading', 'aria-hidden': true,\n }, command.heading),\n createElement(ListItemButton<'button'>, {\n ...item(index), key: command.id, component: 'button', type: 'button', dense, className: 'mui-ex-pivot-menu-item',\n },\n // The mark is the stylesheet's, read off aria-checked, so the command's words stay the pivot's.\n command.checked === undefined ? null : createElement('span', { className: 'mui-ex-pivot-menu-mark', 'aria-hidden': true }),\n command.label),\n ]));\n}\n\n/** OK before Cancel, as Excel orders them; Cancel is never disabled. */\nfunction panelActions(context: PivotPanelSlotContext, size: MuiExGridSize['size'], canApply = true): ReactElement {\n return createElement('div', { className: 'mui-ex-pivot-actions' },\n createElement(Button, { type: 'button', size, variant: 'contained', disabled: !canApply, onClick: context.apply, ...claimsClick }, context.label('ok')),\n createElement(Button, { type: 'button', size, variant: 'outlined', onClick: context.cancel, ...claimsClick }, context.label('cancel')));\n}\n\n/** The choice an option names: one the pivot offered, never one a changed option claims. */\nfunction offered<T extends string>(value: string, choices: readonly { readonly value: T }[], what: string): T {\n const found = choices.find(choice => choice.value === value);\n if (found === undefined) throw new RangeError(`'${value}' is not ${what} the panel offers.`);\n return found.value;\n}\n\n/** A native select in MUI's field, its label above it: its list is the browser's, a popup of no page's. */\nfunction nativeSelect(label: string, value: string, options: readonly { readonly value: string; readonly label: string; readonly disabled?: boolean }[],\n change: (value: string) => void, size: MuiExGridSize['size']): ReactElement {\n return createElement(TextField, {\n size, select: true, label, value, fullWidth: true, ...claimsNothing,\n slotProps: { select: { native: true }, inputLabel: { shrink: true } },\n onChange: event => { change(event.target.value); },\n }, ...options.map(option => createElement('option', { key: option.value, value: option.value, disabled: option.disabled === true }, option.label)));\n}\n\n/** Field Settings\u2026: the subtotals' choice, which takes the keyboard, and the field's order. */\nfunction MuiFieldSettings({ context }: { readonly context: PivotFieldSettingsSlotContext }): ReactElement {\n const { view, first } = usePivotPanel(context);\n const { size } = useMuiSize();\n // One group, so the arrows move between the two choices, named apart from every other panel's.\n const subtotals = useId();\n return createElement('div', { className: 'mui-ex-pivot-panel' },\n createElement(Typography<'div'>, { component: 'div', variant: textVariant(size), className: 'mui-ex-pivot-strong' }, view.caption),\n createElement(RadioGroup, {\n name: subtotals, value: view.subtotals ? 'automatic' : 'none',\n onChange: (_event: unknown, value: string) => { context.setSubtotals(value === 'automatic'); },\n },\n labelled(context.label('subtotals-automatic'), createElement(Radio, { size, value: 'automatic', slotProps: { input: { ref: first } } }), size, claimsNothing),\n labelled(context.label('subtotals-none'), createElement(Radio, { size, value: 'none' }), size, claimsNothing)),\n nativeSelect(context.label('sort-order'), String(view.sort), view.sorts.map((sort, index) => ({ value: String(index), label: sort })),\n value => { context.setSort(Number(value)); }, size),\n panelActions(context, size));\n}\n\n/** Value Field Settings\u2026: the caption, which takes the keyboard, the Aggregation, Show Values As and the number\n * format, its sample, and the pivot's refusal. */\nfunction MuiValueFieldSettings({ context }: { readonly context: PivotValueFieldSettingsSlotContext }): ReactElement {\n const { view, first } = usePivotPanel(context);\n const { size } = useMuiSize();\n return createElement('div', { className: 'mui-ex-pivot-panel' },\n createElement(Typography<'div'>, { component: 'div', variant: textVariant(size), color: 'textSecondary' }, view.sourceCaption),\n createElement(TextField, {\n size, inputRef: first, label: context.label('custom-name'), value: view.caption, fullWidth: true, ...claimsNothing,\n slotProps: { inputLabel: { shrink: true } },\n onChange: event => { context.setCaption(event.target.value); },\n }),\n nativeSelect(context.label('summarize-by'), view.aggregation,\n view.aggregations.map(choice => ({ value: choice.value, label: choice.label, disabled: !choice.enabled })),\n value => { context.setAggregation(offered(value, view.aggregations, 'an Aggregation')); }, size),\n nativeSelect(context.label('show-values-as'), view.showValuesAs, view.showValuesAsChoices,\n value => { context.setShowValuesAs(offered(value, view.showValuesAsChoices, 'a Show Values As')); }, size),\n createElement(TextField, {\n size, label: context.label('number-format'), value: view.numberFormat, placeholder: view.numberFormatPlaceholder, fullWidth: true, ...claimsNothing,\n slotProps: { inputLabel: { shrink: true } },\n onChange: event => { context.setNumberFormat(event.target.value); },\n }),\n createElement(Typography<'div'>, { component: 'div', variant: textVariant(size), color: 'textSecondary', className: 'mui-ex-pivot-sample' }, view.sample),\n view.refusal === undefined ? null : createElement(Alert, { severity: 'error' }, view.refusal),\n panelActions(context, size));\n}\n\n/** Filter\u2026: the search, which takes the keyboard, (Select All) and each Item ticked while shown, why they cannot be\n * listed or that they are on their way, the note under them, and the pivot's refusal. */\nfunction MuiItemFilter({ context }: { readonly context: PivotItemFilterSlotContext }): ReactElement {\n const { view, first } = usePivotPanel(context);\n const { size } = useMuiSize();\n const list = view.unavailable !== undefined\n ? createElement(Alert, { severity: 'warning' }, view.unavailable)\n : view.loading\n ? createElement('div', { className: 'mui-ex-pivot-loading' },\n createElement(CircularProgress, { size: size === 'small' ? 16 : 20 }),\n createElement(Typography<'span'>, { component: 'span', variant: textVariant(size) }, context.label('loading')))\n : createElement('div', { role: 'group', 'aria-label': view.caption, 'aria-busy': view.updating, className: 'mui-ex-pivot-items' },\n view.items.length === 0 ? null : labelled(context.label('select-all'), createElement(Checkbox, {\n size, checked: view.allTicked === true, indeterminate: view.allTicked === undefined,\n onChange: event => { context.setAll(event.target.checked); },\n }), size, claimsNothing),\n // Keyed by position: the pivot's list is the order the Items stand in.\n ...view.items.map((item, at) => labelled(item.label, createElement(Checkbox, {\n size, checked: item.ticked, onChange: event => { context.setTicked(item.key, event.target.checked); },\n }), size, claimsNothing, at)),\n view.note === undefined ? null : createElement(Typography<'div'>, { component: 'div', variant: textVariant(size), color: 'textSecondary' }, view.note));\n return createElement('div', { className: 'mui-ex-pivot-panel' },\n createElement(TextField, {\n size, inputRef: first, type: 'search', placeholder: context.label('search-items'), value: view.search, fullWidth: true, ...claimsNothing,\n slotProps: { htmlInput: { 'aria-label': context.label('search-items') } },\n onChange: event => { context.setSearch(event.target.value); },\n }),\n list,\n view.refusal === undefined ? null : createElement(Alert, { severity: 'error' }, view.refusal),\n panelActions(context, size, view.canApply));\n}\n\n/** The Pivot Toolbar: the report filter band's anchor on its left; Layout \u25BE in the anchor its menu opens in, Refresh\n * where the source can be refreshed, and the Field List's toggle on its right; and the refusal of the user's last\n * change beneath. */\nfunction MuiPivotToolbar({ context }: { readonly context: PivotToolbarSlotContext }): ReactElement {\n const view = usePivotPart(context);\n const { size } = useMuiSize();\n return createElement('div', { className: 'mui-ex-pivot-toolbar-frame' },\n createElement('div', { role: 'toolbar', className: 'mui-ex-pivot-toolbar' },\n createElement('div', { ref: context.anchor('report-filters'), className: 'mui-ex-pivot-toolbar-start' }),\n createElement('div', { className: 'mui-ex-pivot-toolbar-end' },\n createElement('span', { ref: context.anchor('layout'), className: 'mui-ex-pivot-anchor' }, createElement(Button, {\n ref: context.control('layout'), type: 'button', size, variant: view.layoutMenu.open ? 'outlined' : 'text', className: 'mui-ex-pivot-button',\n endIcon: opens, 'aria-haspopup': 'menu', 'aria-expanded': view.layoutMenu.open, onClick: context.toggleLayoutMenu, ...claimsClick,\n }, view.layoutMenu.label)),\n view.refresh === undefined ? null : createElement(Button, {\n ref: context.control('refresh'), type: 'button', size, variant: 'text', className: 'mui-ex-pivot-button', onClick: context.refresh, ...claimsClick,\n }, view.refresh.label),\n createElement(Button, {\n ref: context.control('field-list'), type: 'button', size, variant: view.fieldList.shown ? 'outlined' : 'text', className: 'mui-ex-pivot-button',\n 'aria-pressed': view.fieldList.shown, onClick: context.toggleFieldList, ...claimsClick,\n }, view.fieldList.label))),\n view.refusal === undefined ? null : createElement(Alert, { severity: 'error', className: 'mui-ex-pivot-refusal' }, view.refusal));\n}\n\n/** The report filter band: each field in Filters, its caption, and the button that says what it shows and opens\n * Filter\u2026 in the anchor around it. */\nfunction MuiReportFilters({ context }: { readonly context: PivotReportFiltersSlotContext }): ReactElement {\n const view = usePivotPart(context);\n const { size } = useMuiSize();\n return createElement('div', { className: 'mui-ex-pivot-filters' }, ...view.filters.map(filter => createElement('div', {\n key: filter.field, className: 'mui-ex-pivot-filter',\n },\n createElement(Typography<'span'>, { component: 'span', variant: textVariant(size), className: 'mui-ex-pivot-strong mui-ex-pivot-filter-caption' }, filter.caption),\n createElement('span', { ref: context.anchor(filter.field), className: 'mui-ex-pivot-anchor mui-ex-pivot-filter-anchor' }, createElement(Button, {\n ref: context.control(filter.field), type: 'button', size, variant: 'outlined', color: filter.filtered ? 'primary' : 'inherit', fullWidth: true,\n className: 'mui-ex-pivot-button mui-ex-pivot-caption-button', endIcon: opens,\n 'aria-label': filter.buttonLabel, 'aria-haspopup': 'dialog', 'aria-expanded': filter.open, onClick: () => { context.toggle(filter.field); }, ...claimsClick,\n }, createElement('span', { className: 'mui-ex-pivot-caption' }, filter.summary))))));\n}\n\n/** The Stale Report's notice, in MUI's alert, inside the pivot's live region: the sentence, and Retry as the alert's\n * action. */\nfunction MuiStaleReport({ context }: { readonly context: PivotStaleReportSlotContext }): ReactElement {\n const view = usePivotPart(context);\n const { size } = useMuiSize();\n return createElement(Alert, {\n severity: 'warning', className: 'mui-ex-pivot-stale',\n action: createElement(Button, {\n ref: context.control('retry'), type: 'button', size, color: 'inherit', disabled: !view.retry.enabled, onClick: context.retry, ...claimsClick,\n }, view.retry.label),\n }, view.message);\n}\n\n/** Where a drag may land in the Field List: an Area's end, or before an entry. Each hears the drag's events, the\n * innermost first, so an entry's place outranks its Area's end. */\nfunction dropTarget(context: PivotFieldListSlotContext, area: PivotArea, index: number): Record<string, (event: DragEvent<HTMLElement>) => void> {\n return {\n onDragOver: event => { context.dragOver(event.nativeEvent); },\n onDragEnter: event => { event.stopPropagation(); context.dragEnter(area, index); },\n onDrop: event => { event.stopPropagation(); context.drop(event.nativeEvent, area, index); },\n };\n}\n\n/** The Field List, Excel's PivotTable Fields pane, in MUI's controls: the search and the fields with their tick boxes,\n * scrolling on their own; the four Areas, two by two, always in view at the foot; then Defer Layout Update and Update.\n * Drag and drop is the browser's own, its every event handed to the pivot. */\nfunction MuiFieldList({ context }: { readonly context: PivotFieldListSlotContext }): ReactElement {\n const view = usePivotPart(context);\n const { size } = useMuiSize();\n const variant = textVariant(size);\n const fields = view.fields.map(field => createElement('div', {\n key: field.name, role: 'listitem', draggable: true,\n className: field.dragging ? 'mui-ex-pivot-field mui-ex-pivot-dragging' : 'mui-ex-pivot-field',\n onDragStart: (event: DragEvent<HTMLElement>) => { context.startDrag(event.nativeEvent, { field: field.name }); },\n onDragEnd: context.endDrag,\n },\n labelled(field.caption, createElement(Checkbox, {\n size, slotProps: { input: { ref: context.control(field.key) } }, checked: field.placed, onChange: () => { context.toggle(field.name); },\n }), size, claimsClick),\n field.filtered ? createElement(Typography<'span'>, { component: 'span', variant, color: 'textSecondary', 'aria-hidden': true }, '\u23F7') : null,\n field.count === undefined ? null : createElement(Typography<'span'>, {\n component: 'span', variant, color: 'textSecondary', title: field.countLabel, 'aria-label': field.countLabel,\n }, field.count)));\n const areas = view.areas.map(area => createElement(Paper, {\n key: area.area, ref: context.area(area.area), variant: 'outlined', ...dropTarget(context, area.area, area.end),\n className: area.dropTarget ? 'mui-ex-pivot-area mui-ex-pivot-drop-target' : 'mui-ex-pivot-area',\n },\n createElement(Typography<'div'>, { component: 'div', variant, className: 'mui-ex-pivot-strong mui-ex-pivot-area-title' }, area.title),\n area.overCap === undefined ? null : createElement(Typography<'div'>, { component: 'div', variant, color: 'error', role: 'status' }, area.overCap),\n createElement('div', { role: 'list', 'aria-label': area.title, className: 'mui-ex-pivot-entries' }, ...area.entries.map(entry => {\n const classes = ['mui-ex-pivot-entry'];\n if (entry.dragging) classes.push('mui-ex-pivot-dragging');\n if (entry.dropBefore) classes.push('mui-ex-pivot-drop-before');\n // The anchor the entry's menu and panels open in: no position of its own, so their frame takes the pane's width.\n return createElement('div', {\n key: entry.key, role: 'listitem', ref: context.anchor(entry.key), draggable: !entry.menuOpen, className: classes.join(' '),\n ...dropTarget(context, area.area, entry.dropIndex),\n onDragStart: (event: DragEvent<HTMLElement>) => { context.startDrag(event.nativeEvent, { entry: entry.entry }); },\n onDragEnd: context.endDrag,\n }, createElement(Button, {\n ref: context.control(entry.key), type: 'button', size, variant: 'outlined', color: entry.filtered ? 'primary' : 'inherit', fullWidth: true,\n className: 'mui-ex-pivot-button mui-ex-pivot-caption-button', endIcon: opens,\n 'aria-label': entry.menuLabel, 'aria-haspopup': 'menu', 'aria-expanded': entry.menuOpen, onClick: () => { context.openMenu(entry.entry); }, ...claimsClick,\n }, createElement('span', { className: 'mui-ex-pivot-caption' }, entry.caption)));\n }))));\n return createElement('div', { className: 'mui-ex-pivot-pane' },\n createElement('div', { className: 'mui-ex-pivot-pane-heading' },\n createElement(Typography<'span'>, { component: 'span', variant, className: 'mui-ex-pivot-strong mui-ex-pivot-pane-title' }, view.title),\n createElement(IconButton, { size: 'small', 'aria-label': view.hideLabel, title: view.hideLabel, onClick: context.close, ...claimsClick }, cross)),\n createElement('div', { className: 'mui-ex-pivot-pane-body' },\n createElement(Typography<'div'>, { component: 'div', variant, color: 'textSecondary' }, view.chooseCaption),\n createElement(TextField, {\n inputRef: context.control('search'), size, type: 'search', placeholder: view.searchLabel, value: view.search, fullWidth: true, ...claimsNothing,\n slotProps: { htmlInput: { 'aria-label': view.searchLabel } },\n onChange: event => { context.setSearch(event.target.value); },\n }),\n createElement('div', {\n role: 'list', 'aria-label': view.title,\n className: view.dropRemoves ? 'mui-ex-pivot-fields mui-ex-pivot-drop-remove' : 'mui-ex-pivot-fields',\n onDragOver: (event: DragEvent<HTMLElement>) => { context.dragOver(event.nativeEvent); },\n onDrop: (event: DragEvent<HTMLElement>) => { context.dropOnList(event.nativeEvent); },\n }, ...fields),\n createElement(Typography<'div'>, { component: 'div', variant, color: 'textSecondary' }, view.dragCaption),\n createElement('div', { className: 'mui-ex-pivot-areas' }, ...areas)),\n createElement('div', { className: 'mui-ex-pivot-defer' },\n labelled(view.defer.label, createElement(Checkbox, {\n size, checked: view.defer.ticked, onChange: event => { context.setDeferred(event.target.checked); },\n }), size, claimsClick),\n createElement(Button, {\n type: 'button', size, variant: 'outlined', disabled: !view.update.enabled, onClick: context.update, ...claimsClick,\n }, view.update.label)));\n}\n\n/** Show Details' sheet tabs at the report's foot, as MUI's tabs: the report's own first, then each sheet's with its\n * close mark. Which sheet is selected, and what closing one selects, are the pivot's. */\nfunction MuiDetailsTabs({ context }: { readonly context: PivotDetailsTabsSlotContext }): ReactElement {\n const view = usePivotPart(context);\n const { dense } = useMuiSize();\n const selected = view.tabs.find(tab => tab.selected);\n return createElement(Tabs, {\n // oxlint-disable-next-line ex-grid/no-stand-in-default -- MUI's Tabs say \"no tab\" as false; the pivot always selects one\n value: selected?.key ?? false, variant: 'scrollable', scrollButtons: 'auto', 'aria-label': view.name,\n // MUI's tabs take no size: a compact pivot's are drawn as low as its other parts by the stylesheet.\n className: dense ? 'mui-ex-pivot-tabs mui-ex-pivot-tabs-dense' : 'mui-ex-pivot-tabs',\n onChange: (_: unknown, key: unknown) => {\n if (typeof key !== 'string') throw new TypeError('A sheet\\'s tab is named by its key.');\n context.select(key);\n },\n }, ...view.tabs.map(tab => createElement(Tab, {\n key: tab.key, value: tab.key, ref: context.control(tab.key), className: 'mui-ex-pivot-tab', ...claimsClick,\n ...(tab.panelId === undefined ? {} : { 'aria-controls': tab.panelId }),\n // Delete closes the sheet the keyboard is on, as the close mark does under the pointer.\n ...(tab.closeLabel === undefined ? {} : {\n 'aria-keyshortcuts': 'Delete',\n onKeyDown: (event: KeyboardEvent<HTMLDivElement>) => { if (event.key === 'Delete') { event.preventDefault(); context.close(tab.key); } },\n }),\n // The title in a box of its own, which ends in an ellipsis when it is too long, so the close mark after it stays whole.\n label: createElement('span', { className: 'mui-ex-pivot-tab-label' }, createElement('span', { className: 'mui-ex-pivot-tab-title' }, tab.title),\n tab.closeLabel === undefined ? null :\n // A span, not a button: a button inside the tab's button is not one the browser lets stand. Off the Tab\n // order: Delete closes from the keyboard, and a stop inside each tab's button would be one the tabs' own\n // roving order does not know.\n createElement(IconButton<'span'>, {\n component: 'span', role: 'button', size: 'small', tabIndex: -1, 'aria-label': tab.closeLabel, title: tab.closeLabel,\n className: 'mui-ex-pivot-tab-close', ...claimsClick,\n onClick: event => { event.stopPropagation(); context.close(tab.key); },\n }, cross)),\n })));\n}\n\n/** What the Show Details dialog draws inside the pivot's frame: its title, the records where they stand, and Close. */\nfunction MuiDetailsDialog({ context }: { readonly context: PivotDetailsDialogSlotContext }): ReactElement {\n const view = usePivotPart(context);\n const { size } = useMuiSize();\n return createElement('div', { className: 'mui-ex-pivot-dialog' },\n createElement(Typography<'div'>, { component: 'div', variant: 'h6', className: 'mui-ex-pivot-dialog-title' }, view.title),\n createElement('div', { ref: context.anchor('records'), className: 'mui-ex-pivot-dialog-records' }),\n createElement('div', { className: 'mui-ex-pivot-actions' },\n createElement(Button, { ref: context.control('close'), type: 'button', size, variant: 'contained', onClick: context.close, ...claimsClick }, view.closeLabel)));\n}\n\nconst menu = (context: PivotMenuSlotContext): ReactNode => createElement(MuiPivotMenu, { context });\n\n/** ExPivot's Chrome drawn in MUI's components, but for the report's grid: what {@link MuiPivot} hands its pivot, with\n * `@ex-grid/mui`'s `muiChrome` as `grid`. A Consumer that frames an ExPivot itself spreads both into its `chrome`,\n * inside an element of the classes `mui-ex-grid mui-ex-pivot` and a MuiSizeContext, under a ThemeProvider whose theme\n * was made with `cssVariables: true`, as MuiPivot draws them. */\nexport const muiPivotChrome: Omit<ExPivotChrome, 'grid'> = Object.freeze({\n menu,\n itemFilter: (context: PivotItemFilterSlotContext) => createElement(MuiItemFilter, { context }),\n fieldSettings: (context: PivotFieldSettingsSlotContext) => createElement(MuiFieldSettings, { context }),\n valueFieldSettings: (context: PivotValueFieldSettingsSlotContext) => createElement(MuiValueFieldSettings, { context }),\n toolbar: (context: PivotToolbarSlotContext) => createElement(MuiPivotToolbar, { context }),\n reportFilters: (context: PivotReportFiltersSlotContext) => createElement(MuiReportFilters, { context }),\n staleReport: (context: PivotStaleReportSlotContext) => createElement(MuiStaleReport, { context }),\n fieldList: (context: PivotFieldListSlotContext) => createElement(MuiFieldList, { context }),\n detailsTabs: (context: PivotDetailsTabsSlotContext) => createElement(MuiDetailsTabs, { context }),\n detailsDialog: (context: PivotDetailsDialogSlotContext) => createElement(MuiDetailsDialog, { context }),\n});\n", "// MuiPivot (ADR-0390, ADR-0030): ExPivot for React inside MUI's Paper, its Chrome and its report grid's drawn in MUI's\n// components and its Visual Tokens mapped onto the theme's CSS variables by the Wrapper's stylesheet. It owns no\n// geometry, no DOM inside the pivot's root and no state: it hands the pivot its Chrome, and the pivot decides the rest.\n// A theme without CSS variables is refused, since its colours exist only in script and the pivot would be drawn in\n// colours that are not the theme's (ADR-0390 section 5).\n\nimport { createElement, useMemo, type CSSProperties, type ReactElement } from 'react';\nimport { Paper } from '@mui/material';\nimport { useTheme } from '@mui/material/styles';\nimport { MuiSizeContext, muiChrome, muiSizeOf, refuseThemeWithoutVariables } from '@ex-grid/mui';\nimport { ExPivot, layPivotChrome, type ExPivotProps } from '@ex-grid/pivot-react';\nimport { muiPivotChrome } from './pivot-chrome';\n\n/** A MuiPivot's props: an ExPivot's, and the words MUI's Paper takes. */\nexport type MuiPivotProps = ExPivotProps & {\n /** The Paper's variant: `outlined`, the default here, draws a border; `elevation` a shadow, and under the dark\n * scheme MUI lightens an elevated Paper with an overlay the report's own ground does not take. */\n readonly variant?: 'elevation' | 'outlined';\n /** The Paper's shadow depth, 0 to 24, under the `elevation` variant. */\n readonly elevation?: number;\n /** Square corners: the Paper's own word, and the pivot set square in it with no inset. */\n readonly square?: boolean;\n /** A class for the Paper. */\n readonly className?: string;\n /** A style for the Paper. */\n readonly style?: CSSProperties;\n};\n\n/**\n * Excel's PivotTable drawn with MUI: the Chrome slots a `chrome` prop leaves, the pivot's and its report grid's, are\n * MUI's, and the colours follow the theme and its scheme with no render. Needs a ThemeProvider above it whose theme is\n * made with `createTheme({ cssVariables: true })`, and `@ex-grid/mui/styles.css` and `@ex-grid/pivot-mui/styles.css`\n * loaded.\n */\nexport function MuiPivot(props: MuiPivotProps): ReactElement {\n const { variant = 'outlined', elevation, square, className, style, chrome, ...pivot } = props;\n refuseThemeWithoutVariables(useTheme(), 'MuiPivot');\n const density = pivot.density ?? 'compact'; // oxlint-disable-line ex-grid/no-stand-in-default -- ADR-0028: an untouched grid is compact\n // One merge per chrome handed, so a render function handed again keeps its mount (ADR-0390).\n const slots = useMemo(() => layPivotChrome(muiPivotChrome, muiChrome, chrome), [chrome]);\n const classes = ['mui-ex-grid', 'mui-ex-pivot'];\n if (square === true) classes.push('mui-ex-grid-square');\n // The report takes what the Field List leaves of the pivot's width unless it is given one (ADR-0045), so the Paper\n // round it is as wide as its container, as a Stretch grid's is, rather than as wide as its content.\n if (pivot.viewportWidth === undefined || pivot.viewportWidth === 'stretch') classes.push('mui-ex-grid-stretch');\n if (className !== undefined) classes.push(className);\n return createElement(Paper, {\n className: classes.join(' '),\n variant,\n ...(style === undefined ? {} : { style }),\n ...(elevation === undefined ? {} : { elevation }),\n ...(square === undefined ? {} : { square }),\n }, createElement(MuiSizeContext.Provider, { value: muiSizeOf(density) },\n createElement(ExPivot, { ...pivot, chrome: slots })));\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["createElement", "Paper", "useTheme", "MuiSizeContext", "refuseThemeWithoutVariables", "refuseThemeWithoutVariables", "useTheme", "createElement", "Paper", "MuiSizeContext"]
7
+ }
package/package.json CHANGED
@@ -1,6 +1,41 @@
1
1
  {
2
2
  "name": "@ex-grid/pivot-mui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0-rc.1",
4
+ "description": "ExPivot for MUI: the design-system Wrapper that draws ExPivot for React's Chrome — the Field List, the Pivot Toolbar, its menus and panels, the report filters, the Stale Report's notice and Show Details' tabs and dialog — with MUI's components, and maps the pivot's Visual Tokens onto a MUI theme made with CSS variables.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/AkiraMisawa/ex-grid.git",
9
+ "directory": "packages/pivot-mui"
10
+ },
11
+ "homepage": "https://akiramisawa.github.io/ex-grid/",
12
+ "type": "module",
13
+ "sideEffects": [
14
+ "*.css"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./styles.css": "./styles.css"
22
+ },
23
+ "files": [
24
+ "dist/index.js",
25
+ "dist/index.js.map",
26
+ "dist/index.d.ts",
27
+ "styles.css"
28
+ ],
29
+ "dependencies": {
30
+ "@ex-grid/mui": "0.2.0-rc.1",
31
+ "@ex-grid/pivot": "0.2.0-rc.1",
32
+ "@ex-grid/pivot-react": "0.2.0-rc.1"
33
+ },
34
+ "peerDependencies": {
35
+ "@emotion/react": "^11.14.0",
36
+ "@emotion/styled": "^11.14.0",
37
+ "@mui/material": "^9.5.0",
38
+ "react": "^19.2.0",
39
+ "react-dom": "^19.2.0"
40
+ }
41
+ }
package/styles.css ADDED
@@ -0,0 +1,184 @@
1
+ /* @ex-grid/pivot-mui — the Wrapper's stylesheet (ADR-0390 section 5, ADR-0062). It does two things and nothing else.
2
+ It maps ExPivot's Visual Tokens onto the variables a MUI theme made with cssVariables writes on the page, under the
3
+ pivot's Paper, .mui-ex-pivot, so it outranks ExPivot's defaults whatever order the stylesheets load in, and a change
4
+ of scheme or a palette edit recolours the pivot with no render anywhere (ADR-0027/0030). Every variable it reads is
5
+ one MUI writes again per colour scheme, so it names no scheme selector of its own. The report grid's own tokens are
6
+ @ex-grid/mui's, under the same Paper, which also carries .mui-ex-grid. And it lays out this package's own elements,
7
+ each rule scoped to its own mui-ex-pivot- class. It writes no geometry ExPivot computes (the Field List's width and
8
+ a level's indent are the pivot's, written inline), no font for the report (it is metrics-bearing and the grid's,
9
+ ADR-0027), and no rule against a class of ExPivot's or ExGrid's own (ADR-0029/0030). */
10
+ .mui-ex-pivot {
11
+ --ex-pivot-color: var(--mui-palette-text-primary);
12
+ --ex-pivot-muted-color: var(--mui-palette-text-secondary);
13
+ --ex-pivot-rule-color: var(--mui-palette-divider);
14
+ --ex-pivot-accent-color: var(--mui-palette-primary-main);
15
+ --ex-pivot-drop-color: var(--mui-palette-primary-main);
16
+ --ex-pivot-refusal-color: var(--mui-palette-error-main);
17
+ --ex-pivot-pane-background: var(--mui-palette-background-paper);
18
+ --ex-pivot-pane-color: var(--mui-palette-text-primary);
19
+ --ex-pivot-area-background: var(--mui-palette-action-hover);
20
+ --ex-pivot-empty-background: var(--mui-palette-background-paper);
21
+ /* A menu or a panel is ExPivot's frame (ADR-0061), painted as MUI paints its Popover's paper: the paper's ground
22
+ and the eighth shadow. */
23
+ --ex-pivot-popup-background: var(--mui-palette-background-paper);
24
+ --ex-pivot-popup-color: var(--mui-palette-text-primary);
25
+ --ex-pivot-popup-shadow: var(--mui-shadows-8);
26
+ --ex-pivot-hover-background: var(--mui-palette-action-hover);
27
+ --ex-pivot-input-background: var(--mui-palette-background-paper);
28
+ --ex-pivot-entry-background: var(--mui-palette-background-paper);
29
+ --ex-pivot-entry-color: var(--mui-palette-text-primary);
30
+ --ex-pivot-entry-border-color: var(--mui-palette-divider);
31
+ /* The ± button in a label cell stays ExPivot's plain markup (ADR-0062): a component there would be one in every
32
+ painted row. Only its colours are the theme's. */
33
+ --ex-pivot-toggle-color: var(--mui-palette-text-secondary);
34
+ --ex-pivot-toggle-background: transparent;
35
+ --ex-pivot-toggle-border-color: var(--mui-palette-divider);
36
+ /* An Area a drop would take past the cap (ADR-0370 section 3): the theme's error, as a refusal is painted. */
37
+ --ex-pivot-over-cap-color: var(--mui-palette-error-main);
38
+ /* Show Details (ADR-0059): a tab's records over the report, and the dialog's frame and backdrop, ExPivot's,
39
+ painted as MUI paints a Dialog: the paper, no edge, the theme's corners, the largest shadow and its Backdrop. */
40
+ --ex-pivot-details-background: var(--mui-palette-background-paper);
41
+ --ex-pivot-tab-background: var(--mui-palette-action-hover);
42
+ --ex-pivot-tab-selected-background: var(--mui-palette-background-paper);
43
+ --ex-pivot-dialog-backdrop: rgba(0, 0, 0, 0.5);
44
+ --ex-pivot-dialog-border-color: transparent;
45
+ --ex-pivot-dialog-radius: var(--mui-shape-borderRadius);
46
+ --ex-pivot-dialog-shadow: var(--mui-shadows-24);
47
+ /* The Stale Report (ADR-0067): the built-in notice's colours where a Consumer keeps it, and the value cells while
48
+ the report is stale, the text mixed 82% into the paper they stand on, muted and still body text (UX-8), as the
49
+ MudBlazor Wrapper mixes its own. */
50
+ --ex-pivot-stale-color: var(--mui-palette-text-primary);
51
+ --ex-pivot-stale-background: var(--mui-palette-background-paper);
52
+ --ex-pivot-stale-border-color: var(--mui-palette-warning-main);
53
+ --ex-pivot-stale-value-color: color-mix(in srgb, var(--mui-palette-text-primary) 82%, var(--mui-palette-background-paper));
54
+ /* No --ex-pivot-font-size: ExPivot's 14px is MUI's body2, and MUI writes no size of its own apart from the whole
55
+ font shorthand, which the pane below takes. */
56
+ }
57
+
58
+ /* A heading's weight, MUI's medium: under the Paper, so it outranks the weight of the Typography's variant, which
59
+ Emotion writes after this stylesheet. */
60
+ .mui-ex-pivot .mui-ex-pivot-strong { font-weight: 500; }
61
+
62
+ /* An anchor a menu or a panel opens in: positioned, so the pivot's frame opens under its button. */
63
+ .mui-ex-pivot-anchor { position: relative; display: inline-flex; }
64
+
65
+ /* The Pivot's own buttons say a field's caption or a part's name, so they keep the words as written rather than
66
+ MUI's capitals. The panels' OK and Cancel keep MUI's, as the grid's filter panel beside them does. */
67
+ .mui-ex-pivot-button.MuiButton-root { text-transform: none; }
68
+
69
+ /* A placed field's menu, and the Layout menu: MUI's list item buttons laid out as its Menu's items, each label at the
70
+ start, the current choice marked. The mark is drawn here off aria-checked, so the command's words are only the
71
+ pivot's. */
72
+ .mui-ex-pivot-menu { display: flex; flex-direction: column; padding: 4px 0; }
73
+ /* Only the width and the text: ButtonBase already takes the button's own border and ground away, and a background set
74
+ here would tie with MUI's hover and keyboard-focus shades and hide which item Enter runs. */
75
+ .mui-ex-pivot-menu .mui-ex-pivot-menu-item { width: 100%; gap: 4px; font: var(--mui-font-body1); text-align: start; }
76
+ .mui-ex-pivot-menu .mui-ex-pivot-menu-item.MuiListItemButton-dense { font: var(--mui-font-body2); }
77
+ .mui-ex-pivot-menu-mark { display: inline-block; flex: none; width: 1em; text-align: center; }
78
+ .mui-ex-pivot-menu-item[aria-checked='true'] .mui-ex-pivot-menu-mark::before { content: '✓'; }
79
+ /* A group's heading in the Layout menu: Subtotals, Grand Totals, Report Layout. */
80
+ .mui-ex-pivot-menu .mui-ex-pivot-menu-heading { padding: 6px 16px 2px; font-weight: 500; }
81
+
82
+ /* The panels: MUI's controls in a column, the Item list scrolling on its own and the actions at the end. No width of
83
+ their own: the pivot's frame sets it. */
84
+ .mui-ex-pivot-panel { display: flex; flex-direction: column; gap: 12px; padding: 12px; }
85
+ .mui-ex-pivot-items { display: flex; flex-direction: column; max-height: 240px; min-height: 48px; overflow-y: auto; }
86
+ .mui-ex-pivot-loading { display: flex; align-items: center; gap: 8px; }
87
+ .mui-ex-pivot-actions { display: flex; justify-content: flex-end; gap: 8px; flex-shrink: 0; }
88
+
89
+ /* The Pivot Toolbar above the report (ADR-0061): the report filter band on its left; Layout ▾, Refresh and the Field
90
+ List's toggle on its right. */
91
+ .mui-ex-pivot-toolbar-frame { display: flex; flex-direction: column; gap: 8px; padding: 0 0 8px; }
92
+ .mui-ex-pivot-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 16px; }
93
+ .mui-ex-pivot-toolbar-start { flex: 1 1 auto; min-width: 0; }
94
+ .mui-ex-pivot-toolbar-end { display: flex; flex: none; align-items: center; gap: 4px; }
95
+
96
+ /* The report filter band, on the Pivot Toolbar's left: each field's caption beside the button that opens Filter… in
97
+ the anchor around it (ADR-0061). */
98
+ .mui-ex-pivot-filters { display: flex; flex-wrap: wrap; gap: 4px 16px; }
99
+ .mui-ex-pivot-filter { display: flex; align-items: center; gap: 8px; }
100
+ .mui-ex-pivot-filter-anchor { min-width: 160px; max-width: 320px; }
101
+
102
+ /* A button that says a caption, a placed field's or a report filter's: the caption at the start, ending in an
103
+ ellipsis when it is longer than the button, and the ▾ at the end. */
104
+ .mui-ex-pivot-caption-button.MuiButton-root { justify-content: space-between; min-width: 0; }
105
+ .mui-ex-pivot-caption { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
106
+
107
+ /* The Stale Report's notice under the Pivot Toolbar (ADR-0067). */
108
+ .mui-ex-pivot-stale { margin: 0 0 8px; }
109
+
110
+ /* The Field List (ADR-0061): MUI's controls in a column as tall as the pane, the Areas always in view at its foot and
111
+ the list of fields taking what is left, scrolling on its own, as Excel's pane does, so a field is dragged to an Area
112
+ without either scrolling away. */
113
+ .mui-ex-pivot-pane {
114
+ display: flex;
115
+ flex-direction: column;
116
+ gap: 6px;
117
+ padding: 8px 0;
118
+ box-sizing: border-box;
119
+ height: 100%;
120
+ color: var(--mui-palette-text-primary);
121
+ font: var(--mui-font-body2);
122
+ }
123
+ .mui-ex-pivot-pane > * { flex: none; }
124
+ .mui-ex-pivot-pane > .mui-ex-pivot-pane-body {
125
+ position: relative;
126
+ display: flex;
127
+ flex-direction: column;
128
+ gap: inherit;
129
+ flex: 1 1 auto;
130
+ min-height: 0;
131
+ overflow: auto;
132
+ padding: 0 12px;
133
+ }
134
+ .mui-ex-pivot-pane-body > * { flex: none; }
135
+ .mui-ex-pivot-pane-heading { display: flex; align-items: center; gap: 8px; min-width: 0; margin: 0 8px 0 12px; }
136
+ .mui-ex-pivot-pane-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
137
+ .mui-ex-pivot-pane-body > .mui-ex-pivot-fields {
138
+ flex: 1 1 0;
139
+ display: flex;
140
+ flex-direction: column;
141
+ min-height: 4.5em;
142
+ overflow-y: auto;
143
+ padding: 0 8px;
144
+ border: 1px solid var(--mui-palette-divider);
145
+ border-radius: var(--mui-shape-borderRadius);
146
+ }
147
+ .mui-ex-pivot-field { display: flex; align-items: center; gap: 4px; cursor: grab; }
148
+ .mui-ex-pivot-field .mui-ex-pivot-label { flex: 1 1 auto; min-width: 0; margin-right: 0; }
149
+ .mui-ex-pivot-fields.mui-ex-pivot-drop-remove { outline: 2px dashed var(--mui-palette-primary-main); outline-offset: -2px; }
150
+ .mui-ex-pivot-dragging { opacity: 0.5; }
151
+
152
+ /* The four Areas, two by two as Excel's pane has them, one above another when narrow. */
153
+ .mui-ex-pivot-areas { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 6px; }
154
+ .mui-ex-pivot-area.MuiPaper-root { display: flex; flex-direction: column; min-height: 64px; background: var(--mui-palette-action-hover); }
155
+ .mui-ex-pivot-area.mui-ex-pivot-drop-target { outline: 2px solid var(--mui-palette-primary-main); outline-offset: -2px; }
156
+ .mui-ex-pivot-area-title { padding: 2px 8px; border-bottom: 1px solid var(--mui-palette-divider); }
157
+ .mui-ex-pivot-entries { display: flex; flex-direction: column; gap: 4px; padding: 4px; flex: 1 1 auto; }
158
+ /* A placed field's ground is the paper's, on the entry rather than its button, so the button's hover and keyboard-focus
159
+ shades still fall on it. */
160
+ .mui-ex-pivot-entry { border-radius: var(--mui-shape-borderRadius); background: var(--mui-palette-background-paper); }
161
+ .mui-ex-pivot-entry.mui-ex-pivot-drop-before { box-shadow: 0 -2px 0 var(--mui-palette-primary-main); }
162
+
163
+ /* Defer Layout Update and Update at the pane's foot (ADR-0061). */
164
+ .mui-ex-pivot-defer { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 0 12px; }
165
+
166
+ /* Show Details' tabs at the report's foot (ADR-0059): MUI's tabs where Excel's sheet tabs are, a tab keeping the width
167
+ of its title, cut with an ellipsis past a sheet tab's width, its close mark after it. MUI's own panels are not
168
+ drawn: ExPivot places the records over the report. */
169
+ .mui-ex-pivot-tabs .mui-ex-pivot-tab { text-transform: none; max-width: 320px; }
170
+ .mui-ex-pivot-tab-label { display: inline-flex; align-items: center; gap: 4px; min-width: 0; max-width: 100%; }
171
+ .mui-ex-pivot-tab-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
172
+ .mui-ex-pivot-tab-close { flex: none; }
173
+ /* A compact pivot's tabs, as low as its small buttons: MUI's tabs take no size of their own. */
174
+ .mui-ex-pivot-tabs.mui-ex-pivot-tabs-dense,
175
+ .mui-ex-pivot-tabs-dense .mui-ex-pivot-tab { min-height: 32px; }
176
+ .mui-ex-pivot-tabs-dense .mui-ex-pivot-tab { padding-top: 4px; padding-bottom: 4px; }
177
+
178
+ /* Show Details' dialog, inside ExPivot's frame (ADR-0059): laid out as a Dialog's content, the title, the records
179
+ taking what is left, and Close at the end. */
180
+ .mui-ex-pivot-dialog { display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; min-height: 0; padding: 16px; }
181
+ .mui-ex-pivot-dialog-records { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
182
+
183
+ /* The ▾ after a button's words at the words' size: MUI sets an end icon's child at an icon's. */
184
+ .mui-ex-pivot .MuiButton-endIcon > .mui-ex-pivot-opens { font-size: inherit; }