@12-apps/ui 6.64.0 → 6.64.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/components/form/PickerSheet/PickerSheet.js +5 -4
- package/dist/_internal/components/form/PickerSheet/PickerSheet.parts.js +3 -2
- package/dist/_internal/components/form/PickerSheet/PickerSheet.styles.js +2 -1
- package/dist/_internal/components/form/PickerSheet/usePickerSheet.js +1 -1
- package/dist/{chunk-Z3DEMZ5J.js → chunk-OLT6ZXK5.js} +2 -2
- package/dist/{chunk-43UPEJML.js → chunk-TX3B7OAA.js} +4 -4
- package/dist/{chunk-ZQRNCUBB.js → chunk-WSMUOFWF.js} +3 -10
- package/dist/chunk-WSMUOFWF.js.map +1 -0
- package/dist/{chunk-RLTCU57K.js → chunk-WYRDR4T5.js} +11 -3
- package/dist/chunk-WYRDR4T5.js.map +1 -0
- package/dist/form/PickerSheet.js +5 -4
- package/dist/types/components/form/PickerSheet/PickerSheet.styles.d.ts.map +1 -1
- package/dist/types/components/form/PickerSheet/usePickerSheet.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/form/PickerSheet/PickerSheet.md +1 -1
- package/src/components/form/PickerSheet/PickerSheet.styles.ts +8 -2
- package/src/components/form/PickerSheet/usePickerSheet.ts +5 -17
- package/dist/chunk-RLTCU57K.js.map +0 -1
- package/dist/chunk-ZQRNCUBB.js.map +0 -1
- /package/dist/{chunk-Z3DEMZ5J.js.map → chunk-OLT6ZXK5.js.map} +0 -0
- /package/dist/{chunk-43UPEJML.js.map → chunk-TX3B7OAA.js.map} +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
PickerSheet
|
|
4
|
-
} from "../../../../chunk-
|
|
5
|
-
import "../../../../chunk-
|
|
6
|
-
import "../../../../chunk-
|
|
4
|
+
} from "../../../../chunk-TX3B7OAA.js";
|
|
5
|
+
import "../../../../chunk-OLT6ZXK5.js";
|
|
6
|
+
import "../../../../chunk-WSMUOFWF.js";
|
|
7
7
|
import "../../../../chunk-QZOXMH7X.js";
|
|
8
|
-
import "../../../../chunk-
|
|
8
|
+
import "../../../../chunk-WYRDR4T5.js";
|
|
9
9
|
import "../../../../chunk-VPGDYI63.js";
|
|
10
10
|
import "../../../../chunk-DMNZD3Q7.js";
|
|
11
11
|
import "../../../../chunk-PN4HVM43.js";
|
|
12
|
+
import "../../../../chunk-TZ23DNKY.js";
|
|
12
13
|
import "../../../../chunk-RGVNXES5.js";
|
|
13
14
|
import "../../../../chunk-GDN2LUTZ.js";
|
|
14
15
|
import "../../../../chunk-7ZADLRLP.js";
|
|
@@ -3,9 +3,10 @@ import {
|
|
|
3
3
|
PickerSheetHead,
|
|
4
4
|
PickerSheetList,
|
|
5
5
|
PickerSheetSearch
|
|
6
|
-
} from "../../../../chunk-
|
|
7
|
-
import "../../../../chunk-
|
|
6
|
+
} from "../../../../chunk-OLT6ZXK5.js";
|
|
7
|
+
import "../../../../chunk-WYRDR4T5.js";
|
|
8
8
|
import "../../../../chunk-DMNZD3Q7.js";
|
|
9
|
+
import "../../../../chunk-TZ23DNKY.js";
|
|
9
10
|
import "../../../../chunk-RGVNXES5.js";
|
|
10
11
|
import "../../../../chunk-GDN2LUTZ.js";
|
|
11
12
|
import "../../../../chunk-7ZADLRLP.js";
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
searchFieldSx,
|
|
18
18
|
sheetPaperSx,
|
|
19
19
|
titleSx
|
|
20
|
-
} from "../../../../chunk-
|
|
20
|
+
} from "../../../../chunk-WYRDR4T5.js";
|
|
21
|
+
import "../../../../chunk-TZ23DNKY.js";
|
|
21
22
|
import "../../../../chunk-RGVNXES5.js";
|
|
22
23
|
import "../../../../chunk-GDN2LUTZ.js";
|
|
23
24
|
import "../../../../chunk-7ZADLRLP.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
rowTextSx,
|
|
14
14
|
searchFieldSx,
|
|
15
15
|
titleSx
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-WYRDR4T5.js";
|
|
17
17
|
import {
|
|
18
18
|
CheckGlyph,
|
|
19
19
|
SearchGlyph
|
|
@@ -152,4 +152,4 @@ export {
|
|
|
152
152
|
PickerSheetSearch,
|
|
153
153
|
PickerSheetList
|
|
154
154
|
};
|
|
155
|
-
//# sourceMappingURL=chunk-
|
|
155
|
+
//# sourceMappingURL=chunk-OLT6ZXK5.js.map
|
|
@@ -2,16 +2,16 @@ import {
|
|
|
2
2
|
PickerSheetHead,
|
|
3
3
|
PickerSheetList,
|
|
4
4
|
PickerSheetSearch
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OLT6ZXK5.js";
|
|
6
6
|
import {
|
|
7
7
|
usePickerSheet
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WSMUOFWF.js";
|
|
9
9
|
import {
|
|
10
10
|
bodySx,
|
|
11
11
|
dialogPaperSx,
|
|
12
12
|
footSx,
|
|
13
13
|
sheetPaperSx
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-WYRDR4T5.js";
|
|
15
15
|
import {
|
|
16
16
|
METRICS
|
|
17
17
|
} from "./chunk-VPGDYI63.js";
|
|
@@ -105,4 +105,4 @@ function PickerSheet(props) {
|
|
|
105
105
|
export {
|
|
106
106
|
PickerSheet
|
|
107
107
|
};
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-TX3B7OAA.js.map
|
|
@@ -5,14 +5,7 @@ import {
|
|
|
5
5
|
|
|
6
6
|
// src/components/form/PickerSheet/usePickerSheet.ts
|
|
7
7
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
-
function
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
if (!open) return void 0;
|
|
11
|
-
const frame = requestAnimationFrame(() => {
|
|
12
|
-
listRef.current?.querySelector('[aria-selected="true"]')?.scrollIntoView?.({ block: "nearest" });
|
|
13
|
-
});
|
|
14
|
-
return () => cancelAnimationFrame(frame);
|
|
15
|
-
}, [open, listRef]);
|
|
8
|
+
function useCursorScroll(activeIndex, listRef) {
|
|
16
9
|
useEffect(() => {
|
|
17
10
|
if (activeIndex < 0) return;
|
|
18
11
|
const rows = listRef.current?.querySelectorAll('[role="option"]');
|
|
@@ -37,7 +30,7 @@ function usePickerSheet({ open, items, onPick, onCreate, createLabel }) {
|
|
|
37
30
|
const creatable = Boolean(onCreate && createLabel);
|
|
38
31
|
const view = useMemo(() => pickerSheetView(items, query, creatable), [items, query, creatable]);
|
|
39
32
|
const activeIndex = rawActiveIndex < view.rowCount ? rawActiveIndex : -1;
|
|
40
|
-
|
|
33
|
+
useCursorScroll(activeIndex, listRef);
|
|
41
34
|
const setQuery = useCallback((next) => {
|
|
42
35
|
setRawQuery(next);
|
|
43
36
|
setActiveIndex(next.trim() ? 0 : -1);
|
|
@@ -75,4 +68,4 @@ function usePickerSheet({ open, items, onPick, onCreate, createLabel }) {
|
|
|
75
68
|
export {
|
|
76
69
|
usePickerSheet
|
|
77
70
|
};
|
|
78
|
-
//# sourceMappingURL=chunk-
|
|
71
|
+
//# sourceMappingURL=chunk-WSMUOFWF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/PickerSheet/usePickerSheet.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { pickerSheetView, stepActiveIndex, type PickerSheetView } from './picker-sheet-model';\nimport type { PickerSheetProps } from './PickerSheet.types';\n\n/** Everything the sheet's parts read: the query, the derived list, the cursor and the handlers. */\ninterface PickerSheetState {\n query: string;\n view: PickerSheetView;\n /** The keyboard cursor, an index into `view` (items, then the create row); -1 for none. Always a row that exists. */\n activeIndex: number;\n /** Draw the cursor: only once an arrow key has moved it, never from typing alone. */\n cursorShown: boolean;\n searchInputRef: React.RefObject<HTMLInputElement | null>;\n listRef: React.RefObject<HTMLUListElement | null>;\n setQuery: (next: string) => void;\n /** Pick the row at `index` — an item, or the create row when it is the last one. */\n choose: (index: number) => void;\n onSearchKeyDown: (event: React.KeyboardEvent) => void;\n}\n\ntype HookProps = Pick<PickerSheetProps, 'open' | 'items' | 'onPick' | 'onCreate' | 'createLabel'>;\n\n/**\n * Keep the keyboard cursor in view.\n *\n * Nothing inside is focused on open — the modal focuses its own paper. Focusing\n * the search box raised a phone's keyboard over the list it was opened to show.\n * The list opens at its top, the selected row wherever it falls (the design's\n * picker does not jump to it).\n */\nfunction useCursorScroll(activeIndex: number, listRef: React.RefObject<HTMLUListElement | null>): void {\n useEffect(() => {\n if (activeIndex < 0) return;\n const rows = listRef.current?.querySelectorAll('[role=\"option\"]');\n rows?.[activeIndex]?.scrollIntoView?.({ block: 'nearest' });\n }, [activeIndex, listRef]);\n}\n\n/**\n * The sheet's state. The query resets every time the sheet opens; typing puts\n * the cursor on the first match, so \"type, then Enter\" picks it (or creates,\n * when the create row is all there is).\n */\nexport function usePickerSheet({ open, items, onPick, onCreate, createLabel }: HookProps): PickerSheetState {\n const [query, setRawQuery] = useState('');\n const [rawActiveIndex, setActiveIndex] = useState(-1);\n const [cursorShown, setCursorShown] = useState(false);\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n const listRef = useRef<HTMLUListElement | null>(null);\n\n // Reset while rendering the frame that opens the sheet, not in an effect\n // after it — an effect would paint the last session's query for one frame.\n const [wasOpen, setWasOpen] = useState(open);\n if (open !== wasOpen) {\n setWasOpen(open);\n if (open) {\n setRawQuery('');\n setActiveIndex(-1);\n setCursorShown(false);\n }\n }\n\n const creatable = Boolean(onCreate && createLabel);\n const view = useMemo(() => pickerSheetView(items, query, creatable), [items, query, creatable]);\n // Clamped on read: the rows can shrink under the cursor (a query, new items),\n // and `aria-activedescendant` must never name a row that is not there.\n const activeIndex = rawActiveIndex < view.rowCount ? rawActiveIndex : -1;\n\n useCursorScroll(activeIndex, listRef);\n\n const setQuery = useCallback((next: string) => {\n setRawQuery(next);\n setActiveIndex(next.trim() ? 0 : -1);\n }, []);\n\n const choose = useCallback(\n (index: number) => {\n const item = view.items[index];\n if (item) {\n onPick(item.id);\n return;\n }\n if (view.canCreate && index === view.items.length) onCreate?.(view.query);\n },\n [view, onPick, onCreate],\n );\n\n const onSearchKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n const delta = event.key === 'ArrowDown' ? 1 : -1;\n setActiveIndex(stepActiveIndex(activeIndex, delta, view.rowCount));\n setCursorShown(true);\n return;\n }\n if (event.key === 'Enter' && activeIndex >= 0) {\n event.preventDefault();\n choose(activeIndex);\n }\n // Escape is the modal's: it closes through `onClose`, once.\n },\n [view.rowCount, activeIndex, choose],\n );\n\n return { query, view, activeIndex, cursorShown, searchInputRef, listRef, setQuery, choose, onSearchKeyDown };\n}\n"],"mappings":";;;;;;AAEA,SAAS,aAAa,WAAW,SAAS,QAAQ,gBAAgB;AA+BlE,SAAS,gBAAgB,aAAqB,SAAyD;AACrG,YAAU,MAAM;AACd,QAAI,cAAc,EAAG;AACrB,UAAM,OAAO,QAAQ,SAAS,iBAAiB,iBAAiB;AAChE,WAAO,WAAW,GAAG,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAAA,EAC5D,GAAG,CAAC,aAAa,OAAO,CAAC;AAC3B;AAOO,SAAS,eAAe,EAAE,MAAM,OAAO,QAAQ,UAAU,YAAY,GAAgC;AAC1G,QAAM,CAAC,OAAO,WAAW,IAAI,SAAS,EAAE;AACxC,QAAM,CAAC,gBAAgB,cAAc,IAAI,SAAS,EAAE;AACpD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,iBAAiB,OAAgC,IAAI;AAC3D,QAAM,UAAU,OAAgC,IAAI;AAIpD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,MAAI,SAAS,SAAS;AACpB,eAAW,IAAI;AACf,QAAI,MAAM;AACR,kBAAY,EAAE;AACd,qBAAe,EAAE;AACjB,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,YAAY,QAAQ,YAAY,WAAW;AACjD,QAAM,OAAO,QAAQ,MAAM,gBAAgB,OAAO,OAAO,SAAS,GAAG,CAAC,OAAO,OAAO,SAAS,CAAC;AAG9F,QAAM,cAAc,iBAAiB,KAAK,WAAW,iBAAiB;AAEtE,kBAAgB,aAAa,OAAO;AAEpC,QAAM,WAAW,YAAY,CAAC,SAAiB;AAC7C,gBAAY,IAAI;AAChB,mBAAe,KAAK,KAAK,IAAI,IAAI,EAAE;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,QAAM,SAAS;AAAA,IACb,CAAC,UAAkB;AACjB,YAAM,OAAO,KAAK,MAAM,KAAK;AAC7B,UAAI,MAAM;AACR,eAAO,KAAK,EAAE;AACd;AAAA,MACF;AACA,UAAI,KAAK,aAAa,UAAU,KAAK,MAAM,OAAQ,YAAW,KAAK,KAAK;AAAA,IAC1E;AAAA,IACA,CAAC,MAAM,QAAQ,QAAQ;AAAA,EACzB;AAEA,QAAM,kBAAkB;AAAA,IACtB,CAAC,UAA+B;AAC9B,UAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,WAAW;AACxD,cAAM,eAAe;AACrB,cAAM,QAAQ,MAAM,QAAQ,cAAc,IAAI;AAC9C,uBAAe,gBAAgB,aAAa,OAAO,KAAK,QAAQ,CAAC;AACjE,uBAAe,IAAI;AACnB;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,WAAW,eAAe,GAAG;AAC7C,cAAM,eAAe;AACrB,eAAO,WAAW;AAAA,MACpB;AAAA,IAEF;AAAA,IACA,CAAC,KAAK,UAAU,aAAa,MAAM;AAAA,EACrC;AAEA,SAAO,EAAE,OAAO,MAAM,aAAa,aAAa,gBAAgB,SAAS,UAAU,QAAQ,gBAAgB;AAC7G;","names":[]}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
surfaces
|
|
3
|
+
} from "./chunk-TZ23DNKY.js";
|
|
1
4
|
import {
|
|
2
5
|
fieldBorder,
|
|
3
6
|
fieldHeight
|
|
@@ -51,7 +54,8 @@ var dialogPaperSx = (theme) => ({
|
|
|
51
54
|
margin: 0,
|
|
52
55
|
borderRadius: rem(theme, METRICS.dialogRadiusPx),
|
|
53
56
|
boxShadow: panelShadow(theme),
|
|
54
|
-
|
|
57
|
+
// The raised step (`uiSurfaces.raised`): a sheet is nearer than the page.
|
|
58
|
+
background: surfaces(theme).raised,
|
|
55
59
|
backgroundImage: "none",
|
|
56
60
|
display: "flex",
|
|
57
61
|
flexDirection: "column",
|
|
@@ -62,7 +66,8 @@ var sheetPaperSx = (theme) => ({
|
|
|
62
66
|
maxHeight: "88%",
|
|
63
67
|
borderRadius: rems(theme, METRICS.sheetRadiusPx, METRICS.sheetRadiusPx, 0, 0),
|
|
64
68
|
boxShadow: panelShadow(theme),
|
|
65
|
-
|
|
69
|
+
// The raised step (`uiSurfaces.raised`): a sheet is nearer than the page.
|
|
70
|
+
background: surfaces(theme).raised,
|
|
66
71
|
backgroundImage: "none",
|
|
67
72
|
display: "flex",
|
|
68
73
|
flexDirection: "column",
|
|
@@ -169,6 +174,9 @@ var rowSx = (theme, selected, active) => ({
|
|
|
169
174
|
gap: rem(theme, 10),
|
|
170
175
|
padding: rem(theme, METRICS.rowPaddingPx),
|
|
171
176
|
minHeight: rem(theme, METRICS.rowMinHeightPx),
|
|
177
|
+
// The list is a capped flex column: without this a row with a meta line
|
|
178
|
+
// shrinks to the 44px floor and the two lines overprint.
|
|
179
|
+
flexShrink: 0,
|
|
172
180
|
boxSizing: "border-box",
|
|
173
181
|
borderRadius: rem(theme, METRICS.controlRadiusPx),
|
|
174
182
|
textAlign: "left",
|
|
@@ -235,4 +243,4 @@ export {
|
|
|
235
243
|
emptySx,
|
|
236
244
|
footSx
|
|
237
245
|
};
|
|
238
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-WYRDR4T5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/PickerSheet/PickerSheet.styles.ts"],"sourcesContent":["import { alpha, type CSSObject, type Theme } from '@mui/material/styles/index.js';\n\nimport { fieldBorder, fieldHeight } from '../../../tokens/field-height';\nimport { fieldRadiusPx } from '../../../tokens/field-radius';\nimport { fieldWell, uiInk } from '../../../tokens/ink';\nimport { rem, rems } from '../../../tokens/relative';\nimport { surfaces } from '../../../tokens/surfaces';\n\n/**\n * The prototype's measurements, verbatim, in the design's px.\n *\n * Each is drawn through `rem()` where it is read, so the sheet follows a\n * density mode the way every other surface in the package does. Colours come\n * from the palette (a white-labelled tenant and dark mode carry through).\n */\nconst METRICS = {\n dialogWidthPx: 520,\n dialogMaxHeightPx: 780,\n dialogRadiusPx: 14,\n /** The gap a centred dialog keeps from the viewport edge. */\n dialogMarginPx: 32,\n sheetRadiusPx: 16,\n headGapPx: 10,\n closeButtonPx: 36,\n controlRadiusPx: 8,\n bodyPaddingPx: 16,\n bodyGapPx: 12,\n listMaxHeightPx: 380,\n listGapPx: 2,\n rowPaddingPx: 10,\n rowMinHeightPx: 44,\n /** Left padding per level of `indent` (`.pitem.ind{padding-left:28px}` at depth 1). */\n rowIndentStepPx: 28,\n checkGlyphPx: 16,\n closeGlyphPx: 18,\n searchGlyphPx: 16,\n /** The magnifier's inset from the field's inner edge, and the gap before the text. */\n searchGlyphInsetPx: 10,\n searchGlyphGapPx: 6,\n /** The prototype's body line-height; the sheet sets it rather than inheriting a host's. */\n lineHeight: 1.4,\n} as const;\n\n/** The panel's shadow: `0 16px 48px` of ink at 0.28. */\nconst panelShadow = (theme: Theme): string =>\n `${rems(theme, 0, 16, 48)} ${alpha(uiInk(theme).panelShadowInk, 0.28)}`;\n\n/** The centred dialog's paper, on a pointer. */\nexport const dialogPaperSx = (theme: Theme): CSSObject => ({\n width: rem(theme, METRICS.dialogWidthPx),\n maxWidth: `calc(100% - ${rem(theme, METRICS.dialogMarginPx)})`,\n maxHeight: `min(${rem(theme, METRICS.dialogMaxHeightPx)}, calc(100% - ${rem(theme, METRICS.dialogMarginPx * 2)}))`,\n margin: 0,\n borderRadius: rem(theme, METRICS.dialogRadiusPx),\n boxShadow: panelShadow(theme),\n // The raised step (`uiSurfaces.raised`): a sheet is nearer than the page.\n background: surfaces(theme).raised,\n backgroundImage: 'none',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n});\n\n/** The bottom sheet's paper, on a phone: full width, no grab handle. */\nexport const sheetPaperSx = (theme: Theme): CSSObject => ({\n width: '100%',\n maxHeight: '88%',\n borderRadius: rems(theme, METRICS.sheetRadiusPx, METRICS.sheetRadiusPx, 0, 0),\n boxShadow: panelShadow(theme),\n // The raised step (`uiSurfaces.raised`): a sheet is nearer than the page.\n background: surfaces(theme).raised,\n backgroundImage: 'none',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n});\n\n/** `.sh` — kicker and title on the left, the close button on the right. */\nexport const headSx = (theme: Theme): CSSObject => ({\n display: 'flex',\n alignItems: 'center',\n gap: rem(theme, METRICS.headGapPx),\n padding: rems(theme, 14, 16),\n // A shade under the controls' own border: the rule separates, it does not frame.\n borderBottom: `1px solid ${alpha(theme.palette.divider, 0.6)}`,\n flex: '0 0 auto',\n});\n\nexport const kickerSx = (theme: Theme): CSSObject => ({\n fontSize: rem(theme, 11.5),\n color: theme.palette.text.secondary,\n lineHeight: METRICS.lineHeight,\n});\n\nexport const titleSx = (theme: Theme): CSSObject => ({\n margin: 0,\n fontSize: rem(theme, 14),\n fontWeight: 700,\n lineHeight: METRICS.lineHeight,\n color: theme.palette.text.primary,\n});\n\nexport const closeButtonSx = (theme: Theme): CSSObject => ({\n flex: '0 0 auto',\n width: rem(theme, METRICS.closeButtonPx),\n height: rem(theme, METRICS.closeButtonPx),\n display: 'grid',\n placeItems: 'center',\n padding: 0,\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: rem(theme, METRICS.controlRadiusPx),\n background: 'transparent',\n color: theme.palette.text.primary,\n cursor: 'pointer',\n '& svg': { width: rem(theme, METRICS.closeGlyphPx), height: rem(theme, METRICS.closeGlyphPx) },\n '&:hover': { background: theme.palette.action.hover },\n '&:focus-visible': { outline: `${rem(theme, 2)} solid ${theme.palette.primary.main}`, outlineOffset: rem(theme, 1) },\n});\n\n/** `.sb` — the scrolling body: search, list, foot. */\nexport const bodySx = (theme: Theme): CSSObject => ({\n display: 'flex',\n flexDirection: 'column',\n gap: rem(theme, METRICS.bodyGapPx),\n padding: rem(theme, METRICS.bodyPaddingPx),\n overflow: 'auto',\n minHeight: 0,\n flex: '1 1 auto',\n});\n\n/**\n * The search field: the library's field height, border and corner, with a\n * leading magnifier. White on the light sheet, so the field reads as a field\n * against the paper behind it.\n */\nexport const searchFieldSx = (theme: Theme): CSSObject => ({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n flex: '0 0 auto',\n '& > svg': {\n position: 'absolute',\n // + 1 for the input's own border, which the glyph sits inside of.\n left: rem(theme, METRICS.searchGlyphInsetPx + 1),\n width: rem(theme, METRICS.searchGlyphPx),\n height: rem(theme, METRICS.searchGlyphPx),\n color: theme.palette.text.secondary,\n pointerEvents: 'none',\n zIndex: 1,\n },\n '& input': {\n width: '100%',\n height: fieldHeight(theme),\n boxSizing: 'border-box',\n padding: rems(theme, 0, 10, 0, METRICS.searchGlyphInsetPx + METRICS.searchGlyphPx + METRICS.searchGlyphGapPx),\n fontSize: rem(theme, 14),\n border: fieldBorder(theme),\n borderRadius: fieldRadiusPx(theme),\n background: fieldWell(theme),\n color: theme.palette.text.primary,\n '&:focus': {\n outline: 'none',\n borderColor: theme.palette.primary.main,\n boxShadow: `0 0 0 ${rem(theme, 3)} ${alpha(theme.palette.primary.main, 0.15)}`,\n },\n },\n});\n\n/** The list: a column, scrolling on its own past 380px. */\nexport const listSx = (theme: Theme): CSSObject => ({\n display: 'flex',\n flexDirection: 'column',\n gap: rem(theme, METRICS.listGapPx),\n maxHeight: rem(theme, METRICS.listMaxHeightPx),\n overflow: 'auto',\n overscrollBehavior: 'contain',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n flex: '0 1 auto',\n});\n\n/** Left padding for a row at `depth` — the row's own 10px at depth 0, 28px per level below it. */\nexport const rowPaddingLeft = (theme: Theme, depth: number): string =>\n rem(theme, depth > 0 ? METRICS.rowIndentStepPx * depth : METRICS.rowPaddingPx);\n\n/** Background of a row: selected wins over the keyboard cursor, which wins over rest. */\nfunction rowBackground(theme: Theme, selected: boolean, active: boolean): string {\n if (selected) return alpha(theme.palette.primary.main, 0.1);\n return active ? theme.palette.action.hover : 'transparent';\n}\n\n/** `.pitem` — a row. `active` is the keyboard cursor, drawn as a ring so it reads apart from `selected`. */\nexport const rowSx = (theme: Theme, selected: boolean, active: boolean): CSSObject => ({\n display: 'flex',\n alignItems: 'center',\n gap: rem(theme, 10),\n padding: rem(theme, METRICS.rowPaddingPx),\n minHeight: rem(theme, METRICS.rowMinHeightPx),\n // The list is a capped flex column: without this a row with a meta line\n // shrinks to the 44px floor and the two lines overprint.\n flexShrink: 0,\n boxSizing: 'border-box',\n borderRadius: rem(theme, METRICS.controlRadiusPx),\n textAlign: 'left',\n cursor: 'pointer',\n userSelect: 'none',\n color: theme.palette.text.primary,\n background: rowBackground(theme, selected, active),\n boxShadow: active ? `inset 0 0 0 ${rem(theme, 1)} ${alpha(theme.palette.primary.main, 0.3)}` : 'none',\n '&:hover': { background: selected ? alpha(theme.palette.primary.main, 0.1) : theme.palette.action.hover },\n});\n\nexport const rowTextSx: CSSObject = { flex: '1 1 auto', minWidth: 0 };\n\nexport const rowLabelSx = (theme: Theme): CSSObject => ({\n display: 'block',\n fontSize: rem(theme, 14),\n lineHeight: METRICS.lineHeight,\n});\n\nexport const rowMetaSx = (theme: Theme): CSSObject => ({\n display: 'block',\n fontSize: rem(theme, 12),\n lineHeight: METRICS.lineHeight,\n color: theme.palette.text.secondary,\n});\n\nexport const checkSx = (theme: Theme): CSSObject => ({\n flex: '0 0 auto',\n display: 'grid',\n color: theme.palette.primary.main,\n '& svg': { width: rem(theme, METRICS.checkGlyphPx), height: rem(theme, METRICS.checkGlyphPx) },\n});\n\n/** The create row's text: the accent, at 600. */\nexport const createLabelSx = (theme: Theme): CSSObject => ({\n ...rowLabelSx(theme),\n color: theme.palette.primary.main,\n fontWeight: 600,\n});\n\nexport const emptySx = (theme: Theme): CSSObject => ({\n fontSize: rem(theme, 12),\n lineHeight: METRICS.lineHeight,\n color: theme.palette.text.secondary,\n padding: rem(theme, 10),\n});\n\nexport const footSx = (theme: Theme): CSSObject => ({\n margin: 0,\n fontSize: rem(theme, 12),\n lineHeight: METRICS.lineHeight,\n color: theme.palette.text.secondary,\n});\n"],"mappings":"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","names":[]}
|
package/dist/form/PickerSheet.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PickerSheet
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-TX3B7OAA.js";
|
|
4
|
+
import "../chunk-OLT6ZXK5.js";
|
|
5
|
+
import "../chunk-WSMUOFWF.js";
|
|
6
6
|
import "../chunk-QZOXMH7X.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-WYRDR4T5.js";
|
|
8
8
|
import "../chunk-VPGDYI63.js";
|
|
9
9
|
import "../chunk-DMNZD3Q7.js";
|
|
10
10
|
import "../chunk-PN4HVM43.js";
|
|
11
|
+
import "../chunk-TZ23DNKY.js";
|
|
11
12
|
import "../chunk-RGVNXES5.js";
|
|
12
13
|
import "../chunk-GDN2LUTZ.js";
|
|
13
14
|
import "../chunk-7ZADLRLP.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickerSheet.styles.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/PickerSheet/PickerSheet.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"PickerSheet.styles.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/PickerSheet/PickerSheet.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,+BAA+B,CAAC;AA+ClF,gDAAgD;AAChD,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,SAa3C,CAAC;AAEH,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,OAAO,KAAK,KAAG,SAW1C,CAAC;AAEH,2EAA2E;AAC3E,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,KAAG,SAQpC,CAAC;AAEH,eAAO,MAAM,QAAQ,GAAI,OAAO,KAAK,KAAG,SAItC,CAAC;AAEH,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,SAMrC,CAAC;AAEH,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,SAe3C,CAAC;AAEH,sDAAsD;AACtD,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,KAAG,SAQpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,SA+B3C,CAAC;AAEH,2DAA2D;AAC3D,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,KAAG,SAWpC,CAAC;AAEH,kGAAkG;AAClG,eAAO,MAAM,cAAc,GAAI,OAAO,KAAK,EAAE,OAAO,MAAM,KAAG,MACmB,CAAC;AAQjF,4GAA4G;AAC5G,eAAO,MAAM,KAAK,GAAI,OAAO,KAAK,EAAE,UAAU,OAAO,EAAE,QAAQ,OAAO,KAAG,SAkBvE,CAAC;AAEH,eAAO,MAAM,SAAS,EAAE,SAA6C,CAAC;AAEtE,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,KAAG,SAIxC,CAAC;AAEH,eAAO,MAAM,SAAS,GAAI,OAAO,KAAK,KAAG,SAKvC,CAAC;AAEH,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,SAKrC,CAAC;AAEH,iDAAiD;AACjD,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,SAI3C,CAAC;AAEH,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,SAKrC,CAAC;AAEH,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,KAAG,SAKpC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePickerSheet.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/PickerSheet/usePickerSheet.ts"],"names":[],"mappings":"AAIA,OAAO,EAAoC,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC9F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,mGAAmG;AACnG,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;IACtB,qHAAqH;IACrH,WAAW,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACzD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAClD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,oFAAoF;IACpF,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;CACvD;AAED,KAAK,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"usePickerSheet.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/PickerSheet/usePickerSheet.ts"],"names":[],"mappings":"AAIA,OAAO,EAAoC,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC9F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,mGAAmG;AACnG,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;IACtB,qHAAqH;IACrH,WAAW,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACzD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAClD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,oFAAoF;IACpF,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC;CACvD;AAED,KAAK,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAC,CAAC;AAkBlG;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,SAAS,GAAG,gBAAgB,CA+D1G"}
|
package/package.json
CHANGED
|
@@ -44,7 +44,7 @@ Every word it shows comes from the caller: the package ships no copy for it.
|
|
|
44
44
|
|
|
45
45
|
## Behaviour
|
|
46
46
|
|
|
47
|
-
- Nothing takes focus inside the sheet on open — the dialog itself does — so a phone does not raise its keyboard over the list
|
|
47
|
+
- Nothing takes focus inside the sheet on open — the dialog itself does — so a phone does not raise its keyboard over the list. The list opens at its top. Tab reaches the search box.
|
|
48
48
|
- Filtering is a substring match on each item's label and `searchText`, case and ACCENTS aside ("acai" finds "Açaí", as in `CategorySelect`).
|
|
49
49
|
- ↑/↓ move the keyboard cursor (wrapping at both ends, as `Command` does) and Enter picks the row under it. Typing puts the cursor on the first match, so "type, then Enter" picks it — or creates, when the create row is the only row. The cursor is drawn only once an arrow key has moved it: typing alone shows no ring on a phone.
|
|
50
50
|
- Esc and a tap outside close (through `onClose`).
|
|
@@ -4,6 +4,7 @@ import { fieldBorder, fieldHeight } from '../../../tokens/field-height';
|
|
|
4
4
|
import { fieldRadiusPx } from '../../../tokens/field-radius';
|
|
5
5
|
import { fieldWell, uiInk } from '../../../tokens/ink';
|
|
6
6
|
import { rem, rems } from '../../../tokens/relative';
|
|
7
|
+
import { surfaces } from '../../../tokens/surfaces';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* The prototype's measurements, verbatim, in the design's px.
|
|
@@ -52,7 +53,8 @@ export const dialogPaperSx = (theme: Theme): CSSObject => ({
|
|
|
52
53
|
margin: 0,
|
|
53
54
|
borderRadius: rem(theme, METRICS.dialogRadiusPx),
|
|
54
55
|
boxShadow: panelShadow(theme),
|
|
55
|
-
|
|
56
|
+
// The raised step (`uiSurfaces.raised`): a sheet is nearer than the page.
|
|
57
|
+
background: surfaces(theme).raised,
|
|
56
58
|
backgroundImage: 'none',
|
|
57
59
|
display: 'flex',
|
|
58
60
|
flexDirection: 'column',
|
|
@@ -65,7 +67,8 @@ export const sheetPaperSx = (theme: Theme): CSSObject => ({
|
|
|
65
67
|
maxHeight: '88%',
|
|
66
68
|
borderRadius: rems(theme, METRICS.sheetRadiusPx, METRICS.sheetRadiusPx, 0, 0),
|
|
67
69
|
boxShadow: panelShadow(theme),
|
|
68
|
-
|
|
70
|
+
// The raised step (`uiSurfaces.raised`): a sheet is nearer than the page.
|
|
71
|
+
background: surfaces(theme).raised,
|
|
69
72
|
backgroundImage: 'none',
|
|
70
73
|
display: 'flex',
|
|
71
74
|
flexDirection: 'column',
|
|
@@ -194,6 +197,9 @@ export const rowSx = (theme: Theme, selected: boolean, active: boolean): CSSObje
|
|
|
194
197
|
gap: rem(theme, 10),
|
|
195
198
|
padding: rem(theme, METRICS.rowPaddingPx),
|
|
196
199
|
minHeight: rem(theme, METRICS.rowMinHeightPx),
|
|
200
|
+
// The list is a capped flex column: without this a row with a meta line
|
|
201
|
+
// shrinks to the 44px floor and the two lines overprint.
|
|
202
|
+
flexShrink: 0,
|
|
197
203
|
boxSizing: 'border-box',
|
|
198
204
|
borderRadius: rem(theme, METRICS.controlRadiusPx),
|
|
199
205
|
textAlign: 'left',
|
|
@@ -24,26 +24,14 @@ interface PickerSheetState {
|
|
|
24
24
|
type HookProps = Pick<PickerSheetProps, 'open' | 'items' | 'onPick' | 'onCreate' | 'createLabel'>;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Keep the keyboard cursor in view.
|
|
28
28
|
*
|
|
29
29
|
* Nothing inside is focused on open — the modal focuses its own paper. Focusing
|
|
30
30
|
* the search box raised a phone's keyboard over the list it was opened to show.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* The list opens at its top, the selected row wherever it falls (the design's
|
|
32
|
+
* picker does not jump to it).
|
|
33
33
|
*/
|
|
34
|
-
function
|
|
35
|
-
open: boolean,
|
|
36
|
-
activeIndex: number,
|
|
37
|
-
listRef: React.RefObject<HTMLUListElement | null>,
|
|
38
|
-
): void {
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (!open) return undefined;
|
|
41
|
-
const frame = requestAnimationFrame(() => {
|
|
42
|
-
listRef.current?.querySelector('[aria-selected="true"]')?.scrollIntoView?.({ block: 'nearest' });
|
|
43
|
-
});
|
|
44
|
-
return () => cancelAnimationFrame(frame);
|
|
45
|
-
}, [open, listRef]);
|
|
46
|
-
|
|
34
|
+
function useCursorScroll(activeIndex: number, listRef: React.RefObject<HTMLUListElement | null>): void {
|
|
47
35
|
useEffect(() => {
|
|
48
36
|
if (activeIndex < 0) return;
|
|
49
37
|
const rows = listRef.current?.querySelectorAll('[role="option"]');
|
|
@@ -81,7 +69,7 @@ export function usePickerSheet({ open, items, onPick, onCreate, createLabel }: H
|
|
|
81
69
|
// and `aria-activedescendant` must never name a row that is not there.
|
|
82
70
|
const activeIndex = rawActiveIndex < view.rowCount ? rawActiveIndex : -1;
|
|
83
71
|
|
|
84
|
-
|
|
72
|
+
useCursorScroll(activeIndex, listRef);
|
|
85
73
|
|
|
86
74
|
const setQuery = useCallback((next: string) => {
|
|
87
75
|
setRawQuery(next);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/PickerSheet/PickerSheet.styles.ts"],"sourcesContent":["import { alpha, type CSSObject, type Theme } from '@mui/material/styles/index.js';\n\nimport { fieldBorder, fieldHeight } from '../../../tokens/field-height';\nimport { fieldRadiusPx } from '../../../tokens/field-radius';\nimport { fieldWell, uiInk } from '../../../tokens/ink';\nimport { rem, rems } from '../../../tokens/relative';\n\n/**\n * The prototype's measurements, verbatim, in the design's px.\n *\n * Each is drawn through `rem()` where it is read, so the sheet follows a\n * density mode the way every other surface in the package does. Colours come\n * from the palette (a white-labelled tenant and dark mode carry through).\n */\nconst METRICS = {\n dialogWidthPx: 520,\n dialogMaxHeightPx: 780,\n dialogRadiusPx: 14,\n /** The gap a centred dialog keeps from the viewport edge. */\n dialogMarginPx: 32,\n sheetRadiusPx: 16,\n headGapPx: 10,\n closeButtonPx: 36,\n controlRadiusPx: 8,\n bodyPaddingPx: 16,\n bodyGapPx: 12,\n listMaxHeightPx: 380,\n listGapPx: 2,\n rowPaddingPx: 10,\n rowMinHeightPx: 44,\n /** Left padding per level of `indent` (`.pitem.ind{padding-left:28px}` at depth 1). */\n rowIndentStepPx: 28,\n checkGlyphPx: 16,\n closeGlyphPx: 18,\n searchGlyphPx: 16,\n /** The magnifier's inset from the field's inner edge, and the gap before the text. */\n searchGlyphInsetPx: 10,\n searchGlyphGapPx: 6,\n /** The prototype's body line-height; the sheet sets it rather than inheriting a host's. */\n lineHeight: 1.4,\n} as const;\n\n/** The panel's shadow: `0 16px 48px` of ink at 0.28. */\nconst panelShadow = (theme: Theme): string =>\n `${rems(theme, 0, 16, 48)} ${alpha(uiInk(theme).panelShadowInk, 0.28)}`;\n\n/** The centred dialog's paper, on a pointer. */\nexport const dialogPaperSx = (theme: Theme): CSSObject => ({\n width: rem(theme, METRICS.dialogWidthPx),\n maxWidth: `calc(100% - ${rem(theme, METRICS.dialogMarginPx)})`,\n maxHeight: `min(${rem(theme, METRICS.dialogMaxHeightPx)}, calc(100% - ${rem(theme, METRICS.dialogMarginPx * 2)}))`,\n margin: 0,\n borderRadius: rem(theme, METRICS.dialogRadiusPx),\n boxShadow: panelShadow(theme),\n background: theme.palette.background.paper,\n backgroundImage: 'none',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n});\n\n/** The bottom sheet's paper, on a phone: full width, no grab handle. */\nexport const sheetPaperSx = (theme: Theme): CSSObject => ({\n width: '100%',\n maxHeight: '88%',\n borderRadius: rems(theme, METRICS.sheetRadiusPx, METRICS.sheetRadiusPx, 0, 0),\n boxShadow: panelShadow(theme),\n background: theme.palette.background.paper,\n backgroundImage: 'none',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n});\n\n/** `.sh` — kicker and title on the left, the close button on the right. */\nexport const headSx = (theme: Theme): CSSObject => ({\n display: 'flex',\n alignItems: 'center',\n gap: rem(theme, METRICS.headGapPx),\n padding: rems(theme, 14, 16),\n // A shade under the controls' own border: the rule separates, it does not frame.\n borderBottom: `1px solid ${alpha(theme.palette.divider, 0.6)}`,\n flex: '0 0 auto',\n});\n\nexport const kickerSx = (theme: Theme): CSSObject => ({\n fontSize: rem(theme, 11.5),\n color: theme.palette.text.secondary,\n lineHeight: METRICS.lineHeight,\n});\n\nexport const titleSx = (theme: Theme): CSSObject => ({\n margin: 0,\n fontSize: rem(theme, 14),\n fontWeight: 700,\n lineHeight: METRICS.lineHeight,\n color: theme.palette.text.primary,\n});\n\nexport const closeButtonSx = (theme: Theme): CSSObject => ({\n flex: '0 0 auto',\n width: rem(theme, METRICS.closeButtonPx),\n height: rem(theme, METRICS.closeButtonPx),\n display: 'grid',\n placeItems: 'center',\n padding: 0,\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: rem(theme, METRICS.controlRadiusPx),\n background: 'transparent',\n color: theme.palette.text.primary,\n cursor: 'pointer',\n '& svg': { width: rem(theme, METRICS.closeGlyphPx), height: rem(theme, METRICS.closeGlyphPx) },\n '&:hover': { background: theme.palette.action.hover },\n '&:focus-visible': { outline: `${rem(theme, 2)} solid ${theme.palette.primary.main}`, outlineOffset: rem(theme, 1) },\n});\n\n/** `.sb` — the scrolling body: search, list, foot. */\nexport const bodySx = (theme: Theme): CSSObject => ({\n display: 'flex',\n flexDirection: 'column',\n gap: rem(theme, METRICS.bodyGapPx),\n padding: rem(theme, METRICS.bodyPaddingPx),\n overflow: 'auto',\n minHeight: 0,\n flex: '1 1 auto',\n});\n\n/**\n * The search field: the library's field height, border and corner, with a\n * leading magnifier. White on the light sheet, so the field reads as a field\n * against the paper behind it.\n */\nexport const searchFieldSx = (theme: Theme): CSSObject => ({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n flex: '0 0 auto',\n '& > svg': {\n position: 'absolute',\n // + 1 for the input's own border, which the glyph sits inside of.\n left: rem(theme, METRICS.searchGlyphInsetPx + 1),\n width: rem(theme, METRICS.searchGlyphPx),\n height: rem(theme, METRICS.searchGlyphPx),\n color: theme.palette.text.secondary,\n pointerEvents: 'none',\n zIndex: 1,\n },\n '& input': {\n width: '100%',\n height: fieldHeight(theme),\n boxSizing: 'border-box',\n padding: rems(theme, 0, 10, 0, METRICS.searchGlyphInsetPx + METRICS.searchGlyphPx + METRICS.searchGlyphGapPx),\n fontSize: rem(theme, 14),\n border: fieldBorder(theme),\n borderRadius: fieldRadiusPx(theme),\n background: fieldWell(theme),\n color: theme.palette.text.primary,\n '&:focus': {\n outline: 'none',\n borderColor: theme.palette.primary.main,\n boxShadow: `0 0 0 ${rem(theme, 3)} ${alpha(theme.palette.primary.main, 0.15)}`,\n },\n },\n});\n\n/** The list: a column, scrolling on its own past 380px. */\nexport const listSx = (theme: Theme): CSSObject => ({\n display: 'flex',\n flexDirection: 'column',\n gap: rem(theme, METRICS.listGapPx),\n maxHeight: rem(theme, METRICS.listMaxHeightPx),\n overflow: 'auto',\n overscrollBehavior: 'contain',\n margin: 0,\n padding: 0,\n listStyle: 'none',\n flex: '0 1 auto',\n});\n\n/** Left padding for a row at `depth` — the row's own 10px at depth 0, 28px per level below it. */\nexport const rowPaddingLeft = (theme: Theme, depth: number): string =>\n rem(theme, depth > 0 ? METRICS.rowIndentStepPx * depth : METRICS.rowPaddingPx);\n\n/** Background of a row: selected wins over the keyboard cursor, which wins over rest. */\nfunction rowBackground(theme: Theme, selected: boolean, active: boolean): string {\n if (selected) return alpha(theme.palette.primary.main, 0.1);\n return active ? theme.palette.action.hover : 'transparent';\n}\n\n/** `.pitem` — a row. `active` is the keyboard cursor, drawn as a ring so it reads apart from `selected`. */\nexport const rowSx = (theme: Theme, selected: boolean, active: boolean): CSSObject => ({\n display: 'flex',\n alignItems: 'center',\n gap: rem(theme, 10),\n padding: rem(theme, METRICS.rowPaddingPx),\n minHeight: rem(theme, METRICS.rowMinHeightPx),\n boxSizing: 'border-box',\n borderRadius: rem(theme, METRICS.controlRadiusPx),\n textAlign: 'left',\n cursor: 'pointer',\n userSelect: 'none',\n color: theme.palette.text.primary,\n background: rowBackground(theme, selected, active),\n boxShadow: active ? `inset 0 0 0 ${rem(theme, 1)} ${alpha(theme.palette.primary.main, 0.3)}` : 'none',\n '&:hover': { background: selected ? alpha(theme.palette.primary.main, 0.1) : theme.palette.action.hover },\n});\n\nexport const rowTextSx: CSSObject = { flex: '1 1 auto', minWidth: 0 };\n\nexport const rowLabelSx = (theme: Theme): CSSObject => ({\n display: 'block',\n fontSize: rem(theme, 14),\n lineHeight: METRICS.lineHeight,\n});\n\nexport const rowMetaSx = (theme: Theme): CSSObject => ({\n display: 'block',\n fontSize: rem(theme, 12),\n lineHeight: METRICS.lineHeight,\n color: theme.palette.text.secondary,\n});\n\nexport const checkSx = (theme: Theme): CSSObject => ({\n flex: '0 0 auto',\n display: 'grid',\n color: theme.palette.primary.main,\n '& svg': { width: rem(theme, METRICS.checkGlyphPx), height: rem(theme, METRICS.checkGlyphPx) },\n});\n\n/** The create row's text: the accent, at 600. */\nexport const createLabelSx = (theme: Theme): CSSObject => ({\n ...rowLabelSx(theme),\n color: theme.palette.primary.main,\n fontWeight: 600,\n});\n\nexport const emptySx = (theme: Theme): CSSObject => ({\n fontSize: rem(theme, 12),\n lineHeight: METRICS.lineHeight,\n color: theme.palette.text.secondary,\n padding: rem(theme, 10),\n});\n\nexport const footSx = (theme: Theme): CSSObject => ({\n margin: 0,\n fontSize: rem(theme, 12),\n lineHeight: METRICS.lineHeight,\n color: theme.palette.text.secondary,\n});\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/PickerSheet/usePickerSheet.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { pickerSheetView, stepActiveIndex, type PickerSheetView } from './picker-sheet-model';\nimport type { PickerSheetProps } from './PickerSheet.types';\n\n/** Everything the sheet's parts read: the query, the derived list, the cursor and the handlers. */\ninterface PickerSheetState {\n query: string;\n view: PickerSheetView;\n /** The keyboard cursor, an index into `view` (items, then the create row); -1 for none. Always a row that exists. */\n activeIndex: number;\n /** Draw the cursor: only once an arrow key has moved it, never from typing alone. */\n cursorShown: boolean;\n searchInputRef: React.RefObject<HTMLInputElement | null>;\n listRef: React.RefObject<HTMLUListElement | null>;\n setQuery: (next: string) => void;\n /** Pick the row at `index` — an item, or the create row when it is the last one. */\n choose: (index: number) => void;\n onSearchKeyDown: (event: React.KeyboardEvent) => void;\n}\n\ntype HookProps = Pick<PickerSheetProps, 'open' | 'items' | 'onPick' | 'onCreate' | 'createLabel'>;\n\n/**\n * Bring the selected row into view on open, and keep the keyboard cursor in view.\n *\n * Nothing inside is focused on open — the modal focuses its own paper. Focusing\n * the search box raised a phone's keyboard over the list it was opened to show.\n * Deferred a frame: the surface mounts its content in the same commit as this\n * effect.\n */\nfunction useSheetScroll(\n open: boolean,\n activeIndex: number,\n listRef: React.RefObject<HTMLUListElement | null>,\n): void {\n useEffect(() => {\n if (!open) return undefined;\n const frame = requestAnimationFrame(() => {\n listRef.current?.querySelector('[aria-selected=\"true\"]')?.scrollIntoView?.({ block: 'nearest' });\n });\n return () => cancelAnimationFrame(frame);\n }, [open, listRef]);\n\n useEffect(() => {\n if (activeIndex < 0) return;\n const rows = listRef.current?.querySelectorAll('[role=\"option\"]');\n rows?.[activeIndex]?.scrollIntoView?.({ block: 'nearest' });\n }, [activeIndex, listRef]);\n}\n\n/**\n * The sheet's state. The query resets every time the sheet opens; typing puts\n * the cursor on the first match, so \"type, then Enter\" picks it (or creates,\n * when the create row is all there is).\n */\nexport function usePickerSheet({ open, items, onPick, onCreate, createLabel }: HookProps): PickerSheetState {\n const [query, setRawQuery] = useState('');\n const [rawActiveIndex, setActiveIndex] = useState(-1);\n const [cursorShown, setCursorShown] = useState(false);\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n const listRef = useRef<HTMLUListElement | null>(null);\n\n // Reset while rendering the frame that opens the sheet, not in an effect\n // after it — an effect would paint the last session's query for one frame.\n const [wasOpen, setWasOpen] = useState(open);\n if (open !== wasOpen) {\n setWasOpen(open);\n if (open) {\n setRawQuery('');\n setActiveIndex(-1);\n setCursorShown(false);\n }\n }\n\n const creatable = Boolean(onCreate && createLabel);\n const view = useMemo(() => pickerSheetView(items, query, creatable), [items, query, creatable]);\n // Clamped on read: the rows can shrink under the cursor (a query, new items),\n // and `aria-activedescendant` must never name a row that is not there.\n const activeIndex = rawActiveIndex < view.rowCount ? rawActiveIndex : -1;\n\n useSheetScroll(open, activeIndex, listRef);\n\n const setQuery = useCallback((next: string) => {\n setRawQuery(next);\n setActiveIndex(next.trim() ? 0 : -1);\n }, []);\n\n const choose = useCallback(\n (index: number) => {\n const item = view.items[index];\n if (item) {\n onPick(item.id);\n return;\n }\n if (view.canCreate && index === view.items.length) onCreate?.(view.query);\n },\n [view, onPick, onCreate],\n );\n\n const onSearchKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n const delta = event.key === 'ArrowDown' ? 1 : -1;\n setActiveIndex(stepActiveIndex(activeIndex, delta, view.rowCount));\n setCursorShown(true);\n return;\n }\n if (event.key === 'Enter' && activeIndex >= 0) {\n event.preventDefault();\n choose(activeIndex);\n }\n // Escape is the modal's: it closes through `onClose`, once.\n },\n [view.rowCount, activeIndex, choose],\n );\n\n return { query, view, activeIndex, cursorShown, searchInputRef, listRef, setQuery, choose, onSearchKeyDown };\n}\n"],"mappings":";;;;;;AAEA,SAAS,aAAa,WAAW,SAAS,QAAQ,gBAAgB;AA+BlE,SAAS,eACP,MACA,aACA,SACM;AACN,YAAU,MAAM;AACd,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,QAAQ,sBAAsB,MAAM;AACxC,cAAQ,SAAS,cAAc,wBAAwB,GAAG,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAAA,IACjG,CAAC;AACD,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACzC,GAAG,CAAC,MAAM,OAAO,CAAC;AAElB,YAAU,MAAM;AACd,QAAI,cAAc,EAAG;AACrB,UAAM,OAAO,QAAQ,SAAS,iBAAiB,iBAAiB;AAChE,WAAO,WAAW,GAAG,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAAA,EAC5D,GAAG,CAAC,aAAa,OAAO,CAAC;AAC3B;AAOO,SAAS,eAAe,EAAE,MAAM,OAAO,QAAQ,UAAU,YAAY,GAAgC;AAC1G,QAAM,CAAC,OAAO,WAAW,IAAI,SAAS,EAAE;AACxC,QAAM,CAAC,gBAAgB,cAAc,IAAI,SAAS,EAAE;AACpD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,iBAAiB,OAAgC,IAAI;AAC3D,QAAM,UAAU,OAAgC,IAAI;AAIpD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,MAAI,SAAS,SAAS;AACpB,eAAW,IAAI;AACf,QAAI,MAAM;AACR,kBAAY,EAAE;AACd,qBAAe,EAAE;AACjB,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,YAAY,QAAQ,YAAY,WAAW;AACjD,QAAM,OAAO,QAAQ,MAAM,gBAAgB,OAAO,OAAO,SAAS,GAAG,CAAC,OAAO,OAAO,SAAS,CAAC;AAG9F,QAAM,cAAc,iBAAiB,KAAK,WAAW,iBAAiB;AAEtE,iBAAe,MAAM,aAAa,OAAO;AAEzC,QAAM,WAAW,YAAY,CAAC,SAAiB;AAC7C,gBAAY,IAAI;AAChB,mBAAe,KAAK,KAAK,IAAI,IAAI,EAAE;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,QAAM,SAAS;AAAA,IACb,CAAC,UAAkB;AACjB,YAAM,OAAO,KAAK,MAAM,KAAK;AAC7B,UAAI,MAAM;AACR,eAAO,KAAK,EAAE;AACd;AAAA,MACF;AACA,UAAI,KAAK,aAAa,UAAU,KAAK,MAAM,OAAQ,YAAW,KAAK,KAAK;AAAA,IAC1E;AAAA,IACA,CAAC,MAAM,QAAQ,QAAQ;AAAA,EACzB;AAEA,QAAM,kBAAkB;AAAA,IACtB,CAAC,UAA+B;AAC9B,UAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,WAAW;AACxD,cAAM,eAAe;AACrB,cAAM,QAAQ,MAAM,QAAQ,cAAc,IAAI;AAC9C,uBAAe,gBAAgB,aAAa,OAAO,KAAK,QAAQ,CAAC;AACjE,uBAAe,IAAI;AACnB;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,WAAW,eAAe,GAAG;AAC7C,cAAM,eAAe;AACrB,eAAO,WAAW;AAAA,MACpB;AAAA,IAEF;AAAA,IACA,CAAC,KAAK,UAAU,aAAa,MAAM;AAAA,EACrC;AAEA,SAAO,EAAE,OAAO,MAAM,aAAa,aAAa,gBAAgB,SAAS,UAAU,QAAQ,gBAAgB;AAC7G;","names":[]}
|
|
File without changes
|
|
File without changes
|