@surflane/ui 0.11.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/app-shell.d.ts +10 -0
- package/dist/app-shell.js +7 -1
- package/dist/autocomplete.d.ts +2 -6
- package/dist/avatar.d.ts +7 -7
- package/dist/avatar.js +2 -2
- package/dist/bar-list.d.ts +31 -0
- package/dist/bar-list.js +31 -0
- package/dist/bottom-navigation.d.ts +6 -2
- package/dist/bottom-navigation.js +2 -2
- 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 +58 -3
- package/dist/chart.js +330 -48
- 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/bar-list.css +9 -0
- package/dist/css/chart.css +2 -0
- package/dist/css/dashboard.css +20 -0
- package/dist/css/rules/alert-dialog.css +8 -2
- package/dist/css/rules/app-shell.css +57 -10
- package/dist/css/rules/bar-list.css +101 -0
- package/dist/css/rules/bar.css +5 -12
- package/dist/css/rules/bottom-navigation.css +21 -2
- package/dist/css/rules/breadcrumbs.css +0 -6
- package/dist/css/rules/button.css +4 -12
- package/dist/css/rules/card.css +19 -0
- package/dist/css/rules/chart.css +87 -8
- package/dist/css/rules/choice.css +26 -2
- package/dist/css/rules/combobox.css +17 -16
- package/dist/css/rules/dashboard.css +118 -0
- package/dist/css/rules/date-picker.css +50 -27
- package/dist/css/rules/dialog.css +47 -7
- 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 +32 -2
- 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 +59 -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 +29 -2
- package/dist/css/rules/sizing.css +25 -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 +66 -0
- package/dist/css/rules/tabs.css +14 -6
- package/dist/css/rules/textarea.css +28 -0
- package/dist/css/rules/time-range-picker.css +14 -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/css/styles.css +3 -0
- package/dist/css/time-range-picker.css +14 -0
- package/dist/dashboard.d.ts +58 -0
- package/dist/dashboard.js +60 -0
- 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 +15 -5
- package/dist/icon.js +16 -3
- package/dist/index.d.ts +9 -6
- package/dist/index.js +7 -4
- 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 +38 -8
- package/dist/select.js +110 -22
- 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 +24 -0
- package/dist/stat.js +15 -1
- 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 +42 -0
- package/dist/time-range-picker.js +69 -0
- 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 +18 -3
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 = {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* Bar list, with the styles of what it renders. */
|
|
2
|
+
@layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
|
|
3
|
+
surflane.sizes;
|
|
4
|
+
@import '@surflane/design-tokens/tokens.css';
|
|
5
|
+
@import '@surflane/css/presentation/base.css';
|
|
6
|
+
@import './rules/base.css';
|
|
7
|
+
@import './rules/sizing.css';
|
|
8
|
+
@import './rules/scroll-area.css';
|
|
9
|
+
@import './rules/bar-list.css';
|
package/dist/css/chart.css
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
@import '@surflane/css/presentation/base.css';
|
|
6
6
|
@import './rules/base.css';
|
|
7
7
|
@import './rules/sizing.css';
|
|
8
|
+
@import './rules/popup.css';
|
|
8
9
|
@import './rules/icon.css';
|
|
10
|
+
@import './rules/popover.css';
|
|
9
11
|
@import './rules/button.css';
|
|
10
12
|
@import './rules/scroll-area.css';
|
|
11
13
|
@import '@surflane/css/presentation/table.css';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* Dashboard, with the styles of what it renders. */
|
|
2
|
+
@layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
|
|
3
|
+
surflane.sizes;
|
|
4
|
+
@import '@surflane/design-tokens/tokens.css';
|
|
5
|
+
@import '@surflane/css/presentation/base.css';
|
|
6
|
+
@import './rules/base.css';
|
|
7
|
+
@import './rules/sizing.css';
|
|
8
|
+
@import './rules/popup.css';
|
|
9
|
+
@import './rules/icon.css';
|
|
10
|
+
@import './rules/popover.css';
|
|
11
|
+
@import './rules/button.css';
|
|
12
|
+
@import './rules/spinner.css';
|
|
13
|
+
@import './rules/skeleton.css';
|
|
14
|
+
@import '@surflane/css/foundations/layout.css';
|
|
15
|
+
@import './rules/layout.css';
|
|
16
|
+
@import '@surflane/css/presentation/card.css';
|
|
17
|
+
@import './rules/card.css';
|
|
18
|
+
@import '@surflane/css/presentation/inline-message.css';
|
|
19
|
+
@import './rules/inline-message.css';
|
|
20
|
+
@import './rules/dashboard.css';
|
|
@@ -6,18 +6,24 @@
|
|
|
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 {
|
|
21
23
|
padding-inline-end: var(--surf-ui-card-padding);
|
|
22
24
|
}
|
|
25
|
+
:where([data-ui-size], [data-ui-size] *).surf-confirm-content
|
|
26
|
+
.surf-dialog-footer {
|
|
27
|
+
padding-top: var(--surf-space-2);
|
|
28
|
+
}
|
|
23
29
|
}
|
|
@@ -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
|
}
|
|
@@ -211,6 +217,16 @@
|
|
|
211
217
|
border-radius: 0;
|
|
212
218
|
}
|
|
213
219
|
}
|
|
220
|
+
/* The menu trigger shows only where the sidebar hides, at the header's end. */
|
|
221
|
+
.surf-app-shell-menu-trigger.surf-button {
|
|
222
|
+
display: none;
|
|
223
|
+
}
|
|
224
|
+
@media (max-width: 760px) {
|
|
225
|
+
.surf-app-shell-menu-trigger.surf-button {
|
|
226
|
+
display: inline-flex;
|
|
227
|
+
margin-inline-start: auto;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
214
230
|
@media (max-width: 760px), (pointer: coarse) {
|
|
215
231
|
.surf-app-shell-link {
|
|
216
232
|
min-block-size: var(--surf-control-target-size);
|
|
@@ -223,7 +239,9 @@
|
|
|
223
239
|
}
|
|
224
240
|
@media (forced-colors: active) {
|
|
225
241
|
.surf-app-shell-link[aria-current='page'] {
|
|
226
|
-
|
|
242
|
+
background-color: Highlight;
|
|
243
|
+
color: HighlightText;
|
|
244
|
+
forced-color-adjust: none;
|
|
227
245
|
}
|
|
228
246
|
}
|
|
229
247
|
/* Workspace variant: product sidebar owns the brand; the content scrollport is caller-provided. */
|
|
@@ -267,7 +285,7 @@
|
|
|
267
285
|
position: absolute;
|
|
268
286
|
inset-block-start: var(--surf-space-5);
|
|
269
287
|
inset-inline-end: calc(var(--surf-app-shell-toggle-size) / -2);
|
|
270
|
-
border: 1px solid var(--surf-
|
|
288
|
+
border: 1px solid var(--surf-control-border);
|
|
271
289
|
background: var(--surf-canvas);
|
|
272
290
|
z-index: 1;
|
|
273
291
|
}
|
|
@@ -281,6 +299,25 @@
|
|
|
281
299
|
border-color: transparent;
|
|
282
300
|
background: transparent;
|
|
283
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
|
+
}
|
|
284
321
|
/* A ScrollArea in the sidebar takes the room between the brand and the footer, and
|
|
285
322
|
* one in the content fills it, so the menu and the page scroll on their own. */
|
|
286
323
|
.surf-app-shell[data-layout='workspace']
|
|
@@ -340,6 +377,8 @@
|
|
|
340
377
|
padding: var(--surf-space-6);
|
|
341
378
|
background: var(--surf-canvas);
|
|
342
379
|
border-radius: var(--surf-app-shell-page-radius);
|
|
380
|
+
--surf-plane: var(--surf-canvas);
|
|
381
|
+
--surf-recessed: var(--surf-surface);
|
|
343
382
|
}
|
|
344
383
|
/* On a phone the page meets the screen's edges, below the mobile header. */
|
|
345
384
|
@media (max-width: 760px) {
|
|
@@ -391,6 +430,14 @@
|
|
|
391
430
|
}
|
|
392
431
|
}
|
|
393
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
|
+
}
|
|
394
441
|
/* A row rendered as a Button, such as Menu.Trigger, keeps the row's shape and the
|
|
395
442
|
* sidebar's text over the button's size scheme. */
|
|
396
443
|
.surf-app-shell-link.surf-button {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* Bar list, with the styles of what it renders. */
|
|
2
|
+
@layer surflane.tokens, surflane.themes, surflane.base, surflane.components,
|
|
3
|
+
surflane.sizes;
|
|
4
|
+
|
|
5
|
+
@layer surflane.components {
|
|
6
|
+
/* Every row is one height, so a list that scrolls shows whole rows: a name line,
|
|
7
|
+
and a detail line when any row has one. */
|
|
8
|
+
.surf-bar-list {
|
|
9
|
+
--surf-bar-list-row: calc(
|
|
10
|
+
var(--surf-type-body-size) * var(--surf-type-body-leading) +
|
|
11
|
+
var(--surf-space-2)
|
|
12
|
+
);
|
|
13
|
+
min-inline-size: 0;
|
|
14
|
+
}
|
|
15
|
+
.surf-bar-list[data-detail] {
|
|
16
|
+
--surf-bar-list-row: calc(
|
|
17
|
+
var(--surf-type-body-size) * var(--surf-type-body-leading) +
|
|
18
|
+
var(--surf-type-caption-size) * var(--surf-type-caption-leading) +
|
|
19
|
+
var(--surf-space-2)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
.surf-bar-list .surf-scroll-viewport {
|
|
23
|
+
max-block-size: calc(var(--surf-bar-list-rows) * var(--surf-bar-list-row));
|
|
24
|
+
}
|
|
25
|
+
/* Name, bar and value share one line, in columns every row shares, so the bars
|
|
26
|
+
start together and the values line up at the end. */
|
|
27
|
+
.surf-bar-list-items {
|
|
28
|
+
display: grid;
|
|
29
|
+
grid-template-columns: minmax(0, 2fr) minmax(var(--surf-space-12), 3fr) auto;
|
|
30
|
+
column-gap: var(--surf-space-3);
|
|
31
|
+
margin: 0;
|
|
32
|
+
padding: 0;
|
|
33
|
+
list-style: none;
|
|
34
|
+
}
|
|
35
|
+
.surf-bar-list-item {
|
|
36
|
+
display: grid;
|
|
37
|
+
grid-column: 1 / -1;
|
|
38
|
+
grid-template-columns: subgrid;
|
|
39
|
+
align-items: center;
|
|
40
|
+
box-sizing: border-box;
|
|
41
|
+
block-size: var(--surf-bar-list-row);
|
|
42
|
+
padding-block: var(--surf-space-1);
|
|
43
|
+
color: var(--surf-ink-body);
|
|
44
|
+
font: var(--surf-weight-regular)
|
|
45
|
+
var(--surf-type-body-size)/var(--surf-type-body-leading)
|
|
46
|
+
var(--surf-font-body);
|
|
47
|
+
}
|
|
48
|
+
.surf-bar-list-label {
|
|
49
|
+
display: grid;
|
|
50
|
+
min-inline-size: 0;
|
|
51
|
+
}
|
|
52
|
+
.surf-bar-list-name,
|
|
53
|
+
.surf-bar-list-detail {
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
text-overflow: ellipsis;
|
|
56
|
+
white-space: nowrap;
|
|
57
|
+
}
|
|
58
|
+
.surf-bar-list-name {
|
|
59
|
+
color: var(--surf-ink);
|
|
60
|
+
}
|
|
61
|
+
.surf-bar-list-detail {
|
|
62
|
+
color: var(--surf-ink-muted);
|
|
63
|
+
font-size: var(--surf-type-caption-size);
|
|
64
|
+
line-height: var(--surf-type-caption-leading);
|
|
65
|
+
}
|
|
66
|
+
/* The pair Progress and Slider use: the brand mark on the recessed step. */
|
|
67
|
+
.surf-bar-list-bar {
|
|
68
|
+
display: block;
|
|
69
|
+
block-size: var(--surf-space-2);
|
|
70
|
+
border-radius: var(--surf-radius-full);
|
|
71
|
+
background: var(--surf-recessed);
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
}
|
|
74
|
+
.surf-bar-list-fill {
|
|
75
|
+
display: block;
|
|
76
|
+
block-size: 100%;
|
|
77
|
+
border-radius: inherit;
|
|
78
|
+
background: var(--surf-brand-mark);
|
|
79
|
+
}
|
|
80
|
+
.surf-bar-list-value {
|
|
81
|
+
color: var(--surf-ink);
|
|
82
|
+
font-variant-numeric: tabular-nums;
|
|
83
|
+
text-align: end;
|
|
84
|
+
white-space: nowrap;
|
|
85
|
+
}
|
|
86
|
+
@media (forced-colors: active) {
|
|
87
|
+
.surf-bar-list-bar {
|
|
88
|
+
background: Canvas;
|
|
89
|
+
outline: 1px solid CanvasText;
|
|
90
|
+
}
|
|
91
|
+
.surf-bar-list-fill {
|
|
92
|
+
background: CanvasText;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
@layer surflane.sizes {
|
|
97
|
+
/* The bar is as thick as Meter's and Progress's at each size. */
|
|
98
|
+
:where([data-ui-size], [data-ui-size] *).surf-bar-list-bar {
|
|
99
|
+
block-size: var(--surf-ui-gauge);
|
|
100
|
+
}
|
|
101
|
+
}
|
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 {
|
|
@@ -117,6 +117,22 @@
|
|
|
117
117
|
font-variant-numeric: tabular-nums;
|
|
118
118
|
text-align: center;
|
|
119
119
|
}
|
|
120
|
+
/* Status fills, each 4.5:1 against the canvas-colored count. */
|
|
121
|
+
.surf-bottom-nav-badge[data-tone='caution'] {
|
|
122
|
+
background: var(--surf-status-caution-fg);
|
|
123
|
+
}
|
|
124
|
+
.surf-bottom-nav-badge[data-tone='success'] {
|
|
125
|
+
background: var(--surf-status-success-fg);
|
|
126
|
+
}
|
|
127
|
+
.surf-bottom-nav-badge[data-tone='info'] {
|
|
128
|
+
background: var(--surf-status-info-fg);
|
|
129
|
+
}
|
|
130
|
+
.surf-bottom-nav-badge[data-tone='progress'] {
|
|
131
|
+
background: var(--surf-status-progress-fg);
|
|
132
|
+
}
|
|
133
|
+
.surf-bottom-nav-badge[data-tone='neutral'] {
|
|
134
|
+
background: var(--surf-status-neutral-fg);
|
|
135
|
+
}
|
|
120
136
|
.surf-bottom-nav-badge[data-dot] {
|
|
121
137
|
inset-block-start: 0;
|
|
122
138
|
min-inline-size: 0;
|
|
@@ -134,10 +150,13 @@
|
|
|
134
150
|
border-block-start-color: CanvasText;
|
|
135
151
|
}
|
|
136
152
|
.surf-bottom-nav-item[aria-current='page'] .surf-bottom-nav-icon {
|
|
137
|
-
|
|
153
|
+
background-color: Highlight;
|
|
154
|
+
color: HighlightText;
|
|
155
|
+
forced-color-adjust: none;
|
|
138
156
|
}
|
|
139
157
|
.surf-bottom-nav-badge {
|
|
140
158
|
border: 1px solid CanvasText;
|
|
159
|
+
forced-color-adjust: auto;
|
|
141
160
|
}
|
|
142
161
|
}
|
|
143
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);
|