@charcuterie/ui 4.24.0 → 4.26.0
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/README.md +13 -0
- package/dist/CommandPalette/CommandPalette.d.ts +43 -0
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -0
- package/dist/CommandPalette/CommandPalette.js +53 -0
- package/dist/CommandPalette/CommandPalette.js.map +1 -0
- package/dist/CommandPalette/CommandPaletteFooter.d.ts +16 -0
- package/dist/CommandPalette/CommandPaletteFooter.d.ts.map +1 -0
- package/dist/CommandPalette/CommandPaletteFooter.js +14 -0
- package/dist/CommandPalette/CommandPaletteFooter.js.map +1 -0
- package/dist/CommandPalette/CommandPaletteOption.d.ts +44 -0
- package/dist/CommandPalette/CommandPaletteOption.d.ts.map +1 -0
- package/dist/CommandPalette/CommandPaletteOption.js +72 -0
- package/dist/CommandPalette/CommandPaletteOption.js.map +1 -0
- package/dist/CommandPalette/CommandPalettePanel.d.ts +52 -0
- package/dist/CommandPalette/CommandPalettePanel.d.ts.map +1 -0
- package/dist/CommandPalette/CommandPalettePanel.js +168 -0
- package/dist/CommandPalette/CommandPalettePanel.js.map +1 -0
- package/dist/CommandPalette/commandPaletteSections.d.ts +93 -0
- package/dist/CommandPalette/commandPaletteSections.d.ts.map +1 -0
- package/dist/CommandPalette/commandPaletteSections.js +94 -0
- package/dist/CommandPalette/commandPaletteSections.js.map +1 -0
- package/dist/CommandPalette/useCommandPalette.d.ts +32 -0
- package/dist/CommandPalette/useCommandPalette.d.ts.map +1 -0
- package/dist/CommandPalette/useCommandPalette.js +40 -0
- package/dist/CommandPalette/useCommandPalette.js.map +1 -0
- package/dist/CommandPalette/useCommandPaletteHotkeys.d.ts +35 -0
- package/dist/CommandPalette/useCommandPaletteHotkeys.d.ts.map +1 -0
- package/dist/CommandPalette/useCommandPaletteHotkeys.js +114 -0
- package/dist/CommandPalette/useCommandPaletteHotkeys.js.map +1 -0
- package/dist/GroupedList/GroupedList.d.ts +78 -1
- package/dist/GroupedList/GroupedList.d.ts.map +1 -1
- package/dist/GroupedList/GroupedList.js +14 -0
- package/dist/GroupedList/GroupedList.js.map +1 -1
- package/dist/GroupedList/GroupedListRow.d.ts +17 -6
- package/dist/GroupedList/GroupedListRow.d.ts.map +1 -1
- package/dist/GroupedList/GroupedListRow.js +75 -8
- package/dist/GroupedList/GroupedListRow.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +36 -4
- package/package.json +2 -2
- package/src/CommandPalette/CommandPalette.mdx +129 -0
- package/src/CommandPalette/CommandPalette.stories.tsx +413 -0
- package/src/CommandPalette/CommandPalette.test.tsx +553 -0
- package/src/CommandPalette/CommandPalette.tsx +91 -0
- package/src/CommandPalette/CommandPaletteFooter.tsx +45 -0
- package/src/CommandPalette/CommandPaletteOption.tsx +200 -0
- package/src/CommandPalette/CommandPalettePanel.tsx +411 -0
- package/src/CommandPalette/commandPalette.storyHelpers.tsx +248 -0
- package/src/CommandPalette/commandPaletteSections.test.ts +156 -0
- package/src/CommandPalette/commandPaletteSections.ts +219 -0
- package/src/CommandPalette/useCommandPalette.ts +75 -0
- package/src/CommandPalette/useCommandPaletteHotkeys.ts +158 -0
- package/src/GroupedList/GroupedList.mdx +42 -1
- package/src/GroupedList/GroupedList.stories.tsx +252 -2
- package/src/GroupedList/GroupedList.test.tsx +391 -0
- package/src/GroupedList/GroupedList.tsx +78 -1
- package/src/GroupedList/GroupedListRow.tsx +135 -10
- package/src/index.ts +14 -0
- package/src/sourceRules.test.ts +2 -1
- package/src/styles.css +36 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandPaletteSections.d.ts","sourceRoot":"","sources":["../../src/CommandPalette/commandPaletteSections.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gGAAgG;IAChG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,yGAAyG;IACzG,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAA;IACX,4EAA4E;IAC5E,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,+DAA+D;AAC/D,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAA;IACb,gDAAgD;IAChD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,kBAAkB,CAAA;IACxB,iEAAiE;IACjE,WAAW,EAAE,SAAS,cAAc,EAAE,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,SAAS,iBAAiB,EAAE,CAAA;CACnC,CAAA;AAED,MAAM,MAAM,6BAA6B,GAAG;IAC1C,iBAAiB,EAAE,CACjB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,KACV,MAAM,CAAA;IACX,eAAe,CAAC,EAAE,CAChB,KAAK,EAAE,MAAM,KACV,kBAAkB,GAAG,IAAI,GAAG,SAAS,CAAA;IAC1C,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACpC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAA;IAC3C,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,2BAA2B,GAAI,oIAUzC,6BAA6B,KAAG,qBAAqB,EAqGvD,CAAA"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { parsePrefixCommand, rankWordPrefixMatches, } from "@charcuterie/logic";
|
|
2
|
+
/**
|
|
3
|
+
* What the palette lists for a query, as headed sections in order.
|
|
4
|
+
*
|
|
5
|
+
* Three cases, decided in this order:
|
|
6
|
+
*
|
|
7
|
+
* 1. **A prefix command** (`au lego`) lists one row, for that target.
|
|
8
|
+
* 2. **Nothing typed** lists the caller's `suggestions` — the shape
|
|
9
|
+
* the palette exists for: something to choose the moment it opens.
|
|
10
|
+
* 3. **Anything else** lists the word-prefix matches from `items`,
|
|
11
|
+
* under each item's `group`, and then the caller's fallback row.
|
|
12
|
+
*
|
|
13
|
+
* Matched groups are ordered by their best member, so the first row
|
|
14
|
+
* — the one Enter opens — is always the best match, whichever group
|
|
15
|
+
* it belongs to.
|
|
16
|
+
*/
|
|
17
|
+
export const buildCommandPaletteSections = ({ formatPrefixTitle, getFallbackItem, items, maxResultsPerGroup, prefixGroupLabel, prefixes, query, suggestions, ungroupedLabel, }) => {
|
|
18
|
+
const command = parsePrefixCommand(query, prefixes);
|
|
19
|
+
if (command !== null) {
|
|
20
|
+
const { query: commandQuery, target } = command;
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
key: "prefix",
|
|
24
|
+
label: prefixGroupLabel,
|
|
25
|
+
rows: [
|
|
26
|
+
{
|
|
27
|
+
item: {
|
|
28
|
+
href: target.getHref?.(commandQuery),
|
|
29
|
+
icon: target.icon,
|
|
30
|
+
key: `prefix-${target.prefix}`,
|
|
31
|
+
onSelect: target.onSelect === undefined
|
|
32
|
+
? undefined
|
|
33
|
+
: () => target.onSelect?.(commandQuery),
|
|
34
|
+
title: formatPrefixTitle(target.label, commandQuery),
|
|
35
|
+
},
|
|
36
|
+
titleRanges: [],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
const trimmedQuery = query.trim();
|
|
43
|
+
if (trimmedQuery.length === 0) {
|
|
44
|
+
return suggestions
|
|
45
|
+
.filter((group) => group.items.length > 0)
|
|
46
|
+
.map((group) => ({
|
|
47
|
+
key: `suggestion-${group.key}`,
|
|
48
|
+
label: group.label,
|
|
49
|
+
rows: group.items.map((item) => ({
|
|
50
|
+
item,
|
|
51
|
+
titleRanges: [],
|
|
52
|
+
})),
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
const ranked = rankWordPrefixMatches(items, trimmedQuery, (item) => ({
|
|
56
|
+
extra: [
|
|
57
|
+
...(item.description === undefined
|
|
58
|
+
? []
|
|
59
|
+
: [item.description]),
|
|
60
|
+
...(item.keywords ?? []),
|
|
61
|
+
],
|
|
62
|
+
title: item.title,
|
|
63
|
+
weight: item.weight,
|
|
64
|
+
}));
|
|
65
|
+
const sectionsByLabel = new Map();
|
|
66
|
+
for (const { item, match } of ranked) {
|
|
67
|
+
const label = item.group ?? ungroupedLabel;
|
|
68
|
+
const rows = sectionsByLabel.get(label) ?? [];
|
|
69
|
+
if (maxResultsPerGroup === undefined ||
|
|
70
|
+
rows.length < maxResultsPerGroup) {
|
|
71
|
+
rows.push({ item, titleRanges: match.titleRanges });
|
|
72
|
+
}
|
|
73
|
+
sectionsByLabel.set(label, rows);
|
|
74
|
+
}
|
|
75
|
+
const sections = [
|
|
76
|
+
...sectionsByLabel,
|
|
77
|
+
].map(([label, rows]) => ({
|
|
78
|
+
key: `group-${label}`,
|
|
79
|
+
label,
|
|
80
|
+
rows,
|
|
81
|
+
}));
|
|
82
|
+
const fallbackItem = getFallbackItem?.(trimmedQuery);
|
|
83
|
+
return fallbackItem == null
|
|
84
|
+
? sections
|
|
85
|
+
: [
|
|
86
|
+
...sections,
|
|
87
|
+
{
|
|
88
|
+
key: "fallback",
|
|
89
|
+
label: fallbackItem.group ?? ungroupedLabel,
|
|
90
|
+
rows: [{ item: fallbackItem, titleRanges: [] }],
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=commandPaletteSections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandPaletteSections.js","sourceRoot":"","sources":["../../src/CommandPalette/commandPaletteSections.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type CommandPaletteHotkeysOptions } from "./useCommandPaletteHotkeys.ts";
|
|
2
|
+
export type UseCommandPaletteOptions = Pick<CommandPaletteHotkeysOptions, "isOpenedByTyping"> & {
|
|
3
|
+
/** Register the global keys (`useCommandPaletteHotkeys`). Default `true`. */
|
|
4
|
+
hasHotkeys?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type UseCommandPaletteResult = {
|
|
7
|
+
close: () => void;
|
|
8
|
+
isVisible: boolean;
|
|
9
|
+
/** Open it, optionally with text already in the field. */
|
|
10
|
+
open: (initialQuery?: string) => void;
|
|
11
|
+
/** Spread onto `CommandPalette`. */
|
|
12
|
+
paletteProps: {
|
|
13
|
+
defaultQuery: string;
|
|
14
|
+
isVisible: boolean;
|
|
15
|
+
onClose: () => void;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The open state for one `CommandPalette`, and its global keys.
|
|
20
|
+
*
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const palette = useCommandPalette()
|
|
23
|
+
*
|
|
24
|
+
* <Button onClick={() => palette.open()}>Search</Button>
|
|
25
|
+
* <CommandPalette {...palette.paletteProps} items={items} />
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* The keys are off while it is open, so a second `Ctrl K` does not
|
|
29
|
+
* reset what has been typed.
|
|
30
|
+
*/
|
|
31
|
+
export declare const useCommandPalette: ({ hasHotkeys, isOpenedByTyping, }?: UseCommandPaletteOptions) => UseCommandPaletteResult;
|
|
32
|
+
//# sourceMappingURL=useCommandPalette.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCommandPalette.d.ts","sourceRoot":"","sources":["../../src/CommandPalette/useCommandPalette.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,4BAA4B,EAElC,MAAM,+BAA+B,CAAA;AAEtC,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,4BAA4B,EAC5B,kBAAkB,CACnB,GAAG;IACF,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,0DAA0D;IAC1D,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,oCAAoC;IACpC,YAAY,EAAE;QACZ,YAAY,EAAE,MAAM,CAAA;QACpB,SAAS,EAAE,OAAO,CAAA;QAClB,OAAO,EAAE,MAAM,IAAI,CAAA;KACpB,CAAA;CACF,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAAI,oCAG/B,wBAA6B,KAAG,uBA6BlC,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useVisibility } from "@charcuterie/logic";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
import { useCommandPaletteHotkeys, } from "./useCommandPaletteHotkeys.js";
|
|
4
|
+
/**
|
|
5
|
+
* The open state for one `CommandPalette`, and its global keys.
|
|
6
|
+
*
|
|
7
|
+
* ```tsx
|
|
8
|
+
* const palette = useCommandPalette()
|
|
9
|
+
*
|
|
10
|
+
* <Button onClick={() => palette.open()}>Search</Button>
|
|
11
|
+
* <CommandPalette {...palette.paletteProps} items={items} />
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The keys are off while it is open, so a second `Ctrl K` does not
|
|
15
|
+
* reset what has been typed.
|
|
16
|
+
*/
|
|
17
|
+
export const useCommandPalette = ({ hasHotkeys = true, isOpenedByTyping, } = {}) => {
|
|
18
|
+
const { hide, isVisible, show } = useVisibility();
|
|
19
|
+
const [initialQuery, setInitialQuery] = useState("");
|
|
20
|
+
const open = useCallback((nextInitialQuery = "") => {
|
|
21
|
+
setInitialQuery(nextInitialQuery);
|
|
22
|
+
show();
|
|
23
|
+
}, [show]);
|
|
24
|
+
useCommandPaletteHotkeys({
|
|
25
|
+
isEnabled: hasHotkeys && !isVisible,
|
|
26
|
+
isOpenedByTyping,
|
|
27
|
+
onOpen: open,
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
close: hide,
|
|
31
|
+
isVisible,
|
|
32
|
+
open,
|
|
33
|
+
paletteProps: {
|
|
34
|
+
defaultQuery: initialQuery,
|
|
35
|
+
isVisible,
|
|
36
|
+
onClose: hide,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=useCommandPalette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCommandPalette.js","sourceRoot":"","sources":["../../src/CommandPalette/useCommandPalette.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE7C,OAAO,EAEL,wBAAwB,GACzB,MAAM,+BAA+B,CAAA;AAuBtC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,EAChC,UAAU,GAAG,IAAI,EACjB,gBAAgB,MACY,EAAE,EAA2B,EAAE;IAC3D,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,aAAa,EAAE,CAAA;IAEjD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IAEpD,MAAM,IAAI,GAAG,WAAW,CACtB,CAAC,gBAAgB,GAAG,EAAE,EAAE,EAAE;QACxB,eAAe,CAAC,gBAAgB,CAAC,CAAA;QACjC,IAAI,EAAE,CAAA;IACR,CAAC,EACD,CAAC,IAAI,CAAC,CACP,CAAA;IAED,wBAAwB,CAAC;QACvB,SAAS,EAAE,UAAU,IAAI,CAAC,SAAS;QACnC,gBAAgB;QAChB,MAAM,EAAE,IAAI;KACb,CAAC,CAAA;IAEF,OAAO;QACL,KAAK,EAAE,IAAI;QACX,SAAS;QACT,IAAI;QACJ,YAAY,EAAE;YACZ,YAAY,EAAE,YAAY;YAC1B,SAAS;YACT,OAAO,EAAE,IAAI;SACd;KACF,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type CommandPaletteHotkeysOptions = {
|
|
2
|
+
/** Off while the palette is open, or on a page that should not take the keys. Default `true`. */
|
|
3
|
+
isEnabled?: boolean;
|
|
4
|
+
/**
|
|
5
|
+
* A printable key typed outside an editable field opens the palette
|
|
6
|
+
* with that character as the query. Default `true`.
|
|
7
|
+
*/
|
|
8
|
+
isOpenedByTyping?: boolean;
|
|
9
|
+
/** Receives the text the palette should open with: `""`, or the character typed. */
|
|
10
|
+
onOpen: (initialQuery: string) => void;
|
|
11
|
+
};
|
|
12
|
+
export declare const getIsTypingTarget: (target: EventTarget | null) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The global keys that open a command palette, as an opt-in hook.
|
|
15
|
+
*
|
|
16
|
+
* - **`Ctrl K`, `Ctrl Space`** (or `⌘` on a Mac) open it from
|
|
17
|
+
* anywhere, a text field included — the chord types nothing.
|
|
18
|
+
* - **`/`** opens it when focus is not somewhere that types, and
|
|
19
|
+
* the slash itself is not typed.
|
|
20
|
+
* - **Any printable key** does the same and becomes the query, so
|
|
21
|
+
* the page is a search box without a click (`isOpenedByTyping`).
|
|
22
|
+
*
|
|
23
|
+
* It is a hook, not something `CommandPalette` does on mount,
|
|
24
|
+
* because a document-wide key listener is a page-level decision: an
|
|
25
|
+
* app with two palettes, or a page where letters already mean
|
|
26
|
+
* something (a board with its own shortcuts), must be able to say
|
|
27
|
+
* no. See
|
|
28
|
+
* `docs/decisions/2026-10-16-command-palette-hotkeys-are-an-opt-in-hook.md`.
|
|
29
|
+
*
|
|
30
|
+
* A key another handler already claimed (`defaultPrevented`), a key
|
|
31
|
+
* pressed during IME composition, and a key with `Alt` held are all
|
|
32
|
+
* left alone.
|
|
33
|
+
*/
|
|
34
|
+
export declare const useCommandPaletteHotkeys: ({ isEnabled, isOpenedByTyping, onOpen, }: CommandPaletteHotkeysOptions) => void;
|
|
35
|
+
//# sourceMappingURL=useCommandPaletteHotkeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCommandPaletteHotkeys.d.ts","sourceRoot":"","sources":["../../src/CommandPalette/useCommandPaletteHotkeys.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,4BAA4B,GAAG;IACzC,iGAAiG;IACjG,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,oFAAoF;IACpF,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC,CAAA;AAoCD,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,WAAW,GAAG,IAAI,KACzB,OAiBF,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,wBAAwB,GAAI,0CAItC,4BAA4B,KAAG,IA8DjC,CAAA"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { useLatestRef } from "@charcuterie/logic";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Elements that keep their keys: anything you type into, and the
|
|
5
|
+
* widgets with their own type-to-find (a focused `Listbox` or `Menu`
|
|
6
|
+
* jumps to the option you type, and stealing that key breaks it).
|
|
7
|
+
*/
|
|
8
|
+
const TYPEAHEAD_OWNER_SELECTOR = [
|
|
9
|
+
"input",
|
|
10
|
+
"textarea",
|
|
11
|
+
"select",
|
|
12
|
+
"[contenteditable]:not([contenteditable='false'])",
|
|
13
|
+
"[role='combobox']",
|
|
14
|
+
"[role='grid']",
|
|
15
|
+
"[role='listbox']",
|
|
16
|
+
"[role='menu']",
|
|
17
|
+
"[role='menubar']",
|
|
18
|
+
"[role='searchbox']",
|
|
19
|
+
"[role='textbox']",
|
|
20
|
+
"[role='tree']",
|
|
21
|
+
"[role='treegrid']",
|
|
22
|
+
].join(",");
|
|
23
|
+
/** Input types that take no text, so a letter typed on one is free to open the palette. */
|
|
24
|
+
const NON_TEXT_INPUT_TYPES = new Set([
|
|
25
|
+
"button",
|
|
26
|
+
"checkbox",
|
|
27
|
+
"color",
|
|
28
|
+
"file",
|
|
29
|
+
"image",
|
|
30
|
+
"radio",
|
|
31
|
+
"range",
|
|
32
|
+
"reset",
|
|
33
|
+
"submit",
|
|
34
|
+
]);
|
|
35
|
+
export const getIsTypingTarget = (target) => {
|
|
36
|
+
if (!(target instanceof Element)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (target instanceof HTMLInputElement &&
|
|
40
|
+
NON_TEXT_INPUT_TYPES.has(target.type)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return ((target instanceof HTMLElement &&
|
|
44
|
+
target.isContentEditable) ||
|
|
45
|
+
target.closest(TYPEAHEAD_OWNER_SELECTOR) !== null);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The global keys that open a command palette, as an opt-in hook.
|
|
49
|
+
*
|
|
50
|
+
* - **`Ctrl K`, `Ctrl Space`** (or `⌘` on a Mac) open it from
|
|
51
|
+
* anywhere, a text field included — the chord types nothing.
|
|
52
|
+
* - **`/`** opens it when focus is not somewhere that types, and
|
|
53
|
+
* the slash itself is not typed.
|
|
54
|
+
* - **Any printable key** does the same and becomes the query, so
|
|
55
|
+
* the page is a search box without a click (`isOpenedByTyping`).
|
|
56
|
+
*
|
|
57
|
+
* It is a hook, not something `CommandPalette` does on mount,
|
|
58
|
+
* because a document-wide key listener is a page-level decision: an
|
|
59
|
+
* app with two palettes, or a page where letters already mean
|
|
60
|
+
* something (a board with its own shortcuts), must be able to say
|
|
61
|
+
* no. See
|
|
62
|
+
* `docs/decisions/2026-10-16-command-palette-hotkeys-are-an-opt-in-hook.md`.
|
|
63
|
+
*
|
|
64
|
+
* A key another handler already claimed (`defaultPrevented`), a key
|
|
65
|
+
* pressed during IME composition, and a key with `Alt` held are all
|
|
66
|
+
* left alone.
|
|
67
|
+
*/
|
|
68
|
+
export const useCommandPaletteHotkeys = ({ isEnabled = true, isOpenedByTyping = true, onOpen, }) => {
|
|
69
|
+
const onOpenRef = useLatestRef(onOpen);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (!isEnabled) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const handleKeyDown = (event) => {
|
|
75
|
+
if (event.defaultPrevented ||
|
|
76
|
+
event.isComposing ||
|
|
77
|
+
event.altKey) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const isChord = event.ctrlKey || event.metaKey;
|
|
81
|
+
if (isChord) {
|
|
82
|
+
if (!event.shiftKey &&
|
|
83
|
+
(event.key.toLowerCase() === "k" ||
|
|
84
|
+
event.key === " ")) {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
onOpenRef.current("");
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (getIsTypingTarget(event.target)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (event.key === "/") {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
onOpenRef.current("");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// `key` is the character for a printable key and a name
|
|
99
|
+
// ("Enter", "ArrowDown") for everything else; a space is
|
|
100
|
+
// printable but presses the focused button.
|
|
101
|
+
if (isOpenedByTyping &&
|
|
102
|
+
[...event.key].length === 1 &&
|
|
103
|
+
/\S/u.test(event.key)) {
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
onOpenRef.current(event.key);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
109
|
+
return () => {
|
|
110
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
111
|
+
};
|
|
112
|
+
}, [isEnabled, isOpenedByTyping, onOpenRef]);
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=useCommandPaletteHotkeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCommandPaletteHotkeys.js","sourceRoot":"","sources":["../../src/CommandPalette/useCommandPaletteHotkeys.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IntentName } from "@charcuterie/tokens";
|
|
1
2
|
import type { ReactNode } from "react";
|
|
2
3
|
export type GroupedListHeadingLevel = 2 | 3 | 4 | 5 | 6;
|
|
3
4
|
export type GroupedListSize = "md" | "sm";
|
|
@@ -10,24 +11,86 @@ export type GroupedListItem = {
|
|
|
10
11
|
* **Name a repeated action after its row.** Ten rows of "Open" are
|
|
11
12
|
* ten buttons an agent cannot tell apart; `aria-label="Open Lobby"`
|
|
12
13
|
* on each is one `getByRole` away.
|
|
14
|
+
*
|
|
15
|
+
* In a row with `onSelect` these stay their own targets: they sit
|
|
16
|
+
* above the whole-row button, so pressing one never selects the
|
|
17
|
+
* row.
|
|
13
18
|
*/
|
|
14
19
|
actions?: ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* A figure that belongs beside the text rather than under it — a
|
|
22
|
+
* payout, a total, a schedule. It sits between the text and the
|
|
23
|
+
* actions in a wide list and drops under the text in a narrow one,
|
|
24
|
+
* the same content-decided wrap the actions use: it asks for at
|
|
25
|
+
* least 11rem beside the text column's 12rem. Never interactive:
|
|
26
|
+
* in a row with `onSelect` the whole-row button sits over it.
|
|
27
|
+
*/
|
|
28
|
+
aside?: ReactNode;
|
|
15
29
|
/** `Badge`s, drawn after the label and wrapping onto their own line when they run out of room. */
|
|
16
30
|
badges?: ReactNode;
|
|
17
31
|
/**
|
|
18
32
|
* Anything richer than one line of help — a payout, a rule list.
|
|
19
33
|
* Drawn under the description, inside the text column, and never
|
|
20
|
-
* truncated.
|
|
34
|
+
* truncated. Never interactive in a row with `onSelect`; a control
|
|
35
|
+
* belongs in `actions`.
|
|
21
36
|
*/
|
|
22
37
|
content?: ReactNode;
|
|
23
38
|
/** One muted line under the label: a path, a key, a count. Truncated like the label. */
|
|
24
39
|
description?: string;
|
|
40
|
+
/**
|
|
41
|
+
* The intent `isHighlighted` rings the row in. `accent` by default;
|
|
42
|
+
* an app whose highlight means "this just paid out" passes
|
|
43
|
+
* `success`.
|
|
44
|
+
*/
|
|
45
|
+
highlightIntent?: IntentName;
|
|
46
|
+
/**
|
|
47
|
+
* `true` rings the row and pulses the ring twice — "this one just
|
|
48
|
+
* changed", for a row a scan, a save or an arrival has touched.
|
|
49
|
+
* The ring stays while the flag does, so the app decides how long
|
|
50
|
+
* it lasts, usually by clearing it on a timer.
|
|
51
|
+
*
|
|
52
|
+
* **To pulse a row that is already highlighted again, change its
|
|
53
|
+
* `key`.** A new key is a new row, and a new row starts the pulse
|
|
54
|
+
* from the beginning; `` `${id}-${sequence}` `` is the usual shape.
|
|
55
|
+
*
|
|
56
|
+
* Under `prefers-reduced-motion` and on ePaper the ring does not
|
|
57
|
+
* pulse; it is simply there.
|
|
58
|
+
*/
|
|
59
|
+
isHighlighted?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* `true` draws the row receded — a sunken surface and a quieter
|
|
62
|
+
* label — for an entry that exists but is switched off. It stays
|
|
63
|
+
* fully operable and every colour on it still clears AA, which is
|
|
64
|
+
* why it is a surface and a text role rather than an opacity: a
|
|
65
|
+
* faded row is not a disabled one, and only a disabled control may
|
|
66
|
+
* drop below the contrast floor. Say WHY in a badge too ("off").
|
|
67
|
+
*/
|
|
68
|
+
isMuted?: boolean;
|
|
25
69
|
key: string;
|
|
26
70
|
/**
|
|
27
71
|
* Plain text, because it is what the row is called — the thing a
|
|
28
72
|
* search matched and the text a clipped row's tooltip repeats.
|
|
29
73
|
*/
|
|
30
74
|
label: string;
|
|
75
|
+
/**
|
|
76
|
+
* Makes the whole row one native `<button>` — "open this one" for a
|
|
77
|
+
* row whose primary action is the row itself. Hover tints the row
|
|
78
|
+
* and rings its edge, a press sinks it (with motion only when the
|
|
79
|
+
* user has not asked for less), and keyboard focus draws the focus
|
|
80
|
+
* ring inside the row's edge. `actions` stay separate targets
|
|
81
|
+
* above it.
|
|
82
|
+
*
|
|
83
|
+
* The button is drawn UNDER the row's text and the text lets the
|
|
84
|
+
* pointer through, so nothing interactive is ever nested inside a
|
|
85
|
+
* button: keep `badges`, `content` and `aside` free of controls.
|
|
86
|
+
*/
|
|
87
|
+
onSelect?: () => void;
|
|
88
|
+
/**
|
|
89
|
+
* The whole-row button's accessible name. Defaults to `label`.
|
|
90
|
+
* Name the verb when there is one — `"Edit Water the ferns"` —
|
|
91
|
+
* the same rule as a repeated action.
|
|
92
|
+
*/
|
|
93
|
+
selectLabel?: string;
|
|
31
94
|
};
|
|
32
95
|
export type GroupedListGroup = {
|
|
33
96
|
/** Plain text. It is the accessible name of the group's list, so `getByRole("list", { name })` finds it. */
|
|
@@ -121,6 +184,20 @@ export type GroupedListProps = {
|
|
|
121
184
|
* know that surface, so `isHeadingSticky` reads it from a custom
|
|
122
185
|
* property the host can set, falling back to `surface-base`.
|
|
123
186
|
*
|
|
187
|
+
* ### A row can be the target
|
|
188
|
+
*
|
|
189
|
+
* tally-marks' card list opens a card's editor from anywhere on the
|
|
190
|
+
* card, and the owner asked for that to feel like an app: hover,
|
|
191
|
+
* press, focus, touch. `onSelect` is that, as one real `<button>`
|
|
192
|
+
* stretched under the row's text — not a click handler on an
|
|
193
|
+
* `<li>`, which no keyboard reaches and no screen reader announces.
|
|
194
|
+
* The text lets the pointer through to it; the actions sit above it
|
|
195
|
+
* and keep their own targets, so "Delete" never opens the editor.
|
|
196
|
+
*
|
|
197
|
+
* A row can also be muted (switched off but still there) and
|
|
198
|
+
* highlighted (just touched by a scan or a save) — the two other
|
|
199
|
+
* per-row states the same list drew by hand.
|
|
200
|
+
*
|
|
124
201
|
* ### Not collapsible
|
|
125
202
|
*
|
|
126
203
|
* A group you can close is `Accordion`. This is for a list that is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupedList.d.ts","sourceRoot":"","sources":["../../src/GroupedList/GroupedList.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,MAAM,MAAM,uBAAuB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAEvD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,CAAA;AAEzC,MAAM,MAAM,eAAe,GAAG;IAC5B
|
|
1
|
+
{"version":3,"file":"GroupedList.d.ts","sourceRoot":"","sources":["../../src/GroupedList/GroupedList.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,MAAM,MAAM,uBAAuB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAEvD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,CAAA;AAEzC,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,kGAAkG;IAClG,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,eAAe,CAAC,EAAE,UAAU,CAAA;IAC5B;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,4GAA4G;IAC5G,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;IACjC,GAAG,EAAE,MAAM,CAAA;IACX;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,uBAAuB,CAAA;IACtC;;;;;;;;;;;;;;;;OAgBG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,eAAe,CAAA;CACvB,CAAA;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,eAAO,MAAM,WAAW,GAAI,0FAQzB,gBAAgB,KAAG,SAuErB,CAAA"}
|
|
@@ -52,6 +52,20 @@ const HEADING_TAG = {
|
|
|
52
52
|
* know that surface, so `isHeadingSticky` reads it from a custom
|
|
53
53
|
* property the host can set, falling back to `surface-base`.
|
|
54
54
|
*
|
|
55
|
+
* ### A row can be the target
|
|
56
|
+
*
|
|
57
|
+
* tally-marks' card list opens a card's editor from anywhere on the
|
|
58
|
+
* card, and the owner asked for that to feel like an app: hover,
|
|
59
|
+
* press, focus, touch. `onSelect` is that, as one real `<button>`
|
|
60
|
+
* stretched under the row's text — not a click handler on an
|
|
61
|
+
* `<li>`, which no keyboard reaches and no screen reader announces.
|
|
62
|
+
* The text lets the pointer through to it; the actions sit above it
|
|
63
|
+
* and keep their own targets, so "Delete" never opens the editor.
|
|
64
|
+
*
|
|
65
|
+
* A row can also be muted (switched off but still there) and
|
|
66
|
+
* highlighted (just touched by a scan or a save) — the two other
|
|
67
|
+
* per-row states the same list drew by hand.
|
|
68
|
+
*
|
|
55
69
|
* ### Not collapsible
|
|
56
70
|
*
|
|
57
71
|
* A group you can close is `Accordion`. This is for a list that is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupedList.js","sourceRoot":"","sources":["../../src/GroupedList/GroupedList.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"GroupedList.js","sourceRoot":"","sources":["../../src/GroupedList/GroupedList.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAIhD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAsB,CAAA;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AA0JpE;;;;GAIG;AACH,MAAM,WAAW,GAGb;IACF,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;CACR,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAC1B,SAAS,EACT,UAAU,EACV,MAAM,EACN,YAAY,GAAG,CAAC,EAChB,eAAe,GAAG,KAAK,EACvB,eAAe,GAAG,IAAI,EACtB,IAAI,GAAG,IAAI,GACM,EAAa,EAAE;IAChC,MAAM,MAAM,GAAG,WAAW,EAAE,CAAA;IAE5B,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAC/B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAClC,CAAA;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,YAAY,CAAC,CAAA;IAEzC,OAAO,CACL,cACE,SAAS,EAAE,WAAW,CACpB,eAAe,EACf,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EACjC,SAAS,CACV,YAEA,WAAW,CAAC,MAAM,KAAK,CAAC;YACvB,CAAC,CAAC,CAAC,UAAU,IAAI,IAAI,CAAC;YACtB,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBAC/B,MAAM,SAAS,GAAG,GAAG,MAAM,YAAY,KAAK,EAAE,CAAA;gBAE9C,OAAO,CACL,eACE,SAAS,EAAE,WAAW,CACpB,eAAe,EACf,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CACpC,aAGD,eACE,SAAS,EAAE,WAAW,CACpB,iDAAiD,EACjD,eAAe,IAAI,oBAAoB,CACxC,aAED,KAAC,OAAO,IACN,SAAS,EAAE,WAAW,CACpB,8DAA8D,EAC9D,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CACtC,EACD,EAAE,EAAE,SAAS,YAEZ,KAAK,CAAC,OAAO,GACN,EAET,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CACf,eAAM,SAAS,EAAC,sDAAsD,YACnE,KAAK,CAAC,OAAO,GACT,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,EAEN,gCACmB,SAAS,EAC1B,SAAS,EAAC,wIAAwI,YAEjJ,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACzB,KAAC,cAAc,IACb,eAAe,EAAE,eAAe,EAChC,IAAI,EAAE,IAAI,EAEV,IAAI,EAAE,IAAI,IADL,IAAI,CAAC,GAAG,CAEb,CACH,CAAC,GACC,KArCA,KAAK,CAAC,GAAG,CAsCV,CACP,CAAA;YACH,CAAC,CAAC,GACF,CACP,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -14,16 +14,27 @@ export type GroupedListRowProps = {
|
|
|
14
14
|
* `useIsTextClipped` is `Badge`'s: an ellipsis leaves no trace in the
|
|
15
15
|
* DOM, so the only way to know a label is cut off is to measure it.
|
|
16
16
|
* A `title` on every row would put a tooltip on forty short names to
|
|
17
|
-
* help the one long one.
|
|
17
|
+
* help the one long one. In a selectable row the text lets the
|
|
18
|
+
* pointer through, so the clipped label's tooltip moves to the button
|
|
19
|
+
* the pointer is actually over.
|
|
18
20
|
*
|
|
19
21
|
* ### The actions wrap; they never squeeze the text
|
|
20
22
|
*
|
|
21
23
|
* The row is a wrapping flex line. The text column asks for 12rem
|
|
22
|
-
* and grows into the rest,
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* decided by the content
|
|
24
|
+
* and grows into the rest, and the aside asks for at least 11rem, so
|
|
25
|
+
* in a wide list the aside and the actions sit at the inline end of
|
|
26
|
+
* the same line, and in a list narrower than all of them they drop
|
|
27
|
+
* under the text instead of crushing the label to an ellipsis and
|
|
28
|
+
* nothing. No container query: the wrap is decided by the content
|
|
29
|
+
* itself.
|
|
30
|
+
*
|
|
31
|
+
* ### The layers of a selectable row
|
|
32
|
+
*
|
|
33
|
+
* In tree order, and every one of them positioned, so they paint in
|
|
34
|
+
* that order with no `z-index` to keep in step: the button (bottom),
|
|
35
|
+
* the text and the aside (pointer passes through), the actions
|
|
36
|
+
* (their own targets), and the highlight ring (top, pointer passes
|
|
37
|
+
* through) — the ring is last so a hovered row cannot paint over it.
|
|
27
38
|
*/
|
|
28
39
|
export declare const GroupedListRow: ({ isTextTruncated, item, size, }: GroupedListRowProps) => ReactNode;
|
|
29
40
|
//# sourceMappingURL=GroupedListRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupedListRow.d.ts","sourceRoot":"","sources":["../../src/GroupedList/GroupedListRow.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GroupedListRow.d.ts","sourceRoot":"","sources":["../../src/GroupedList/GroupedListRow.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EAChB,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,mBAAmB,GAAG;IAChC,eAAe,EAAE,OAAO,CAAA;IACxB,IAAI,EAAE,eAAe,CAAA;IACrB,IAAI,EAAE,eAAe,CAAA;CACtB,CAAA;AAmDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,cAAc,GAAI,kCAI5B,mBAAmB,KAAG,SA6IxB,CAAA"}
|