@surflane/ui 0.12.0 → 0.13.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/dist/alert-dialog.d.ts +25 -7
- package/dist/alert-dialog.js +9 -4
- package/dist/autocomplete.d.ts +2 -6
- package/dist/avatar.d.ts +7 -7
- package/dist/avatar.js +2 -2
- package/dist/bar-list.js +7 -1
- package/dist/button.d.ts +6 -11
- package/dist/button.js +8 -8
- package/dist/card.d.ts +15 -10
- package/dist/card.js +7 -2
- package/dist/chart.d.ts +6 -4
- package/dist/chart.js +83 -19
- package/dist/choice.d.ts +2 -2
- package/dist/class-names.d.ts +0 -4
- package/dist/class-names.js +0 -14
- package/dist/code.d.ts +19 -7
- package/dist/code.js +32 -38
- package/dist/combobox.d.ts +2 -8
- package/dist/combobox.js +2 -2
- package/dist/context-menu.d.ts +2 -8
- package/dist/context-menu.js +2 -2
- package/dist/css/rules/alert-dialog.css +4 -2
- package/dist/css/rules/app-shell.css +47 -10
- package/dist/css/rules/bar-list.css +2 -2
- package/dist/css/rules/bar.css +5 -12
- package/dist/css/rules/bottom-navigation.css +5 -2
- package/dist/css/rules/breadcrumbs.css +0 -6
- package/dist/css/rules/button.css +4 -12
- package/dist/css/rules/card.css +14 -0
- package/dist/css/rules/chart.css +13 -5
- package/dist/css/rules/choice.css +26 -2
- package/dist/css/rules/combobox.css +17 -16
- package/dist/css/rules/dashboard.css +53 -1
- package/dist/css/rules/date-picker.css +50 -27
- package/dist/css/rules/dialog.css +38 -9
- package/dist/css/rules/disclosure.css +17 -3
- package/dist/css/rules/field-control.css +9 -6
- package/dist/css/rules/file-upload.css +9 -5
- package/dist/css/rules/filter-bar.css +24 -1
- package/dist/css/rules/gauge.css +2 -2
- package/dist/css/rules/icon.css +0 -5
- package/dist/css/rules/input-group.css +23 -8
- package/dist/css/rules/link.css +0 -6
- package/dist/css/rules/menu.css +5 -2
- package/dist/css/rules/menubar.css +1 -1
- package/dist/css/rules/navigation-menu.css +31 -26
- package/dist/css/rules/number-field.css +8 -1
- package/dist/css/rules/otp-field.css +13 -3
- package/dist/css/rules/page-header.css +10 -0
- package/dist/css/rules/panel.css +51 -10
- package/dist/css/rules/popup.css +50 -0
- package/dist/css/rules/preview-card.css +0 -21
- package/dist/css/rules/scroll-area.css +20 -1
- package/dist/css/rules/select.css +4 -1
- package/dist/css/rules/sizing.css +15 -3
- package/dist/css/rules/skeleton.css +1 -1
- package/dist/css/rules/slider.css +16 -8
- package/dist/css/rules/spinner.css +1 -1
- package/dist/css/rules/stat.css +9 -0
- package/dist/css/rules/tabs.css +14 -6
- package/dist/css/rules/textarea.css +28 -0
- package/dist/css/rules/toast.css +3 -1
- package/dist/css/rules/toggle.css +10 -3
- package/dist/css/rules/toolbar.css +10 -3
- package/dist/css/rules/tree.css +29 -4
- package/dist/dashboard.d.ts +19 -7
- package/dist/dashboard.js +30 -5
- package/dist/date-picker.d.ts +32 -3
- package/dist/date-picker.js +43 -4
- package/dist/dialog.d.ts +25 -7
- package/dist/dialog.js +9 -4
- package/dist/drawer.d.ts +10 -2
- package/dist/drawer.js +6 -3
- package/dist/icon.d.ts +5 -5
- package/dist/icon.js +5 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/inline-message.d.ts +2 -2
- package/dist/inline-message.js +7 -2
- package/dist/input.d.ts +2 -2
- package/dist/layout.d.ts +1 -3
- package/dist/layout.js +2 -2
- package/dist/native-select.d.ts +2 -2
- package/dist/navigation-menu.d.ts +16 -0
- package/dist/navigation-menu.js +6 -1
- package/dist/page-header.d.ts +10 -2
- package/dist/page-header.js +3 -3
- package/dist/panel.d.ts +6 -3
- package/dist/panel.js +2 -2
- package/dist/parts.d.ts +7 -3
- package/dist/parts.js +12 -4
- package/dist/popover.d.ts +6 -6
- package/dist/popover.js +3 -2
- package/dist/preview-card.d.ts +2 -8
- package/dist/preview-card.js +2 -2
- package/dist/select.d.ts +7 -4
- package/dist/select.js +23 -8
- package/dist/sheet.d.ts +10 -2
- package/dist/sheet.js +6 -3
- package/dist/size.d.ts +8 -6
- package/dist/size.js +2 -7
- package/dist/sizing.d.ts +13 -6
- package/dist/sizing.js +13 -4
- package/dist/sparkline.d.ts +2 -2
- package/dist/sparkline.js +2 -2
- package/dist/spinner.d.ts +2 -2
- package/dist/spinner.js +2 -2
- package/dist/stat.d.ts +6 -2
- package/dist/stat.js +2 -2
- package/dist/table.d.ts +12 -4
- package/dist/table.js +8 -3
- package/dist/textarea.d.ts +6 -3
- package/dist/textarea.js +2 -2
- package/dist/time-range-picker.d.ts +2 -2
- package/dist/time-range-picker.js +2 -2
- package/dist/toggle.d.ts +2 -2
- package/dist/toggle.js +2 -2
- package/dist/toolbar.d.ts +11 -10
- package/dist/toolbar.js +23 -1
- package/dist/tooltip.d.ts +1 -3
- package/dist/tooltip.js +4 -2
- package/dist/tree.d.ts +1 -3
- package/dist/tree.js +2 -2
- package/dist/typography.d.ts +10 -3
- package/dist/typography.js +4 -3
- package/package.json +3 -3
package/dist/code.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { Button } from './button.js';
|
|
2
3
|
export type CodeBlockProps = Omit<React.ComponentPropsWithoutRef<'div'>, 'children' | 'onCopy'> & {
|
|
3
4
|
code: string;
|
|
4
5
|
label?: string;
|
|
@@ -6,12 +7,6 @@ export type CodeBlockProps = Omit<React.ComponentPropsWithoutRef<'div'>, 'childr
|
|
|
6
7
|
wrap?: boolean;
|
|
7
8
|
/** The copy button's text: the action, then the result. */
|
|
8
9
|
labels?: Partial<CodeBlockLabels>;
|
|
9
|
-
/** @deprecated Use `labels.copy`. Removed in a later minor. */
|
|
10
|
-
copyLabel?: string;
|
|
11
|
-
/** @deprecated Use `labels.copied`. Removed in a later minor. */
|
|
12
|
-
copiedLabel?: string;
|
|
13
|
-
/** @deprecated Use `labels.copyError`. Removed in a later minor. */
|
|
14
|
-
copyErrorLabel?: string;
|
|
15
10
|
onCopy?: (code: string) => void | Promise<void>;
|
|
16
11
|
};
|
|
17
12
|
export type CodeBlockLabels = {
|
|
@@ -19,6 +14,23 @@ export type CodeBlockLabels = {
|
|
|
19
14
|
copied: string;
|
|
20
15
|
copyError: string;
|
|
21
16
|
};
|
|
22
|
-
export
|
|
17
|
+
export type CopyButtonLabels = CodeBlockLabels;
|
|
18
|
+
export type CopyButtonProps = Omit<React.ComponentPropsWithoutRef<typeof Button>, 'children' | 'onClick' | 'onCopy' | 'value'> & {
|
|
19
|
+
/** What to copy: the text, or a function that returns it when pressed. */
|
|
20
|
+
value: string | (() => string | Promise<string>);
|
|
21
|
+
/** The button's text: the action, then the result. */
|
|
22
|
+
labels?: Partial<CopyButtonLabels>;
|
|
23
|
+
/** Copies instead of the clipboard write; throw to report a failure. */
|
|
24
|
+
onCopy?: (text: string) => void | Promise<void>;
|
|
25
|
+
/** Hears each outcome, such as to select the source after a failure. */
|
|
26
|
+
onResult?: (result: 'copied' | 'failed') => void;
|
|
27
|
+
};
|
|
28
|
+
/** Copies text and says what happened in its own label: the result replaces the
|
|
29
|
+
* action in place, as a polite live region, then the action returns after 2s.
|
|
30
|
+
* The button is as wide as its longest label, so nothing around it moves. */
|
|
31
|
+
export declare const CopyButton: ({ value, labels, onCopy, onResult, variant, size, className, ref, ...props }: CopyButtonProps & {
|
|
32
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
33
|
+
}) => React.JSX.Element;
|
|
34
|
+
export declare const CodeBlock: ({ code, label, language, wrap, labels, onCopy, className, ref, ...props }: CodeBlockProps & {
|
|
23
35
|
ref?: React.Ref<HTMLDivElement>;
|
|
24
36
|
}) => React.JSX.Element;
|
package/dist/code.js
CHANGED
|
@@ -2,22 +2,24 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { Icon } from './icon.js';
|
|
5
|
-
import { cx } from './parts.js';
|
|
5
|
+
import { cx, skin } from './parts.js';
|
|
6
6
|
import { Button } from './button.js';
|
|
7
7
|
import { SyntaxHighlighter } from './syntax-highlighter.js';
|
|
8
8
|
import { ScrollArea } from './scroll-area.js';
|
|
9
9
|
const copyIcons = { idle: 'copy', copied: 'check', failed: 'alert' };
|
|
10
|
-
// Feedback stays inside the
|
|
10
|
+
// Feedback stays inside the button and returns to the action after this long.
|
|
11
11
|
const copyFeedbackDuration = 2000;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
/** Copies text and says what happened in its own label: the result replaces the
|
|
13
|
+
* action in place, as a polite live region, then the action returns after 2s.
|
|
14
|
+
* The button is as wide as its longest label, so nothing around it moves. */
|
|
15
|
+
export const CopyButton = function CopyButton({ value, labels, onCopy, onResult, variant = 'outline', size = 'small', className, ref, ...props }) {
|
|
16
|
+
const copyLabel = labels?.copy ?? 'Copy';
|
|
17
|
+
const copiedLabel = labels?.copied ?? 'Copied';
|
|
18
|
+
const copyErrorLabel = labels?.copyError ?? 'Copy unavailable';
|
|
16
19
|
const [state, setState] = React.useState('idle');
|
|
17
20
|
// After the first copy, each change of label fades in; the first render does not.
|
|
18
21
|
const [used, setUsed] = React.useState(false);
|
|
19
22
|
const revision = React.useRef(0), mounted = React.useRef(false), timer = React.useRef(undefined);
|
|
20
|
-
const button = React.useRef(null), source = React.useRef(null);
|
|
21
23
|
React.useEffect(() => {
|
|
22
24
|
mounted.current = true;
|
|
23
25
|
return () => {
|
|
@@ -26,29 +28,13 @@ export const CodeBlock = function CodeBlock({ code, label = 'Code', language, wr
|
|
|
26
28
|
clearTimeout(timer.current);
|
|
27
29
|
};
|
|
28
30
|
}, []);
|
|
31
|
+
// New text clears a result, and a late answer for the old text is dropped.
|
|
32
|
+
const source = typeof value === 'string' ? value : null;
|
|
29
33
|
React.useEffect(() => {
|
|
30
34
|
revision.current++;
|
|
31
35
|
clearTimeout(timer.current);
|
|
32
36
|
setState('idle');
|
|
33
|
-
}, [
|
|
34
|
-
const resting = React.useRef(true);
|
|
35
|
-
resting.current = state === 'idle';
|
|
36
|
-
// The button keeps its resting width, so the feedback never moves the toolbar.
|
|
37
|
-
React.useLayoutEffect(() => {
|
|
38
|
-
const element = button.current;
|
|
39
|
-
if (!element || state !== 'idle')
|
|
40
|
-
return;
|
|
41
|
-
const measure = () => {
|
|
42
|
-
element.style.minInlineSize = '';
|
|
43
|
-
element.style.minInlineSize = `${element.getBoundingClientRect().width}px`;
|
|
44
|
-
};
|
|
45
|
-
measure();
|
|
46
|
-
// Fonts can finish loading after a copy; the result label must not set the width.
|
|
47
|
-
document.fonts?.ready.then(() => {
|
|
48
|
-
if (mounted.current && resting.current)
|
|
49
|
-
measure();
|
|
50
|
-
});
|
|
51
|
-
}, [copyLabel, state]);
|
|
37
|
+
}, [source]);
|
|
52
38
|
async function copy() {
|
|
53
39
|
const request = ++revision.current;
|
|
54
40
|
clearTimeout(timer.current);
|
|
@@ -56,10 +42,11 @@ export const CodeBlock = function CodeBlock({ code, label = 'Code', language, wr
|
|
|
56
42
|
setState('busy');
|
|
57
43
|
let outcome;
|
|
58
44
|
try {
|
|
45
|
+
const text = typeof value === 'string' ? value : await value();
|
|
59
46
|
if (onCopy)
|
|
60
|
-
await onCopy(
|
|
47
|
+
await onCopy(text);
|
|
61
48
|
else
|
|
62
|
-
await navigator.clipboard.writeText(
|
|
49
|
+
await navigator.clipboard.writeText(text);
|
|
63
50
|
outcome = 'copied';
|
|
64
51
|
}
|
|
65
52
|
catch {
|
|
@@ -68,20 +55,27 @@ export const CodeBlock = function CodeBlock({ code, label = 'Code', language, wr
|
|
|
68
55
|
if (!mounted.current || request !== revision.current)
|
|
69
56
|
return;
|
|
70
57
|
setState(outcome);
|
|
71
|
-
|
|
72
|
-
if (outcome === 'failed' && source.current)
|
|
73
|
-
window.getSelection()?.selectAllChildren(source.current);
|
|
58
|
+
onResult?.(outcome);
|
|
74
59
|
timer.current = setTimeout(() => {
|
|
75
60
|
if (mounted.current && request === revision.current)
|
|
76
61
|
setState('idle');
|
|
77
62
|
}, copyFeedbackDuration);
|
|
78
63
|
}
|
|
79
64
|
const icon = state === 'copied' || state === 'failed' ? state : 'idle';
|
|
80
|
-
return (_jsxs(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
65
|
+
return (_jsxs(Button, { ...props, ref: ref, type: 'button', variant: variant, size: size, className: skin('surf-copy-button', className), onClick: copy,
|
|
66
|
+
// A pending copy blocks a second start but keeps focus on the button.
|
|
67
|
+
disabled: state === 'busy', focusableWhenDisabled: true, "aria-busy": state === 'busy' || undefined, "data-copy-state": icon === 'idle' ? undefined : icon, "data-copy-used": used || undefined, children: [_jsx(Icon, { name: copyIcons[icon], className: 'surf-copy-icon' }, icon), _jsxs("span", { className: 'surf-copy-text', children: [_jsx("span", { "aria-live": 'polite', children: _jsx("span", { className: 'surf-copy-label', children: state === 'copied'
|
|
68
|
+
? copiedLabel
|
|
69
|
+
: state === 'failed'
|
|
70
|
+
? copyErrorLabel
|
|
71
|
+
: copyLabel }, icon) }), [copyLabel, copiedLabel, copyErrorLabel].map((label, index) => (_jsx("span", { className: 'surf-copy-room', "data-label": label, "aria-hidden": 'true' }, index)))] })] }));
|
|
72
|
+
};
|
|
73
|
+
export const CodeBlock = function CodeBlock({ code, label = 'Code', language, wrap = false, labels, onCopy, className, ref, ...props }) {
|
|
74
|
+
const source = React.useRef(null);
|
|
75
|
+
return (_jsxs("div", { ...props, ref: ref, "data-wrap": wrap || undefined, className: cx('surf-code-block', className), children: [_jsxs("div", { className: 'surf-code-toolbar', children: [_jsxs("span", { className: 'surf-code-label', children: [label, language && _jsx("span", { className: 'surf-code-language', children: language })] }), _jsx(CopyButton, { className: 'surf-code-copy', value: code, onCopy: onCopy, labels: { ...labels, copy: labels?.copy ?? 'Copy code' },
|
|
76
|
+
// A failed copy selects the source, so the keyboard copy shortcut works next.
|
|
77
|
+
onResult: (result) => {
|
|
78
|
+
if (result === 'failed' && source.current)
|
|
79
|
+
window.getSelection()?.selectAllChildren(source.current);
|
|
80
|
+
} })] }), _jsx(ScrollArea, { label: label, orientation: 'horizontal', children: _jsx("pre", { ref: source, className: 'surf-code-text', children: _jsx(SyntaxHighlighter, { code: code, language: language, inline: true }) }) })] }));
|
|
87
81
|
};
|
package/dist/combobox.d.ts
CHANGED
|
@@ -4,10 +4,7 @@ declare function ComboboxRoot<Value, Multiple extends boolean | undefined = fals
|
|
|
4
4
|
export declare const Combobox: {
|
|
5
5
|
Root: typeof ComboboxRoot;
|
|
6
6
|
Portal: React.ForwardRefExoticComponent<Omit<import("@base-ui/react/combobox").ComboboxPortalProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
7
|
-
Content: ({ side, align, sideOffset, collisionPadding,
|
|
8
|
-
/** @deprecated Pass side, align, sideOffset and collisionPadding directly. */
|
|
9
|
-
positionerProps?: React.ComponentPropsWithoutRef<typeof BaseCombobox.Positioner>;
|
|
10
|
-
} & {
|
|
7
|
+
Content: ({ side, align, sideOffset, collisionPadding, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseCombobox.Popup> & Pick<React.ComponentPropsWithoutRef<typeof BaseCombobox.Positioner>, "side" | "align" | "sideOffset" | "collisionPadding"> & {
|
|
11
8
|
ref?: React.Ref<HTMLDivElement>;
|
|
12
9
|
}) => React.JSX.Element;
|
|
13
10
|
Popup: ({ ref, ...props }: Omit<Omit<Omit<import("@base-ui/react/combobox").ComboboxPopupProps, "ref"> & React.RefAttributes<HTMLDivElement>, "ref">, "size"> & {
|
|
@@ -159,9 +156,6 @@ export declare const ComboboxArrow: React.ForwardRefExoticComponent<Omit<import(
|
|
|
159
156
|
ref?: React.Ref<HTMLDivElement> | undefined;
|
|
160
157
|
}) => React.JSX.Element, ComboboxTrigger: ({ children, className, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseCombobox.Trigger> & {
|
|
161
158
|
ref?: React.Ref<HTMLButtonElement>;
|
|
162
|
-
}) => React.JSX.Element, ComboboxValue: typeof BaseCombobox.Value, ComboboxContent: ({ side, align, sideOffset, collisionPadding,
|
|
163
|
-
/** @deprecated Pass side, align, sideOffset and collisionPadding directly. */
|
|
164
|
-
positionerProps?: React.ComponentPropsWithoutRef<typeof BaseCombobox.Positioner>;
|
|
165
|
-
} & {
|
|
159
|
+
}) => React.JSX.Element, ComboboxValue: typeof BaseCombobox.Value, ComboboxContent: ({ side, align, sideOffset, collisionPadding, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseCombobox.Popup> & Pick<React.ComponentPropsWithoutRef<typeof BaseCombobox.Positioner>, "side" | "align" | "sideOffset" | "collisionPadding"> & {
|
|
166
160
|
ref?: React.Ref<HTMLDivElement>;
|
|
167
161
|
}) => React.JSX.Element;
|
package/dist/combobox.js
CHANGED
|
@@ -14,8 +14,8 @@ function ComboboxRoot({ children, form, ...props }) {
|
|
|
14
14
|
}
|
|
15
15
|
const ComboPopup = /* @__PURE__ */ part(BaseCombobox.Popup, 'surf-combobox-popup', { portal: true });
|
|
16
16
|
const ComboPositioner = /* @__PURE__ */ part(BaseCombobox.Positioner, 'surf-positioner', { portal: true });
|
|
17
|
-
const ComboContent = function ComboContent({ side, align, sideOffset, collisionPadding,
|
|
18
|
-
return (_jsx(OwnPortal, { portal: BaseCombobox.Portal, children: _jsx(ComboPositioner, { sideOffset: 6, ...
|
|
17
|
+
const ComboContent = function ComboContent({ side, align, sideOffset, collisionPadding, ref, ...props }) {
|
|
18
|
+
return (_jsx(OwnPortal, { portal: BaseCombobox.Portal, children: _jsx(ComboPositioner, { sideOffset: 6, ...defined({ side, align, sideOffset, collisionPadding }), children: _jsx(ComboPopup, { ...props, ref: ref }) }) }));
|
|
19
19
|
};
|
|
20
20
|
const ComboTrigger = function ComboTrigger({ children, className, ref, ...props }) {
|
|
21
21
|
return (_jsx(BaseCombobox.Trigger, { "aria-label": 'Show options', ...props, ref: ref, className: skin('surf-combobox-action', className), children: children ?? _jsx(Icon, { name: 'chevron-down' }) }));
|
package/dist/context-menu.d.ts
CHANGED
|
@@ -14,10 +14,7 @@ export declare const ContextMenu: {
|
|
|
14
14
|
} & {
|
|
15
15
|
ref?: React.Ref<HTMLDivElement> | undefined;
|
|
16
16
|
}) => React.JSX.Element;
|
|
17
|
-
Content: ({ side, align, sideOffset, collisionPadding,
|
|
18
|
-
/** @deprecated Pass side, align, sideOffset and collisionPadding directly. */
|
|
19
|
-
positionerProps?: React.ComponentPropsWithoutRef<typeof BaseContextMenu.Positioner>;
|
|
20
|
-
} & {
|
|
17
|
+
Content: ({ side, align, sideOffset, collisionPadding, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseContextMenu.Popup> & Placement<typeof BaseContextMenu.Positioner> & {
|
|
21
18
|
ref?: React.Ref<HTMLDivElement>;
|
|
22
19
|
}) => React.JSX.Element;
|
|
23
20
|
Popup: ({ ref, ...props }: Omit<Omit<Omit<import("@base-ui/react/context-menu").ContextMenuPopupProps, "ref"> & React.RefAttributes<HTMLDivElement>, "ref">, "size"> & {
|
|
@@ -117,9 +114,6 @@ export declare const ContextMenuArrow: React.ForwardRefExoticComponent<Omit<impo
|
|
|
117
114
|
size?: import("./size.js").ComponentSize;
|
|
118
115
|
} & {
|
|
119
116
|
ref?: React.Ref<HTMLDivElement> | undefined;
|
|
120
|
-
}) => React.JSX.Element, ContextMenuContent: ({ side, align, sideOffset, collisionPadding,
|
|
121
|
-
/** @deprecated Pass side, align, sideOffset and collisionPadding directly. */
|
|
122
|
-
positionerProps?: React.ComponentPropsWithoutRef<typeof BaseContextMenu.Positioner>;
|
|
123
|
-
} & {
|
|
117
|
+
}) => React.JSX.Element, ContextMenuContent: ({ side, align, sideOffset, collisionPadding, ref, ...props }: React.ComponentPropsWithoutRef<typeof BaseContextMenu.Popup> & Placement<typeof BaseContextMenu.Positioner> & {
|
|
124
118
|
ref?: React.Ref<HTMLDivElement>;
|
|
125
119
|
}) => React.JSX.Element;
|
package/dist/context-menu.js
CHANGED
|
@@ -4,8 +4,8 @@ import { ContextMenu as BaseContextMenu } from '@base-ui/react/context-menu';
|
|
|
4
4
|
import { Menu } from './menu.js';
|
|
5
5
|
import { OwnPortal, defined, part, scopedPortal, } from './parts.js';
|
|
6
6
|
const ContextPositioner = /* @__PURE__ */ part(BaseContextMenu.Positioner, 'surf-positioner', { portal: true });
|
|
7
|
-
const ContextContent = function ContextContent({ side, align, sideOffset, collisionPadding,
|
|
8
|
-
return (_jsx(OwnPortal, { portal: BaseContextMenu.Portal, children: _jsx(ContextPositioner, { ...
|
|
7
|
+
const ContextContent = function ContextContent({ side, align, sideOffset, collisionPadding, ref, ...props }) {
|
|
8
|
+
return (_jsx(OwnPortal, { portal: BaseContextMenu.Portal, children: _jsx(ContextPositioner, { ...defined({ side, align, sideOffset, collisionPadding }), children: _jsx(Menu.Popup, { ...props, ref: ref }) }) }));
|
|
9
9
|
};
|
|
10
10
|
/** Same menu anatomy and styling, with the context-specific cursor positioner. */
|
|
11
11
|
export const ContextMenu = {
|
|
@@ -6,15 +6,17 @@
|
|
|
6
6
|
.surf-confirm-content {
|
|
7
7
|
--surf-dialog-width: var(--surf-size-dialog-small);
|
|
8
8
|
}
|
|
9
|
+
/* A confirmation is one short message: its actions sit on the same plane. */
|
|
9
10
|
.surf-confirm-content .surf-dialog-footer {
|
|
10
|
-
border-top: 0;
|
|
11
11
|
padding-top: var(--surf-space-2);
|
|
12
|
+
background: none;
|
|
13
|
+
--surf-plane: var(--surf-canvas);
|
|
14
|
+
--surf-recessed: var(--surf-surface);
|
|
12
15
|
}
|
|
13
16
|
}
|
|
14
17
|
@layer surflane.sizes {
|
|
15
18
|
.surf-confirm-content .surf-dialog-header {
|
|
16
19
|
padding-inline-end: var(--surf-space-6);
|
|
17
|
-
border-bottom: 0;
|
|
18
20
|
}
|
|
19
21
|
:where([data-ui-size], [data-ui-size] *).surf-confirm-content
|
|
20
22
|
.surf-dialog-header {
|
|
@@ -16,13 +16,9 @@
|
|
|
16
16
|
that frames it and clips it. Only the page surface inside it is rounded. */
|
|
17
17
|
.surf-app-shell {
|
|
18
18
|
--surf-app-shell-inset: var(--surf-space-3);
|
|
19
|
-
/* --app-shell-radius, if a caller still sets it, keeps deriving the page radius. */
|
|
20
19
|
--surf-app-shell-page-radius: max(
|
|
21
20
|
0px,
|
|
22
|
-
calc(
|
|
23
|
-
var(--app-shell-radius, var(--surf-radius-sheet)) -
|
|
24
|
-
var(--surf-app-shell-inset)
|
|
25
|
-
)
|
|
21
|
+
calc(var(--surf-radius-sheet) - var(--surf-app-shell-inset))
|
|
26
22
|
);
|
|
27
23
|
--surf-app-shell-sidebar-width: 216px;
|
|
28
24
|
/* The collapsed sidebar's width. Set it on the shell for a narrower rail. */
|
|
@@ -34,6 +30,8 @@
|
|
|
34
30
|
min-inline-size: 0;
|
|
35
31
|
padding: var(--surf-app-shell-inset);
|
|
36
32
|
background: var(--surf-chrome);
|
|
33
|
+
--surf-plane: var(--surf-chrome);
|
|
34
|
+
--surf-recessed: var(--surf-canvas);
|
|
37
35
|
}
|
|
38
36
|
.surf-app-shell[data-sidebar='collapsed'] {
|
|
39
37
|
--surf-app-shell-sidebar-width: var(--surf-app-shell-rail-width);
|
|
@@ -61,6 +59,8 @@
|
|
|
61
59
|
background: var(--surf-canvas);
|
|
62
60
|
border-radius: var(--surf-app-shell-page-radius);
|
|
63
61
|
overflow-wrap: anywhere;
|
|
62
|
+
--surf-plane: var(--surf-canvas);
|
|
63
|
+
--surf-recessed: var(--surf-surface);
|
|
64
64
|
}
|
|
65
65
|
.surf-app-shell-footer {
|
|
66
66
|
margin-block-start: auto;
|
|
@@ -88,13 +88,19 @@
|
|
|
88
88
|
transition: background-color var(--surf-duration-fast) var(--surf-ease-out);
|
|
89
89
|
}
|
|
90
90
|
.surf-app-shell-link:hover {
|
|
91
|
-
background:
|
|
91
|
+
background-image: linear-gradient(
|
|
92
|
+
var(--surf-interaction-hover-bg),
|
|
93
|
+
var(--surf-interaction-hover-bg)
|
|
94
|
+
);
|
|
92
95
|
}
|
|
93
96
|
.surf-app-shell-link:active:not([aria-current='page']) {
|
|
94
|
-
background:
|
|
97
|
+
background-image: linear-gradient(
|
|
98
|
+
var(--surf-interaction-active-bg),
|
|
99
|
+
var(--surf-interaction-active-bg)
|
|
100
|
+
);
|
|
95
101
|
}
|
|
96
102
|
.surf-app-shell-link[aria-current='page'] {
|
|
97
|
-
background: var(--surf-selected);
|
|
103
|
+
background-color: var(--surf-selected);
|
|
98
104
|
color: var(--surf-ink);
|
|
99
105
|
font-weight: var(--surf-weight-medium);
|
|
100
106
|
}
|
|
@@ -233,7 +239,9 @@
|
|
|
233
239
|
}
|
|
234
240
|
@media (forced-colors: active) {
|
|
235
241
|
.surf-app-shell-link[aria-current='page'] {
|
|
236
|
-
|
|
242
|
+
background-color: Highlight;
|
|
243
|
+
color: HighlightText;
|
|
244
|
+
forced-color-adjust: none;
|
|
237
245
|
}
|
|
238
246
|
}
|
|
239
247
|
/* Workspace variant: product sidebar owns the brand; the content scrollport is caller-provided. */
|
|
@@ -277,7 +285,7 @@
|
|
|
277
285
|
position: absolute;
|
|
278
286
|
inset-block-start: var(--surf-space-5);
|
|
279
287
|
inset-inline-end: calc(var(--surf-app-shell-toggle-size) / -2);
|
|
280
|
-
border: 1px solid var(--surf-
|
|
288
|
+
border: 1px solid var(--surf-control-border);
|
|
281
289
|
background: var(--surf-canvas);
|
|
282
290
|
z-index: 1;
|
|
283
291
|
}
|
|
@@ -291,6 +299,25 @@
|
|
|
291
299
|
border-color: transparent;
|
|
292
300
|
background: transparent;
|
|
293
301
|
}
|
|
302
|
+
/* On touch the toggle grows to the target size, expanded or collapsed. */
|
|
303
|
+
@media (pointer: coarse) {
|
|
304
|
+
.surf-app-shell[data-layout='workspace']
|
|
305
|
+
> .surf-app-shell-sidebar
|
|
306
|
+
> .surf-app-shell-toggle {
|
|
307
|
+
--surf-app-shell-toggle-size: max(
|
|
308
|
+
var(--surf-space-8),
|
|
309
|
+
var(--surf-control-target-size)
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
.surf-app-shell[data-layout='workspace'][data-sidebar='collapsed']
|
|
313
|
+
> .surf-app-shell-sidebar
|
|
314
|
+
> .surf-app-shell-toggle {
|
|
315
|
+
--surf-app-shell-toggle-size: max(
|
|
316
|
+
var(--surf-space-10),
|
|
317
|
+
var(--surf-control-target-size)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
294
321
|
/* A ScrollArea in the sidebar takes the room between the brand and the footer, and
|
|
295
322
|
* one in the content fills it, so the menu and the page scroll on their own. */
|
|
296
323
|
.surf-app-shell[data-layout='workspace']
|
|
@@ -350,6 +377,8 @@
|
|
|
350
377
|
padding: var(--surf-space-6);
|
|
351
378
|
background: var(--surf-canvas);
|
|
352
379
|
border-radius: var(--surf-app-shell-page-radius);
|
|
380
|
+
--surf-plane: var(--surf-canvas);
|
|
381
|
+
--surf-recessed: var(--surf-surface);
|
|
353
382
|
}
|
|
354
383
|
/* On a phone the page meets the screen's edges, below the mobile header. */
|
|
355
384
|
@media (max-width: 760px) {
|
|
@@ -401,6 +430,14 @@
|
|
|
401
430
|
}
|
|
402
431
|
}
|
|
403
432
|
@layer surflane.sizes {
|
|
433
|
+
/* Compact density: the page and the content keep the 12px the blocks inside keep,
|
|
434
|
+
on a phone too. */
|
|
435
|
+
:where([data-ui-density='compact'], [data-ui-density='compact'] *):is(
|
|
436
|
+
.surf-app-shell-page,
|
|
437
|
+
.surf-app-shell-content
|
|
438
|
+
) {
|
|
439
|
+
padding: var(--surf-ui-density-gap);
|
|
440
|
+
}
|
|
404
441
|
/* A row rendered as a Button, such as Menu.Trigger, keeps the row's shape and the
|
|
405
442
|
* sidebar's text over the button's size scheme. */
|
|
406
443
|
.surf-app-shell-link.surf-button {
|
|
@@ -63,12 +63,12 @@
|
|
|
63
63
|
font-size: var(--surf-type-caption-size);
|
|
64
64
|
line-height: var(--surf-type-caption-leading);
|
|
65
65
|
}
|
|
66
|
-
/* The pair Progress and Slider use: the brand mark on the
|
|
66
|
+
/* The pair Progress and Slider use: the brand mark on the recessed step. */
|
|
67
67
|
.surf-bar-list-bar {
|
|
68
68
|
display: block;
|
|
69
69
|
block-size: var(--surf-space-2);
|
|
70
70
|
border-radius: var(--surf-radius-full);
|
|
71
|
-
background: var(--surf-
|
|
71
|
+
background: var(--surf-recessed);
|
|
72
72
|
overflow: hidden;
|
|
73
73
|
}
|
|
74
74
|
.surf-bar-list-fill {
|
package/dist/css/rules/bar.css
CHANGED
|
@@ -20,19 +20,12 @@
|
|
|
20
20
|
width: fit-content;
|
|
21
21
|
max-width: 100%;
|
|
22
22
|
}
|
|
23
|
-
.
|
|
24
|
-
.surf-toolbar-button {
|
|
25
|
-
position: relative;
|
|
26
|
-
}
|
|
23
|
+
/* A toolbar or menubar is a recessed plane; forced colors draw its edge. */
|
|
27
24
|
@media (forced-colors: active) {
|
|
28
|
-
.surf-
|
|
29
|
-
.surf-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
inset: 3px;
|
|
33
|
-
border: 1px solid CanvasText;
|
|
34
|
-
border-radius: inherit;
|
|
35
|
-
pointer-events: none;
|
|
25
|
+
.surf-toolbar,
|
|
26
|
+
.surf-menubar {
|
|
27
|
+
outline: 1px solid CanvasText;
|
|
28
|
+
outline-offset: -1px;
|
|
36
29
|
}
|
|
37
30
|
}
|
|
38
31
|
}
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
color: var(--surf-ink);
|
|
76
76
|
}
|
|
77
77
|
.surf-bottom-nav-item[aria-current='page'] .surf-bottom-nav-icon {
|
|
78
|
-
background: var(--surf-selected);
|
|
78
|
+
background-color: var(--surf-selected);
|
|
79
79
|
}
|
|
80
80
|
@media (hover: hover) {
|
|
81
81
|
.surf-bottom-nav-item:hover .surf-bottom-nav-icon {
|
|
@@ -150,10 +150,13 @@
|
|
|
150
150
|
border-block-start-color: CanvasText;
|
|
151
151
|
}
|
|
152
152
|
.surf-bottom-nav-item[aria-current='page'] .surf-bottom-nav-icon {
|
|
153
|
-
|
|
153
|
+
background-color: Highlight;
|
|
154
|
+
color: HighlightText;
|
|
155
|
+
forced-color-adjust: none;
|
|
154
156
|
}
|
|
155
157
|
.surf-bottom-nav-badge {
|
|
156
158
|
border: 1px solid CanvasText;
|
|
159
|
+
forced-color-adjust: auto;
|
|
157
160
|
}
|
|
158
161
|
}
|
|
159
162
|
}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
/* Breadcrumbs, with the styles of what it renders. */
|
|
2
2
|
@layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
|
|
3
3
|
surflane.sizes;
|
|
4
|
-
|
|
5
|
-
@layer surflane.sizes {
|
|
6
|
-
:where([data-ui-size], [data-ui-size] *):is(.surf-breadcrumbs) {
|
|
7
|
-
font-size: var(--surf-ui-font);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
text-decoration: none;
|
|
25
25
|
cursor: pointer;
|
|
26
26
|
}
|
|
27
|
-
.surf-button
|
|
27
|
+
.surf-button[data-variant='primary'] {
|
|
28
28
|
/* On the brand fill the layer is its ink: light ink barely moves bright cyan. */
|
|
29
29
|
--surf-interaction-hover-bg: oklch(from var(--surf-on-brand) l c h / 0.08);
|
|
30
30
|
--surf-interaction-active-bg: oklch(from var(--surf-on-brand) l c h / 0.14);
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
color: var(--surf-on-brand);
|
|
34
34
|
}
|
|
35
35
|
.surf-button[data-variant='outline'] {
|
|
36
|
-
border-color: var(--surf-
|
|
36
|
+
border-color: var(--surf-control-border);
|
|
37
37
|
background: transparent;
|
|
38
38
|
}
|
|
39
39
|
.surf-button[data-variant='ghost'] {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
.surf-button[data-variant='secondary'] {
|
|
69
|
-
background: var(--surf-selected);
|
|
69
|
+
background-color: var(--surf-selected);
|
|
70
70
|
color: var(--surf-ink);
|
|
71
71
|
}
|
|
72
72
|
.surf-button[data-variant='destructive'] {
|
|
@@ -100,14 +100,6 @@
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
@layer surflane.sizes {
|
|
103
|
-
.surf-button[data-size='xs'] {
|
|
104
|
-
height: 24px;
|
|
105
|
-
min-height: 24px;
|
|
106
|
-
padding-inline: var(--surf-space-2);
|
|
107
|
-
font-size: var(--surf-type-caption-size);
|
|
108
|
-
letter-spacing: var(--surf-type-caption-tracking);
|
|
109
|
-
gap: var(--surf-space-1);
|
|
110
|
-
}
|
|
111
103
|
.surf-button[data-size^='icon'] {
|
|
112
104
|
width: var(--surf-control-height);
|
|
113
105
|
padding: 0;
|
|
@@ -117,7 +109,7 @@
|
|
|
117
109
|
min-height: var(--surf-control-height);
|
|
118
110
|
padding-inline: var(--surf-ui-button-padding);
|
|
119
111
|
font-size: var(--surf-ui-font);
|
|
120
|
-
line-height: var(--surf-ui-leading);
|
|
112
|
+
line-height: var(--surf-ui-label-leading);
|
|
121
113
|
}
|
|
122
114
|
:where([data-ui-size], [data-ui-size] *).surf-button[data-size^='icon'] {
|
|
123
115
|
inline-size: var(--surf-control-height);
|
package/dist/css/rules/card.css
CHANGED
|
@@ -12,4 +12,18 @@
|
|
|
12
12
|
line-height: var(--surf-ui-title-leading);
|
|
13
13
|
letter-spacing: var(--surf-ui-title-tracking);
|
|
14
14
|
}
|
|
15
|
+
/* Compact density, after any size: a card pads 12px, keeps 8px between its parts
|
|
16
|
+
and titles itself with the label role, as a dense panel needs. */
|
|
17
|
+
:where([data-ui-density='compact'], [data-ui-density='compact'] *).surf-card {
|
|
18
|
+
padding: var(--surf-surface-padding, var(--surf-ui-density-gap));
|
|
19
|
+
gap: var(--surf-ui-density-inset);
|
|
20
|
+
}
|
|
21
|
+
:where(
|
|
22
|
+
[data-ui-density='compact'],
|
|
23
|
+
[data-ui-density='compact'] *
|
|
24
|
+
).surf-card-title {
|
|
25
|
+
font-size: var(--surf-type-label-size);
|
|
26
|
+
line-height: var(--surf-type-label-leading);
|
|
27
|
+
letter-spacing: var(--surf-type-label-tracking);
|
|
28
|
+
}
|
|
15
29
|
}
|
package/dist/css/rules/chart.css
CHANGED
|
@@ -37,12 +37,14 @@
|
|
|
37
37
|
align-items: center;
|
|
38
38
|
gap: 0 var(--surf-space-4);
|
|
39
39
|
}
|
|
40
|
+
/* The legend takes the inline end of the key: the right, or the left right to left. */
|
|
40
41
|
.surf-chart-legend {
|
|
41
42
|
display: flex;
|
|
42
43
|
flex-wrap: wrap;
|
|
43
44
|
gap: var(--surf-space-1) var(--surf-space-2);
|
|
44
45
|
padding: 0;
|
|
45
|
-
margin: 0
|
|
46
|
+
margin: 0;
|
|
47
|
+
margin-inline-start: auto;
|
|
46
48
|
list-style: none;
|
|
47
49
|
}
|
|
48
50
|
.surf-chart-mark {
|
|
@@ -103,10 +105,16 @@
|
|
|
103
105
|
overflow-wrap: anywhere;
|
|
104
106
|
}
|
|
105
107
|
.surf-chart-legend-button:hover {
|
|
106
|
-
background:
|
|
108
|
+
background-image: linear-gradient(
|
|
109
|
+
var(--surf-interaction-hover-bg),
|
|
110
|
+
var(--surf-interaction-hover-bg)
|
|
111
|
+
);
|
|
107
112
|
}
|
|
108
113
|
.surf-chart-legend-button:active {
|
|
109
|
-
background:
|
|
114
|
+
background-image: linear-gradient(
|
|
115
|
+
var(--surf-interaction-active-bg),
|
|
116
|
+
var(--surf-interaction-active-bg)
|
|
117
|
+
);
|
|
110
118
|
}
|
|
111
119
|
.surf-chart-legend-button[aria-pressed='false'] span {
|
|
112
120
|
text-decoration: line-through;
|
|
@@ -211,7 +219,7 @@
|
|
|
211
219
|
box-sizing: border-box;
|
|
212
220
|
min-height: 100px;
|
|
213
221
|
padding: var(--surf-space-3) var(--surf-space-4);
|
|
214
|
-
background: var(--surf-
|
|
222
|
+
background: var(--surf-recessed);
|
|
215
223
|
color: var(--surf-ink-body);
|
|
216
224
|
border-radius: var(--surf-radius-md);
|
|
217
225
|
font: var(--surf-weight-regular)
|
|
@@ -240,7 +248,7 @@
|
|
|
240
248
|
gap: var(--surf-space-3);
|
|
241
249
|
min-height: 160px;
|
|
242
250
|
padding: var(--surf-space-4);
|
|
243
|
-
background: var(--surf-
|
|
251
|
+
background: var(--surf-recessed);
|
|
244
252
|
color: var(--surf-ink-muted);
|
|
245
253
|
border-radius: var(--surf-radius-md);
|
|
246
254
|
}
|
|
@@ -39,7 +39,10 @@
|
|
|
39
39
|
.surf-radio:hover:not([data-disabled], [data-readonly]),
|
|
40
40
|
.surf-switch:hover:not([data-disabled], [data-readonly]) {
|
|
41
41
|
border-color: var(--surf-control-choice-hover-border);
|
|
42
|
-
background:
|
|
42
|
+
background-image: linear-gradient(
|
|
43
|
+
var(--surf-interaction-hover-bg),
|
|
44
|
+
var(--surf-interaction-hover-bg)
|
|
45
|
+
);
|
|
43
46
|
}
|
|
44
47
|
.surf-checkbox:is([data-checked], [data-indeterminate]),
|
|
45
48
|
.surf-radio[data-checked],
|
|
@@ -54,7 +57,10 @@
|
|
|
54
57
|
[data-disabled],
|
|
55
58
|
[data-readonly]
|
|
56
59
|
) {
|
|
57
|
-
background:
|
|
60
|
+
background-image: linear-gradient(
|
|
61
|
+
var(--surf-interaction-hover-bg),
|
|
62
|
+
var(--surf-interaction-hover-bg)
|
|
63
|
+
);
|
|
58
64
|
border-color: var(--surf-control-choice-hover-border);
|
|
59
65
|
}
|
|
60
66
|
:is(.surf-checkbox, .surf-radio, .surf-switch):is(
|
|
@@ -103,6 +109,24 @@
|
|
|
103
109
|
) {
|
|
104
110
|
border-color: var(--surf-status-danger-fg);
|
|
105
111
|
}
|
|
112
|
+
/* On touch and narrow screens a ChoiceLabel makes the whole row the target. A
|
|
113
|
+
choice without one, such as a table row's checkbox, keeps its size and reaches
|
|
114
|
+
--surf-control-target-size around it instead. */
|
|
115
|
+
@media (max-width: 760px), (pointer: coarse) {
|
|
116
|
+
:is(.surf-checkbox, .surf-radio, .surf-switch):not(.surf-choice-label *) {
|
|
117
|
+
position: relative;
|
|
118
|
+
}
|
|
119
|
+
:is(.surf-checkbox, .surf-radio, .surf-switch):not(
|
|
120
|
+
.surf-choice-label *
|
|
121
|
+
)::after {
|
|
122
|
+
content: '';
|
|
123
|
+
position: absolute;
|
|
124
|
+
inset: 50% auto auto 50%;
|
|
125
|
+
inline-size: max(100%, var(--surf-control-target-size));
|
|
126
|
+
block-size: max(100%, var(--surf-control-target-size));
|
|
127
|
+
translate: -50% -50%;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
106
130
|
@media (forced-colors: active) {
|
|
107
131
|
.surf-checkbox,
|
|
108
132
|
.surf-radio,
|