@domicile-desktop/component-library 0.0.0-alpha-080127a56436 → 0.0.0-alpha-6fbdcee58dcc
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 +1 -1
- package/package.json +3 -3
- package/src/ContextMenu/ContextMenu.tsx +84 -4
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Each export is `@domicile-desktop/component-library/<name>`.
|
|
|
29
29
|
| `Kbd` | Keyboard key cap. |
|
|
30
30
|
| `list-walk` | Arrow-key movement of a highlight through a list while focus stays in an input. |
|
|
31
31
|
| `ModalDialog` | Modal dialog with `title`, `footer` and `trigger` props. |
|
|
32
|
-
| `ContextMenu` | Menu opened at a point, such as a right click's. |
|
|
32
|
+
| `ContextMenu` | Menu opened at a point, such as a right click's. Parts: `Item`, `CheckboxItem`, `RadioGroup` and `RadioItem`, `Submenu`, `Separator`. |
|
|
33
33
|
| `PortalDialogs` | Every dialog applications ask for through the desktop portal. Mount one per shell with `host={domicile}`; it refuses kinds it cannot draw. `omitScreenCasts` leaves screen casts out of its indicator. See [PORTALS.md](/docs/PORTALS.md). |
|
|
34
34
|
| `source-name` | `sourceName`: how a screen cast's source is named. |
|
|
35
35
|
| `Popover` | Non-modal panel anchored to the control that opened it. |
|
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"dependencies": {
|
|
3
3
|
"@base-ui/react": "^1.8.0",
|
|
4
4
|
"@cprussin/option-result": "^2.0.1",
|
|
5
|
-
"@domicile-desktop/sdk": "0.0.0-alpha-
|
|
6
|
-
"@domicile-desktop/system-apps": "0.0.0-alpha-
|
|
5
|
+
"@domicile-desktop/sdk": "0.0.0-alpha-6fbdcee58dcc",
|
|
6
|
+
"@domicile-desktop/system-apps": "0.0.0-alpha-6fbdcee58dcc",
|
|
7
7
|
"@phosphor-icons/react": "^2.1.10"
|
|
8
8
|
},
|
|
9
9
|
"description": "React components and the Panda CSS design system that Domicile's shells are built from.",
|
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"test:unit": "bun test"
|
|
105
105
|
},
|
|
106
106
|
"type": "module",
|
|
107
|
-
"version": "0.0.0-alpha-
|
|
107
|
+
"version": "0.0.0-alpha-6fbdcee58dcc"
|
|
108
108
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { ContextMenu as BaseContextMenu } from "@base-ui/react/context-menu";
|
|
1
2
|
import { Menu as BaseMenu } from "@base-ui/react/menu";
|
|
3
|
+
import { CaretRightIcon } from "@phosphor-icons/react/dist/ssr/CaretRight";
|
|
4
|
+
import { CheckIcon } from "@phosphor-icons/react/dist/ssr/Check";
|
|
5
|
+
import { DotIcon } from "@phosphor-icons/react/dist/ssr/Dot";
|
|
2
6
|
import type { ReactNode } from "react";
|
|
3
7
|
import { useMemo } from "react";
|
|
4
8
|
import { css } from "../../styled-system/css";
|
|
@@ -8,7 +12,7 @@ import type { ExtendProps } from "../extend-props";
|
|
|
8
12
|
export type Point = { x: number; y: number };
|
|
9
13
|
|
|
10
14
|
type Props = ExtendProps<
|
|
11
|
-
typeof
|
|
15
|
+
typeof BaseContextMenu.Root,
|
|
12
16
|
{
|
|
13
17
|
/** Where the menu opens: its top left corner, unless that would not fit. */
|
|
14
18
|
at: Point;
|
|
@@ -37,7 +41,9 @@ const ContextMenuComponent = ({ at, children, label, ...rootProps }: Props) => {
|
|
|
37
41
|
[at.x, at.y],
|
|
38
42
|
);
|
|
39
43
|
return (
|
|
40
|
-
|
|
44
|
+
// The context menu's root, not `Menu.Root`: a `Menu.Root` with no trigger
|
|
45
|
+
// closes when one of its submenus opens.
|
|
46
|
+
<BaseContextMenu.Root {...rootProps}>
|
|
41
47
|
<BaseMenu.Portal>
|
|
42
48
|
<BaseMenu.Positioner
|
|
43
49
|
align="start"
|
|
@@ -51,7 +57,7 @@ const ContextMenuComponent = ({ at, children, label, ...rootProps }: Props) => {
|
|
|
51
57
|
</BaseMenu.Popup>
|
|
52
58
|
</BaseMenu.Positioner>
|
|
53
59
|
</BaseMenu.Portal>
|
|
54
|
-
</
|
|
60
|
+
</BaseContextMenu.Root>
|
|
55
61
|
);
|
|
56
62
|
};
|
|
57
63
|
|
|
@@ -74,12 +80,76 @@ const Item = ({ children, shortcut, ...itemProps }: ItemProps) => (
|
|
|
74
80
|
</BaseMenu.Item>
|
|
75
81
|
);
|
|
76
82
|
|
|
83
|
+
type CheckboxItemProps = ExtendProps<
|
|
84
|
+
typeof BaseMenu.CheckboxItem,
|
|
85
|
+
{ children: ReactNode }
|
|
86
|
+
>;
|
|
87
|
+
|
|
88
|
+
/** An item with a check mark that shows whether it is on. */
|
|
89
|
+
const CheckboxItem = ({ children, ...itemProps }: CheckboxItemProps) => (
|
|
90
|
+
<BaseMenu.CheckboxItem className={itemStyles} {...itemProps}>
|
|
91
|
+
<span className={labelStyles}>{children}</span>
|
|
92
|
+
<BaseMenu.CheckboxItemIndicator className={indicatorStyles}>
|
|
93
|
+
<CheckIcon />
|
|
94
|
+
</BaseMenu.CheckboxItemIndicator>
|
|
95
|
+
</BaseMenu.CheckboxItem>
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
/** Radio items, of which the one whose `value` is the group's is chosen. */
|
|
99
|
+
const RadioGroup = BaseMenu.RadioGroup;
|
|
100
|
+
|
|
101
|
+
type RadioItemProps = ExtendProps<
|
|
102
|
+
typeof BaseMenu.RadioItem,
|
|
103
|
+
{ children: ReactNode }
|
|
104
|
+
>;
|
|
105
|
+
|
|
106
|
+
/** One choice in a `RadioGroup`, with a dot when chosen. */
|
|
107
|
+
const RadioItem = ({ children, ...itemProps }: RadioItemProps) => (
|
|
108
|
+
<BaseMenu.RadioItem className={itemStyles} {...itemProps}>
|
|
109
|
+
<span className={labelStyles}>{children}</span>
|
|
110
|
+
<BaseMenu.RadioItemIndicator className={indicatorStyles}>
|
|
111
|
+
<DotIcon weight="bold" />
|
|
112
|
+
</BaseMenu.RadioItemIndicator>
|
|
113
|
+
</BaseMenu.RadioItem>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
type SubmenuProps = ExtendProps<
|
|
117
|
+
typeof BaseMenu.SubmenuRoot,
|
|
118
|
+
{
|
|
119
|
+
children: ReactNode;
|
|
120
|
+
disabled?: boolean | undefined;
|
|
121
|
+
/** The text of the item that opens it, and the submenu's name. */
|
|
122
|
+
label: string;
|
|
123
|
+
}
|
|
124
|
+
>;
|
|
125
|
+
|
|
126
|
+
/** An item that opens a menu of `children` beside it. */
|
|
127
|
+
const Submenu = ({ children, disabled, label, ...rootProps }: SubmenuProps) => (
|
|
128
|
+
<BaseMenu.SubmenuRoot {...rootProps}>
|
|
129
|
+
<BaseMenu.SubmenuTrigger className={itemStyles} disabled={disabled}>
|
|
130
|
+
<span className={labelStyles}>{label}</span>
|
|
131
|
+
<CaretRightIcon className={caretStyles} />
|
|
132
|
+
</BaseMenu.SubmenuTrigger>
|
|
133
|
+
<BaseMenu.Portal>
|
|
134
|
+
<BaseMenu.Positioner className={positionerStyles}>
|
|
135
|
+
<BaseMenu.Popup aria-label={label} className={popupStyles}>
|
|
136
|
+
{children}
|
|
137
|
+
</BaseMenu.Popup>
|
|
138
|
+
</BaseMenu.Positioner>
|
|
139
|
+
</BaseMenu.Portal>
|
|
140
|
+
</BaseMenu.SubmenuRoot>
|
|
141
|
+
);
|
|
142
|
+
|
|
77
143
|
/** A line between groups of items. */
|
|
78
144
|
const Separator = () => <BaseMenu.Separator className={separatorStyles} />;
|
|
79
145
|
|
|
80
146
|
export const ContextMenu = Object.assign(ContextMenuComponent, {
|
|
147
|
+
CheckboxItem,
|
|
81
148
|
Item,
|
|
149
|
+
RadioGroup,
|
|
150
|
+
RadioItem,
|
|
82
151
|
Separator,
|
|
152
|
+
Submenu,
|
|
83
153
|
});
|
|
84
154
|
|
|
85
155
|
const positionerStyles = css({
|
|
@@ -113,7 +183,7 @@ const popupStyles = css({
|
|
|
113
183
|
transition: "opacity {durations.fast} {easings.out}",
|
|
114
184
|
});
|
|
115
185
|
|
|
116
|
-
// `Select`'s item
|
|
186
|
+
// `Select`'s item. Its end holds a shortcut, check, dot or caret.
|
|
117
187
|
const itemStyles = css({
|
|
118
188
|
"&[data-disabled]": {
|
|
119
189
|
color: "muted",
|
|
@@ -146,6 +216,16 @@ const shortcutStyles = css({
|
|
|
146
216
|
fontSize: "xs",
|
|
147
217
|
});
|
|
148
218
|
|
|
219
|
+
// `Select`'s check.
|
|
220
|
+
const indicatorStyles = css({
|
|
221
|
+
alignItems: "center",
|
|
222
|
+
color: "accent",
|
|
223
|
+
display: "inline-flex",
|
|
224
|
+
flexShrink: 0,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const caretStyles = css({ color: "muted" });
|
|
228
|
+
|
|
149
229
|
const separatorStyles = css({
|
|
150
230
|
backgroundColor: "border",
|
|
151
231
|
blockSize: "1px",
|