@domicile-desktop/component-library 0.0.0-alpha-0bd0d2a4d04f → 0.0.0-alpha-a2ee8eaa5f91
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/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-a2ee8eaa5f91",
|
|
6
|
+
"@domicile-desktop/system-apps": "0.0.0-alpha-a2ee8eaa5f91",
|
|
7
7
|
"@pandacss/dev": "^1.12.1",
|
|
8
8
|
"@phosphor-icons/react": "^2.1.10"
|
|
9
9
|
},
|
|
@@ -159,5 +159,5 @@
|
|
|
159
159
|
"test:unit": "bun test"
|
|
160
160
|
},
|
|
161
161
|
"type": "module",
|
|
162
|
-
"version": "0.0.0-alpha-
|
|
162
|
+
"version": "0.0.0-alpha-a2ee8eaa5f91"
|
|
163
163
|
}
|
|
@@ -36,6 +36,11 @@ type CommonProps<V> = Partial<ControlVariant> & {
|
|
|
36
36
|
disabled?: boolean | undefined;
|
|
37
37
|
/** Shown in place of the list when `suggestions` is empty. */
|
|
38
38
|
emptyMessage?: ReactNode | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* A tinted field with no edge until hovered or focused, as in an address
|
|
41
|
+
* bar, which sits on a toolbar rather than on a page.
|
|
42
|
+
*/
|
|
43
|
+
filled?: boolean | undefined;
|
|
39
44
|
onSuggestionTaken?: ((value: V) => void) | undefined;
|
|
40
45
|
onValueChange?: ((value: string) => void) | undefined;
|
|
41
46
|
rounded?: boolean | undefined;
|
|
@@ -74,6 +79,7 @@ export const Autocomplete = <V,>({
|
|
|
74
79
|
autoHighlight = false,
|
|
75
80
|
disabled,
|
|
76
81
|
emptyMessage,
|
|
82
|
+
filled = false,
|
|
77
83
|
onSuggestionTaken,
|
|
78
84
|
onValueChange,
|
|
79
85
|
prefixButtons,
|
|
@@ -107,7 +113,7 @@ export const Autocomplete = <V,>({
|
|
|
107
113
|
className={cx(
|
|
108
114
|
control({ size }),
|
|
109
115
|
wrapperBase,
|
|
110
|
-
wrapperStyles({ rounded, size }),
|
|
116
|
+
wrapperStyles({ filled, rounded, size }),
|
|
111
117
|
)}
|
|
112
118
|
onMouseDown={focusControlOnMouseDown}
|
|
113
119
|
ref={attach}
|
|
@@ -211,6 +217,15 @@ const wrapperStyles = cva({
|
|
|
211
217
|
{ css: { paddingInline: 11.5 }, rounded: true, size: "4xl" },
|
|
212
218
|
],
|
|
213
219
|
variants: {
|
|
220
|
+
// `wrapperBase` still draws the hover and focus edges.
|
|
221
|
+
filled: {
|
|
222
|
+
false: {},
|
|
223
|
+
true: {
|
|
224
|
+
backgroundColor: "card",
|
|
225
|
+
borderColor: "transparent",
|
|
226
|
+
borderRadius: "lg",
|
|
227
|
+
},
|
|
228
|
+
},
|
|
214
229
|
rounded: {
|
|
215
230
|
true: { borderRadius: "full" },
|
|
216
231
|
},
|
|
@@ -820,13 +820,16 @@ const sidebarStyles = flex({
|
|
|
820
820
|
|
|
821
821
|
const placesStyles = flex({
|
|
822
822
|
"& > button": {
|
|
823
|
+
// 13px, the desktop's text size, as the folder's rows.
|
|
824
|
+
fontSize: "0.8125rem",
|
|
823
825
|
gap: 2.5,
|
|
824
826
|
inlineSize: "100%",
|
|
825
827
|
justifyContent: "flex-start",
|
|
826
828
|
},
|
|
827
829
|
"& > button[aria-current=location]": {
|
|
828
|
-
backgroundColor:
|
|
829
|
-
|
|
830
|
+
backgroundColor:
|
|
831
|
+
"color-mix(in oklab, {colors.foreground} 10%, transparent)",
|
|
832
|
+
color: "foreground",
|
|
830
833
|
fontWeight: "semibold",
|
|
831
834
|
},
|
|
832
835
|
direction: "column",
|
|
@@ -883,7 +886,11 @@ const crumbSeparatorStyles = css({
|
|
|
883
886
|
});
|
|
884
887
|
|
|
885
888
|
const resultsStyles = css({
|
|
886
|
-
|
|
889
|
+
// White in light mode, as a document is.
|
|
890
|
+
backgroundColor: {
|
|
891
|
+
_light: "color-mix(in oklab, {colors.white} 80%, transparent)",
|
|
892
|
+
base: "color-mix(in oklab, {colors.background} 30%, transparent)",
|
|
893
|
+
},
|
|
887
894
|
border: "1px solid {colors.border}",
|
|
888
895
|
borderRadius: "lg",
|
|
889
896
|
flex: "1 1 auto",
|
|
@@ -902,7 +909,8 @@ const columnsStyles = grid({
|
|
|
902
909
|
backgroundColor: "color-mix(in oklab, {colors.card} 70%, transparent)",
|
|
903
910
|
color: "textTertiary",
|
|
904
911
|
columnGap: 3,
|
|
905
|
-
|
|
912
|
+
// 11px, which has no scale token.
|
|
913
|
+
fontSize: "0.6875rem",
|
|
906
914
|
fontWeight: "semibold",
|
|
907
915
|
gridTemplateColumns: "minmax(0, 1fr) {spacing.32}",
|
|
908
916
|
insetBlockStart: 0,
|
|
@@ -927,21 +935,14 @@ const rowStyles = grid({
|
|
|
927
935
|
_hoverEnabled: {
|
|
928
936
|
backgroundColor: "color-mix(in oklab, {colors.foreground} 5%, transparent)",
|
|
929
937
|
},
|
|
930
|
-
|
|
931
|
-
//
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
color: "var(--tone)",
|
|
936
|
-
},
|
|
938
|
+
"--tone": "{colors.muted}",
|
|
939
|
+
// A plain glyph, teal for a folder and gray for anything else, so folders
|
|
940
|
+
// stand out without a tile behind every row.
|
|
941
|
+
"& [data-row-tile]": { color: "var(--tone)" },
|
|
942
|
+
// A neutral wash: color is kept for focus and the main action.
|
|
937
943
|
"&[data-highlighted]": {
|
|
938
|
-
|
|
939
|
-
"
|
|
940
|
-
},
|
|
941
|
-
"&[data-highlighted] [data-row-tile]": {
|
|
942
|
-
backgroundColor: "color-mix(in oklab, {colors.accent} 28%, transparent)",
|
|
943
|
-
borderColor: "color-mix(in oklab, {colors.accent} 45%, transparent)",
|
|
944
|
-
color: "accent",
|
|
944
|
+
backgroundColor:
|
|
945
|
+
"color-mix(in oklab, {colors.foreground} 10%, transparent)",
|
|
945
946
|
},
|
|
946
947
|
"&[data-marked] [data-row-tile]": {
|
|
947
948
|
backgroundColor: "accent",
|
|
@@ -952,24 +953,8 @@ const rowStyles = grid({
|
|
|
952
953
|
"&[data-replaced]": {
|
|
953
954
|
backgroundColor: "color-mix(in oklab, {colors.warning} 14%, transparent)",
|
|
954
955
|
},
|
|
955
|
-
"&[data-tone=archive]": { "--tone": "{colors.warning}" },
|
|
956
|
-
"&[data-tone=audio]": {
|
|
957
|
-
"--tone": "color-mix(in oklch, {colors.warning} 50%, {colors.danger})",
|
|
958
|
-
},
|
|
959
|
-
"&[data-tone=code]": { "--tone": "{colors.success}" },
|
|
960
|
-
"&[data-tone=document]": {
|
|
961
|
-
"--tone": "color-mix(in oklab, {colors.accent} 45%, {colors.foreground})",
|
|
962
|
-
},
|
|
963
956
|
"&[data-tone=folder]": { "--tone": "{colors.accent}" },
|
|
964
|
-
"&[data-tone=
|
|
965
|
-
"--tone": "color-mix(in oklch, {colors.danger} 55%, {colors.accent})",
|
|
966
|
-
},
|
|
967
|
-
"&[data-tone=other]": { "--tone": "{colors.muted}" },
|
|
968
|
-
"&[data-tone=parent]": { "--tone": "{colors.muted}", color: "muted" },
|
|
969
|
-
"&[data-tone=pdf]": { "--tone": "{colors.danger}" },
|
|
970
|
-
"&[data-tone=video]": {
|
|
971
|
-
"--tone": "color-mix(in oklch, {colors.danger} 80%, {colors.warning})",
|
|
972
|
-
},
|
|
957
|
+
"&[data-tone=parent]": { color: "muted" },
|
|
973
958
|
alignItems: "center",
|
|
974
959
|
borderRadius: "md",
|
|
975
960
|
color: "foreground",
|
|
@@ -994,7 +979,8 @@ const rowTileStyles = css({
|
|
|
994
979
|
});
|
|
995
980
|
|
|
996
981
|
const rowNameStyles = css({
|
|
997
|
-
|
|
982
|
+
// 13px, the desktop's text size, between the scale's 12px and 14px.
|
|
983
|
+
fontSize: "0.8125rem",
|
|
998
984
|
fontWeight: "medium",
|
|
999
985
|
overflow: "hidden",
|
|
1000
986
|
textOverflow: "ellipsis",
|
package/src/Input/Input.tsx
CHANGED
|
@@ -17,6 +17,11 @@ export { SIZES, type Size } from "../control-sizes";
|
|
|
17
17
|
|
|
18
18
|
type CommonProps = Partial<ControlVariant> & {
|
|
19
19
|
clearable?: boolean | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* No box: just the text and its icons, for a field that heads a panel. It
|
|
22
|
+
* draws no edge, fill or focus ring.
|
|
23
|
+
*/
|
|
24
|
+
flush?: boolean | undefined;
|
|
20
25
|
prefixIcon?: ReactNode | undefined;
|
|
21
26
|
// Narrower than base-ui's `Ref<HTMLElement>` since this always renders an
|
|
22
27
|
// `<input>`. Ref objects are invariant, so the wider type would need a cast.
|
|
@@ -36,6 +41,7 @@ type Props = ExtendProps<
|
|
|
36
41
|
|
|
37
42
|
export const Input = ({
|
|
38
43
|
clearable = false,
|
|
44
|
+
flush = false,
|
|
39
45
|
prefixIcon,
|
|
40
46
|
rounded = false,
|
|
41
47
|
size = "md",
|
|
@@ -74,8 +80,9 @@ export const Input = ({
|
|
|
74
80
|
className={cx(
|
|
75
81
|
control({ size }),
|
|
76
82
|
wrapperBase,
|
|
77
|
-
wrapperStyles({ rounded, size }),
|
|
83
|
+
wrapperStyles({ flush, rounded, size }),
|
|
78
84
|
)}
|
|
85
|
+
data-flush={flush ? "" : undefined}
|
|
79
86
|
onMouseDown={focusControlOnMouseDown}
|
|
80
87
|
style={controlSizingStyle({ width })}
|
|
81
88
|
>
|
|
@@ -133,6 +140,20 @@ const wrapperStyles = cva({
|
|
|
133
140
|
{ css: { paddingInline: 11.5 }, rounded: true, size: "4xl" },
|
|
134
141
|
],
|
|
135
142
|
variants: {
|
|
143
|
+
// `wrapperBase` and glass surfaces skip their edges and fills for
|
|
144
|
+
// `[data-flush]`.
|
|
145
|
+
// Under `[data-flush]`, to outrank `wrapperBase`'s own fill and edge.
|
|
146
|
+
flush: {
|
|
147
|
+
false: {},
|
|
148
|
+
true: {
|
|
149
|
+
"&[data-flush]": {
|
|
150
|
+
backgroundColor: "transparent",
|
|
151
|
+
borderColor: "transparent",
|
|
152
|
+
borderRadius: 0,
|
|
153
|
+
outlineStyle: "none",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
136
157
|
rounded: {
|
|
137
158
|
true: { borderRadius: "full" },
|
|
138
159
|
},
|
|
@@ -31,8 +31,8 @@ export const SURFACES = ["card", "glass"] as const;
|
|
|
31
31
|
export type Surface = (typeof SURFACES)[number];
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* The popup's width. `lg` fits rows with several columns
|
|
35
|
-
* beside a preview.
|
|
34
|
+
* The popup's width. `lg` fits rows with several columns, or a compact list
|
|
35
|
+
* beside a preview; `xl` fits a roomy list beside a preview.
|
|
36
36
|
*/
|
|
37
37
|
export const SIZES = ["md", "lg", "xl"] as const;
|
|
38
38
|
export type Size = (typeof SIZES)[number];
|
|
@@ -68,6 +68,11 @@ type Props = ExtendProps<
|
|
|
68
68
|
size?: Size | undefined;
|
|
69
69
|
surface?: Surface | undefined;
|
|
70
70
|
title?: ReactNode | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Whether `title` is for screen readers only, as when the popup's first
|
|
73
|
+
* control already says what it is for.
|
|
74
|
+
*/
|
|
75
|
+
titleHidden?: boolean | undefined;
|
|
71
76
|
trigger?: ReactElement | undefined;
|
|
72
77
|
}
|
|
73
78
|
>;
|
|
@@ -83,6 +88,7 @@ const ModalDialogComponent = ({
|
|
|
83
88
|
size = "md",
|
|
84
89
|
surface = "card",
|
|
85
90
|
title,
|
|
91
|
+
titleHidden = false,
|
|
86
92
|
trigger,
|
|
87
93
|
...rootProps
|
|
88
94
|
}: Props) => {
|
|
@@ -119,7 +125,12 @@ const ModalDialogComponent = ({
|
|
|
119
125
|
finalFocus={returnFocus}
|
|
120
126
|
onKeyDown={moveFooterFocus}
|
|
121
127
|
>
|
|
122
|
-
{title !== undefined && (
|
|
128
|
+
{title !== undefined && titleHidden && (
|
|
129
|
+
<BaseDialog.Title className={hiddenTitleStyles}>
|
|
130
|
+
{title}
|
|
131
|
+
</BaseDialog.Title>
|
|
132
|
+
)}
|
|
133
|
+
{title !== undefined && !titleHidden && (
|
|
123
134
|
<header
|
|
124
135
|
className={headerStyles({ hasCloseButton: closeButton })}
|
|
125
136
|
>
|
|
@@ -143,7 +154,7 @@ const ModalDialogComponent = ({
|
|
|
143
154
|
className={bodyStyles({
|
|
144
155
|
hasCloseButton: closeButton,
|
|
145
156
|
hasFooter: footer !== undefined,
|
|
146
|
-
hasTitle: title !== undefined,
|
|
157
|
+
hasTitle: title !== undefined && !titleHidden,
|
|
147
158
|
})}
|
|
148
159
|
>
|
|
149
160
|
{children}
|
|
@@ -225,7 +236,7 @@ const popupStyles = flex({
|
|
|
225
236
|
marginBlockStart: "8cqh",
|
|
226
237
|
},
|
|
227
238
|
"&[data-size=lg]": {
|
|
228
|
-
inlineSize: "min({spacing.
|
|
239
|
+
inlineSize: "min({spacing.220}, 92cqw)",
|
|
229
240
|
},
|
|
230
241
|
"&[data-size=xl]": {
|
|
231
242
|
inlineSize: "min({spacing.320}, 92cqw)",
|
|
@@ -241,20 +252,24 @@ const popupStyles = flex({
|
|
|
241
252
|
position: "absolute",
|
|
242
253
|
},
|
|
243
254
|
backdropFilter: "blur({spacing.5}) saturate(180%)",
|
|
244
|
-
|
|
255
|
+
// Near white in light mode: the light card through blur reads as gray.
|
|
256
|
+
backgroundColor: {
|
|
257
|
+
_light: "color-mix(in oklab, {colors.white} 86%, transparent)",
|
|
258
|
+
base: "color-mix(in oklab, {colors.card} 62%, transparent)",
|
|
259
|
+
},
|
|
245
260
|
borderColor: "color-mix(in oklab, {colors.foreground} 16%, transparent)",
|
|
246
261
|
},
|
|
247
262
|
// Makes form controls translucent on glass. Controls mark their element
|
|
248
263
|
// with `data-control`, so its parent is the wrapper that paints the
|
|
249
264
|
// background. The descendant selector outranks the wrapper's own class
|
|
250
265
|
// regardless of order (see `_control/wrapperBase.ts`).
|
|
251
|
-
"&[data-surface=glass] :has(> [data-control])": {
|
|
266
|
+
"&[data-surface=glass] :has(> [data-control]):not([data-flush])": {
|
|
252
267
|
backgroundColor:
|
|
253
268
|
"color-mix(in oklab, {colors.background} 45%, transparent)",
|
|
254
269
|
},
|
|
255
270
|
backgroundColor: "card",
|
|
256
271
|
border: "1px solid {colors.border}",
|
|
257
|
-
borderRadius: "
|
|
272
|
+
borderRadius: "0.875rem",
|
|
258
273
|
boxShadow: "modal",
|
|
259
274
|
direction: "column",
|
|
260
275
|
inlineSize: "min({spacing.120}, 92cqw)",
|
|
@@ -293,6 +308,8 @@ const titleStyles = css({
|
|
|
293
308
|
margin: 0,
|
|
294
309
|
});
|
|
295
310
|
|
|
311
|
+
const hiddenTitleStyles = css({ srOnly: true });
|
|
312
|
+
|
|
296
313
|
const closeStyles = css({
|
|
297
314
|
insetBlockStart: 3,
|
|
298
315
|
insetInlineEnd: 3,
|
|
@@ -13,44 +13,56 @@ type Props = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
/** A yes/no question from an application. Dismissing it denies. */
|
|
16
|
-
export const AccessDialog = ({ answer, asker, body, screen }: Props) =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
16
|
+
export const AccessDialog = ({ answer, asker, body, screen }: Props) => {
|
|
17
|
+
// One line under the title. The subtitle mostly restates the title, so the
|
|
18
|
+
// body wins when there is one.
|
|
19
|
+
const detail = [body.body, body.subtitle].find((text) => text !== "");
|
|
20
|
+
return (
|
|
21
|
+
<ModalDialog
|
|
22
|
+
closeButton={false}
|
|
23
|
+
footer={
|
|
24
|
+
<>
|
|
25
|
+
{asker !== undefined && (
|
|
26
|
+
<p className={askerStyles}>{`${asker} asks`}</p>
|
|
27
|
+
)}
|
|
28
|
+
<Button
|
|
29
|
+
onClick={() => {
|
|
30
|
+
answer(Answer.Canceled());
|
|
31
|
+
}}
|
|
32
|
+
variant="outline"
|
|
33
|
+
>
|
|
34
|
+
{body.denyLabel ?? "Deny"}
|
|
35
|
+
</Button>
|
|
36
|
+
<Button
|
|
37
|
+
onClick={() => {
|
|
38
|
+
answer(Answer.Access());
|
|
39
|
+
}}
|
|
40
|
+
variant={body.destructive ? "danger" : "accent"}
|
|
41
|
+
>
|
|
42
|
+
{body.grantLabel ?? "Allow"}
|
|
43
|
+
</Button>
|
|
44
|
+
</>
|
|
42
45
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
onOpenChange={(open) => {
|
|
47
|
+
if (!open) {
|
|
48
|
+
answer(Answer.Canceled());
|
|
49
|
+
}
|
|
50
|
+
}}
|
|
51
|
+
open
|
|
52
|
+
screen={screen}
|
|
53
|
+
title={body.title}
|
|
54
|
+
>
|
|
55
|
+
{detail !== undefined && <p className={detailStyles}>{detail}</p>}
|
|
56
|
+
</ModalDialog>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
53
59
|
|
|
54
|
-
|
|
60
|
+
// Who asks sits opposite the buttons, leaving the body one line.
|
|
61
|
+
const askerStyles = css({
|
|
62
|
+
color: "muted",
|
|
63
|
+
fontSize: "0.8125rem",
|
|
64
|
+
margin: 0,
|
|
65
|
+
marginInlineEnd: "auto",
|
|
66
|
+
});
|
|
55
67
|
|
|
56
|
-
const
|
|
68
|
+
const detailStyles = css({ color: "muted", margin: 0 });
|
|
@@ -7,7 +7,8 @@ import { css } from "../../styled-system/css";
|
|
|
7
7
|
* it, marked `data-control`. Selectors always target `[data-control]` so
|
|
8
8
|
* suffix buttons can't affect the wrapper; a disabled Send button must not
|
|
9
9
|
* gray out the textarea. Precedence is disabled, invalid, focused, hovered,
|
|
10
|
-
* enforced by the `:not(...)` chains.
|
|
10
|
+
* enforced by the `:not(...)` chains. A `[data-flush]` wrapper (a flush
|
|
11
|
+
* `Input`) draws no hover or focus edge.
|
|
11
12
|
*
|
|
12
13
|
* The wrapper looks focused when the control has `:focus`,
|
|
13
14
|
* `[data-popup-open]` (a Select popup holds focus), or `[data-active]`
|
|
@@ -31,7 +32,7 @@ export const wrapperBase = css({
|
|
|
31
32
|
"&:has([data-control]:is(:focus, [data-popup-open], [data-active]))": {
|
|
32
33
|
color: "foreground",
|
|
33
34
|
},
|
|
34
|
-
"&:has([data-control]:is(:focus, [data-popup-open], [data-active])):not(:has([data-control][data-invalid]))":
|
|
35
|
+
"&:has([data-control]:is(:focus, [data-popup-open], [data-active])):not(:has([data-control][data-invalid])):not([data-flush])":
|
|
35
36
|
{
|
|
36
37
|
borderColor: "accent",
|
|
37
38
|
outlineColor: "accent",
|
|
@@ -65,7 +66,7 @@ export const wrapperBase = css({
|
|
|
65
66
|
"&:has([data-control][data-resizing]) [data-resize-handle]": {
|
|
66
67
|
color: "muted",
|
|
67
68
|
},
|
|
68
|
-
"&:is(:hover, :has([data-control][data-resizing])):not(:has([data-control]:disabled)):not(:has([data-control]:is(:focus, [data-popup-open], [data-active]))):not(:has([data-control][data-invalid]))":
|
|
69
|
+
"&:is(:hover, :has([data-control][data-resizing])):not(:has([data-control]:disabled)):not(:has([data-control]:is(:focus, [data-popup-open], [data-active]))):not(:has([data-control][data-invalid])):not([data-flush])":
|
|
69
70
|
{
|
|
70
71
|
borderColor: "muted",
|
|
71
72
|
},
|