@domicile-desktop/component-library 0.0.0-alpha-866cf8e38ce5 → 0.0.0-alpha-be59aae76f2f
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 -0
- package/package.json +4 -3
- package/src/QuickTile/QuickTile.tsx +183 -0
- package/src/SlideOver/SlideOver.tsx +26 -2
package/README.md
CHANGED
|
@@ -39,6 +39,7 @@ Each export is `@domicile-desktop/component-library/<name>`.
|
|
|
39
39
|
| `Popover` | Non-modal panel anchored to the control that opened it. |
|
|
40
40
|
| `PopupBoundary` | Keeps every popup inside it (tooltips, popovers, menus, lists) within a box, such as a window's frame. Outside one, popups stay on their `Screen`. `Popover` and `Tooltip` take `spansWindows` to leave it. |
|
|
41
41
|
| `Provider` | base-ui `DirectionProvider` wrapper. Every app roots its tree in it. |
|
|
42
|
+
| `QuickTile` | Quick settings tile: a labeled toggle in the accent color while on, with an arrow to its details. Drawn in `currentcolor`. |
|
|
42
43
|
| `Screen` | Renders its children once per selected display. |
|
|
43
44
|
| `DisplayProvider` | Supplies the host's displays to `Screen`. |
|
|
44
45
|
| `display-source` | `Display` and `DisplaySource` types for `DisplayProvider`. |
|
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-be59aae76f2f",
|
|
6
|
+
"@domicile-desktop/system-apps": "0.0.0-alpha-be59aae76f2f",
|
|
7
7
|
"@pandacss/dev": "^1.12.1",
|
|
8
8
|
"@phosphor-icons/react": "^2.1.10"
|
|
9
9
|
},
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"./PortalDialogs": "./src/PortalDialogs/PortalDialogs.tsx",
|
|
65
65
|
"./Provider": "./src/Provider/Provider.tsx",
|
|
66
66
|
"./pandacss-preset": "./src/pandacss-preset.ts",
|
|
67
|
+
"./QuickTile": "./src/QuickTile/QuickTile.tsx",
|
|
67
68
|
"./Screen": "./src/Screen/Screen.tsx",
|
|
68
69
|
"./Select": "./src/Select/Select.tsx",
|
|
69
70
|
"./SlideOver": "./src/SlideOver/SlideOver.tsx",
|
|
@@ -120,5 +121,5 @@
|
|
|
120
121
|
"test:unit": "bun test"
|
|
121
122
|
},
|
|
122
123
|
"type": "module",
|
|
123
|
-
"version": "0.0.0-alpha-
|
|
124
|
+
"version": "0.0.0-alpha-be59aae76f2f"
|
|
124
125
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { Toggle } from "@base-ui/react/toggle";
|
|
2
|
+
import { CaretRightIcon } from "@phosphor-icons/react/dist/ssr/CaretRight";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { css, cva } from "../../styled-system/css";
|
|
6
|
+
|
|
7
|
+
/** What every tile shows: `icon`, then `label` over `detail`. */
|
|
8
|
+
type Content = {
|
|
9
|
+
/** A short status under the label, such as the network's name. */
|
|
10
|
+
detail?: string | undefined;
|
|
11
|
+
icon: ReactNode;
|
|
12
|
+
/** Names the tile, and its toggle. */
|
|
13
|
+
label: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** A tile that switches something on and off, with details behind an arrow. */
|
|
17
|
+
type ToggleProps = Content & {
|
|
18
|
+
/** Ignores presses, such as on a locked desktop. */
|
|
19
|
+
disabled?: boolean | undefined;
|
|
20
|
+
/** Opens the details; without it there is no arrow. */
|
|
21
|
+
onMore?: (() => void) | undefined;
|
|
22
|
+
onPressedChange: (pressed: boolean) => void;
|
|
23
|
+
pressed: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** A tile with nothing to switch, which opens its details. */
|
|
27
|
+
type MoreProps = Content & {
|
|
28
|
+
onMore: () => void;
|
|
29
|
+
onPressedChange?: undefined;
|
|
30
|
+
pressed?: undefined;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type Props = ToggleProps | MoreProps;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A quick settings tile: a labeled toggle in the accent color while on, and
|
|
37
|
+
* an arrow to its details when it has some. A tile with nothing to switch is
|
|
38
|
+
* one button that opens the details.
|
|
39
|
+
*
|
|
40
|
+
* Drawn in `currentcolor`, so it reads on a panel over the wallpaper.
|
|
41
|
+
*/
|
|
42
|
+
export const QuickTile = (props: Props) =>
|
|
43
|
+
props.onPressedChange === undefined ? (
|
|
44
|
+
<button
|
|
45
|
+
aria-label={`${props.label} details`}
|
|
46
|
+
className={tileStyles({ pressed: false })}
|
|
47
|
+
onClick={props.onMore}
|
|
48
|
+
type="button"
|
|
49
|
+
>
|
|
50
|
+
<Face detail={props.detail} icon={props.icon} label={props.label} />
|
|
51
|
+
<CaretRightIcon aria-hidden className={caretStyles} size={14} />
|
|
52
|
+
</button>
|
|
53
|
+
) : (
|
|
54
|
+
<span className={tileStyles({ pressed: props.pressed })}>
|
|
55
|
+
<Toggle
|
|
56
|
+
aria-label={props.label}
|
|
57
|
+
className={toggleStyles}
|
|
58
|
+
disabled={props.disabled}
|
|
59
|
+
onPressedChange={(pressed) => {
|
|
60
|
+
props.onPressedChange(pressed);
|
|
61
|
+
}}
|
|
62
|
+
pressed={props.pressed}
|
|
63
|
+
>
|
|
64
|
+
<Face detail={props.detail} icon={props.icon} label={props.label} />
|
|
65
|
+
</Toggle>
|
|
66
|
+
{props.onMore !== undefined && (
|
|
67
|
+
<button
|
|
68
|
+
aria-label={`${props.label} details`}
|
|
69
|
+
className={moreStyles}
|
|
70
|
+
onClick={props.onMore}
|
|
71
|
+
type="button"
|
|
72
|
+
>
|
|
73
|
+
<CaretRightIcon aria-hidden size={14} />
|
|
74
|
+
</button>
|
|
75
|
+
)}
|
|
76
|
+
</span>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const Face = ({ detail, icon, label }: Content) => (
|
|
80
|
+
<>
|
|
81
|
+
<span aria-hidden className={iconStyles}>
|
|
82
|
+
{icon}
|
|
83
|
+
</span>
|
|
84
|
+
<span className={textStyles}>
|
|
85
|
+
<span className={labelStyles}>{label}</span>
|
|
86
|
+
{detail !== undefined && <span className={detailStyles}>{detail}</span>}
|
|
87
|
+
</span>
|
|
88
|
+
</>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const tileStyles = cva({
|
|
92
|
+
base: {
|
|
93
|
+
_focusVisible: {
|
|
94
|
+
outline: "{spacing.0.5} solid {colors.accent}",
|
|
95
|
+
outlineOffset: 0.5,
|
|
96
|
+
},
|
|
97
|
+
alignItems: "center",
|
|
98
|
+
backgroundColor: "color-mix(in oklab, currentcolor 10%, transparent)",
|
|
99
|
+
borderRadius: "lg",
|
|
100
|
+
borderStyle: "none",
|
|
101
|
+
color: "inherit",
|
|
102
|
+
cursor: "pointer",
|
|
103
|
+
display: "flex",
|
|
104
|
+
gap: 2,
|
|
105
|
+
minBlockSize: 12,
|
|
106
|
+
minInlineSize: 0,
|
|
107
|
+
paddingInline: 2.5,
|
|
108
|
+
textAlign: "start",
|
|
109
|
+
transition: "background-color {durations.fast} {easings.default}",
|
|
110
|
+
},
|
|
111
|
+
variants: {
|
|
112
|
+
pressed: {
|
|
113
|
+
false: {},
|
|
114
|
+
true: { backgroundColor: "accent", color: "background" },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Fills the tile but its arrow, so most of the tile switches.
|
|
120
|
+
const toggleStyles = css({
|
|
121
|
+
_disabled: { cursor: "default" },
|
|
122
|
+
_focusVisible: {
|
|
123
|
+
outline: "{spacing.0.5} solid {colors.accent}",
|
|
124
|
+
outlineOffset: 0.5,
|
|
125
|
+
},
|
|
126
|
+
alignItems: "center",
|
|
127
|
+
alignSelf: "stretch",
|
|
128
|
+
backgroundColor: "transparent",
|
|
129
|
+
borderStyle: "none",
|
|
130
|
+
color: "inherit",
|
|
131
|
+
cursor: "pointer",
|
|
132
|
+
display: "flex",
|
|
133
|
+
flex: 1,
|
|
134
|
+
gap: 2,
|
|
135
|
+
minInlineSize: 0,
|
|
136
|
+
padding: 0,
|
|
137
|
+
textAlign: "start",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const moreStyles = css({
|
|
141
|
+
_hover: {
|
|
142
|
+
backgroundColor: "color-mix(in oklab, currentcolor 16%, transparent)",
|
|
143
|
+
},
|
|
144
|
+
alignItems: "center",
|
|
145
|
+
backgroundColor: "transparent",
|
|
146
|
+
blockSize: 6,
|
|
147
|
+
borderRadius: "full",
|
|
148
|
+
borderStyle: "none",
|
|
149
|
+
color: "inherit",
|
|
150
|
+
cursor: "pointer",
|
|
151
|
+
display: "inline-flex",
|
|
152
|
+
flexShrink: 0,
|
|
153
|
+
inlineSize: 6,
|
|
154
|
+
justifyContent: "center",
|
|
155
|
+
marginInlineEnd: -1,
|
|
156
|
+
padding: 0,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const caretStyles = css({ flexShrink: 0, marginInlineStart: "auto" });
|
|
160
|
+
|
|
161
|
+
const iconStyles = css({ display: "inline-flex", flexShrink: 0 });
|
|
162
|
+
|
|
163
|
+
const textStyles = css({
|
|
164
|
+
display: "flex",
|
|
165
|
+
flexDirection: "column",
|
|
166
|
+
minInlineSize: 0,
|
|
167
|
+
paddingBlock: 1.5,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const labelStyles = css({
|
|
171
|
+
fontSize: "sm",
|
|
172
|
+
fontWeight: "semibold",
|
|
173
|
+
lineHeight: "tight",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const detailStyles = css({
|
|
177
|
+
fontSize: "xs",
|
|
178
|
+
lineHeight: "tight",
|
|
179
|
+
opacity: "inactive",
|
|
180
|
+
overflow: "hidden",
|
|
181
|
+
textOverflow: "ellipsis",
|
|
182
|
+
whiteSpace: "nowrap",
|
|
183
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Dialog as BaseDialog } from "@base-ui/react/dialog";
|
|
2
|
+
import { CaretLeftIcon } from "@phosphor-icons/react/dist/ssr/CaretLeft";
|
|
2
3
|
import { XIcon } from "@phosphor-icons/react/dist/ssr/X";
|
|
3
4
|
import type { ReactElement, ReactNode } from "react";
|
|
4
5
|
import { css, cva } from "../../styled-system/css";
|
|
@@ -20,6 +21,13 @@ type Props = ExtendProps<
|
|
|
20
21
|
{
|
|
21
22
|
children: ReactNode;
|
|
22
23
|
footer?: ReactNode | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Keeps the panel in the page, hidden, while closed, so what it holds
|
|
26
|
+
* keeps running, such as a web page.
|
|
27
|
+
*/
|
|
28
|
+
keepMounted?: boolean | undefined;
|
|
29
|
+
/** Shows a back button before the title, for a page within the panel. */
|
|
30
|
+
onBack?: (() => void) | undefined;
|
|
23
31
|
/**
|
|
24
32
|
* Name of the display to slide in on and dim. Without it, a page spanning
|
|
25
33
|
* several monitors uses the rightmost edge and dims them all. Needs a
|
|
@@ -28,22 +36,27 @@ type Props = ExtendProps<
|
|
|
28
36
|
screen?: string | undefined;
|
|
29
37
|
title?: ReactNode | undefined;
|
|
30
38
|
trigger?: ReactElement | undefined;
|
|
39
|
+
/** Wider, for a page with a list and its content side by side. */
|
|
40
|
+
wide?: boolean | undefined;
|
|
31
41
|
}
|
|
32
42
|
>;
|
|
33
43
|
|
|
34
44
|
const SlideOverComponent = ({
|
|
35
45
|
children,
|
|
36
46
|
footer,
|
|
47
|
+
keepMounted = false,
|
|
48
|
+
onBack,
|
|
37
49
|
screen,
|
|
38
50
|
title,
|
|
39
51
|
trigger,
|
|
52
|
+
wide = false,
|
|
40
53
|
...rootProps
|
|
41
54
|
}: Props) => {
|
|
42
55
|
const region = useScreenRegion(screen);
|
|
43
56
|
return (
|
|
44
57
|
<BaseDialog.Root {...rootProps}>
|
|
45
58
|
{trigger !== undefined && <BaseDialog.Trigger render={trigger} />}
|
|
46
|
-
<BaseDialog.Portal>
|
|
59
|
+
<BaseDialog.Portal keepMounted={keepMounted}>
|
|
47
60
|
{/*
|
|
48
61
|
On the panel's screen only, as in `ModalDialog`. Data attribute for
|
|
49
62
|
tests.
|
|
@@ -54,8 +67,16 @@ const SlideOverComponent = ({
|
|
|
54
67
|
style={region}
|
|
55
68
|
/>
|
|
56
69
|
<BaseDialog.Viewport className={viewportStyles} style={region}>
|
|
57
|
-
<BaseDialog.Popup
|
|
70
|
+
<BaseDialog.Popup
|
|
71
|
+
className={popupStyles}
|
|
72
|
+
data-wide={wide ? "" : undefined}
|
|
73
|
+
>
|
|
58
74
|
<header className={headerStyles}>
|
|
75
|
+
{onBack !== undefined && (
|
|
76
|
+
<Button label="Back" onClick={onBack} variant="ghost">
|
|
77
|
+
<CaretLeftIcon />
|
|
78
|
+
</Button>
|
|
79
|
+
)}
|
|
59
80
|
{title !== undefined && (
|
|
60
81
|
<BaseDialog.Title className={titleStyles}>
|
|
61
82
|
{title}
|
|
@@ -126,6 +147,7 @@ const popupStyles = flex({
|
|
|
126
147
|
transform: "translateX(100%)",
|
|
127
148
|
transition: "transform {durations.fast} {easings.in}",
|
|
128
149
|
},
|
|
150
|
+
"&[data-wide]": { inlineSize: "min({spacing.240}, 92cqw)" },
|
|
129
151
|
backgroundColor: "card",
|
|
130
152
|
blockSize: "100%",
|
|
131
153
|
borderInlineStartColor: "border",
|
|
@@ -147,8 +169,10 @@ const headerStyles = flex({
|
|
|
147
169
|
paddingInline: 4,
|
|
148
170
|
});
|
|
149
171
|
|
|
172
|
+
// Fills the header between the back and close buttons.
|
|
150
173
|
const titleStyles = css({
|
|
151
174
|
color: "foreground",
|
|
175
|
+
flex: 1,
|
|
152
176
|
fontSize: "md",
|
|
153
177
|
fontWeight: "semibold",
|
|
154
178
|
lineHeight: "snug",
|