@rojaostudio/ds 2.0.0-next.27 → 2.0.0-next.30
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/components/form-actions.d.ts +32 -42
- package/dist/components/form-actions.js +5 -62
- package/dist/components/index.d.ts +3 -2
- package/dist/components/internal/bottom-bar.d.ts +30 -0
- package/dist/components/internal/bottom-bar.js +25 -0
- package/dist/components/internal/icons.d.ts +1 -3
- package/dist/components/internal/icons.js +0 -4
- package/dist/components/internal/use-on-screen-keyboard.d.ts +12 -0
- package/dist/components/internal/use-on-screen-keyboard.js +49 -0
- package/dist/components/saving-bar.d.ts +26 -22
- package/dist/components/saving-bar.js +14 -34
- package/package.json +1 -1
- package/styles/rds/components-styles.css +143 -290
- package/styles/rds/components.css +13 -11
|
@@ -1,60 +1,50 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
import { BottomBarPlacement } from './internal/bottom-bar.js';
|
|
3
4
|
|
|
4
|
-
type
|
|
5
|
+
type FormActionsPlacement = BottomBarPlacement;
|
|
5
6
|
interface FormActionsProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
7
|
/**
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* The actions (Figma: the exposed `cancel` and `primary` Buttons), in reading order: Cancelar (tone neutral, ghost)
|
|
9
|
+
* first, the primary (tone action, fill) last. Expanded (from 1024) they all show, on the right; compact (below
|
|
10
|
+
* 1024) only the last one, the primary: the others are `display: none`, and the way out is the topbar's X.
|
|
10
11
|
*/
|
|
11
12
|
children: ReactNode;
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* docked (default: the container's width, against the foot) or floating (from 1024 up: off the foot, rounded, the
|
|
15
|
+
* overlay shadow, 24 of margin, at most 768, centred). Below 1024 floating is docked (Figma: `placement`).
|
|
15
16
|
*/
|
|
16
|
-
|
|
17
|
+
placement?: FormActionsPlacement;
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* (Figma: `layout`).
|
|
19
|
+
* One ~44 control on the left, never running text (Figma: slot `leading`, `showLeading`). Present, even `null`
|
|
20
|
+
* while its control is hidden, it reserves 44 so the actions do not jump. Whoever fills it says its own changes
|
|
21
|
+
* (its own aria-live); the bar says nothing.
|
|
22
22
|
*/
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* `bar` only, with a `helper`: turns the helper into a button that opens what is missing (a Drawer with
|
|
26
|
-
* the checklist), in the compact arrangement only; expanded, the helper stays text (Figma: `showDetails`).
|
|
27
|
-
*/
|
|
28
|
-
onDetails?: () => void;
|
|
29
|
-
/** Said after the helper in the details button's accessible name, hidden on screen. */
|
|
30
|
-
detailsLabel?: string;
|
|
31
|
-
/** Whether the panel the details button opens is open (aria-expanded). */
|
|
32
|
-
detailsExpanded?: boolean;
|
|
33
|
-
/** The id of that panel (aria-controls). */
|
|
34
|
-
detailsControls?: string;
|
|
23
|
+
leading?: ReactNode;
|
|
35
24
|
}
|
|
36
25
|
/**
|
|
37
|
-
* FormActions — Figma [RDS] Actions/FormActions. The actions at the end of a create form or a task
|
|
38
|
-
*
|
|
39
|
-
* the
|
|
26
|
+
* FormActions — Figma [RDS] Actions/FormActions. The actions at the end of a create form or of a long task (Cancelar
|
|
27
|
+
* and the primary), at the foot of the screen from the first second. Only actions: what is missing is the project's
|
|
28
|
+
* (if it needs a control, it goes in `leading`). Built on the private BottomBar (.rds-bottom-bar), the SavingBar's
|
|
29
|
+
* shell: a band in the brand's primary colour (bottom-bar/background), 64 plus the safe area compact, 68 expanded,
|
|
30
|
+
* 24 at the sides, sticky at the bottom of the scrolling container. Styles: form-actions.css, internal/bottom-bar.css.
|
|
40
31
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* (surface/page) with the hairline form-actions/border on top, never the brand's colour (that is the
|
|
44
|
-
* SavingBar's signal, for a pending edit). Below 1024 (the screen width, as the SavingBar) it is one row:
|
|
45
|
-
* the helper on the left (at most 2 lines; with `onDetails`, the button that opens what is missing) and only
|
|
46
|
-
* the primary on the right.
|
|
32
|
+
* `<FormActions placement="docked" leading={…}><Button tone="neutral" variant="ghost">Cancelar</Button><Button
|
|
33
|
+
* tone="action">Criar produto</Button></FormActions>`: the last child is the primary; compact, the others leave.
|
|
47
34
|
*
|
|
48
35
|
* The app's rules around it:
|
|
49
|
-
* - The primary is never disabled: validate on click, show the error on the field and move the focus to the
|
|
50
|
-
*
|
|
51
|
-
* - The scrolling container takes `scroll-padding-bottom` equal to the bar's height
|
|
52
|
-
* `env(safe-area-inset-bottom)`, so a focused field is never hidden behind it
|
|
53
|
-
*
|
|
54
|
-
* -
|
|
55
|
-
*
|
|
56
|
-
*
|
|
36
|
+
* - The primary is never disabled: validate on click, show the error on the field and move the focus to the first
|
|
37
|
+
* pending one.
|
|
38
|
+
* - The scrolling container takes `scroll-padding-bottom` (and the content `padding-bottom`) equal to the bar's height
|
|
39
|
+
* (plus the margin when floating) plus `env(safe-area-inset-bottom)`, so a focused field is never hidden behind it
|
|
40
|
+
* (WCAG 2.4.11).
|
|
41
|
+
* - On the phone (below 1024) the bar leaves while the on-screen keyboard is open (a field is focused and the visible
|
|
42
|
+
* area shrinks): it slides down (no motion with prefers-reduced-motion) and comes back on blur; it never takes the
|
|
43
|
+
* focus.
|
|
44
|
+
* - Compact has no Cancelar: the way out is the X in the topbar, with an AlertDialog "Sair sem criar?" ([Continuar
|
|
45
|
+
* editando] [Sair sem criar]) when something was typed.
|
|
46
|
+
* - Buttons at the end of a card or a dialog are loose Buttons or a ButtonGroup, not this.
|
|
57
47
|
*/
|
|
58
|
-
declare function FormActions({ children,
|
|
48
|
+
declare function FormActions({ children, placement, leading, className, ...rest }: FormActionsProps): react.JSX.Element;
|
|
59
49
|
|
|
60
|
-
export { FormActions, type
|
|
50
|
+
export { FormActions, type FormActionsPlacement, type FormActionsProps };
|
|
@@ -1,71 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import {
|
|
2
3
|
__objRest,
|
|
3
4
|
__spreadProps,
|
|
4
5
|
__spreadValues
|
|
5
6
|
} from "../chunk-ORMEWXMH.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { ChevronUpIcon } from "./internal/icons.js";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
import { BottomBar } from "./internal/bottom-bar.js";
|
|
9
9
|
function FormActions(_a) {
|
|
10
|
-
var _b = _a, {
|
|
11
|
-
|
|
12
|
-
helper,
|
|
13
|
-
layout = "inline",
|
|
14
|
-
onDetails,
|
|
15
|
-
detailsLabel = "Ver o que falta",
|
|
16
|
-
detailsExpanded,
|
|
17
|
-
detailsControls,
|
|
18
|
-
className
|
|
19
|
-
} = _b, rest = __objRest(_b, [
|
|
20
|
-
"children",
|
|
21
|
-
"helper",
|
|
22
|
-
"layout",
|
|
23
|
-
"onDetails",
|
|
24
|
-
"detailsLabel",
|
|
25
|
-
"detailsExpanded",
|
|
26
|
-
"detailsControls",
|
|
27
|
-
"className"
|
|
28
|
-
]);
|
|
29
|
-
const actions = Children.toArray(children);
|
|
30
|
-
const bar = layout === "bar";
|
|
31
|
-
const details = bar && helper != null && helper !== false && onDetails !== void 0;
|
|
32
|
-
return /* @__PURE__ */ jsxs(
|
|
33
|
-
"div",
|
|
34
|
-
__spreadProps(__spreadValues({}, rest), {
|
|
35
|
-
className: ["rds-form-actions", `rds-form-actions--${layout}`, className].filter(Boolean).join(" "),
|
|
36
|
-
children: [
|
|
37
|
-
helper && /* @__PURE__ */ jsx(
|
|
38
|
-
"p",
|
|
39
|
-
{
|
|
40
|
-
className: ["rds-form-actions__helper", details && "rds-form-actions__helper--details"].filter(Boolean).join(" "),
|
|
41
|
-
role: "status",
|
|
42
|
-
children: bar ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
43
|
-
/* @__PURE__ */ jsx("span", { className: "rds-form-actions__text", children: helper }),
|
|
44
|
-
details && /* @__PURE__ */ jsxs(
|
|
45
|
-
"button",
|
|
46
|
-
{
|
|
47
|
-
type: "button",
|
|
48
|
-
className: "rds-form-actions__details",
|
|
49
|
-
"aria-expanded": detailsExpanded != null ? detailsExpanded : false,
|
|
50
|
-
"aria-controls": detailsControls,
|
|
51
|
-
onClick: onDetails,
|
|
52
|
-
children: [
|
|
53
|
-
/* @__PURE__ */ jsx("span", { className: "rds-form-actions__text", children: helper }),
|
|
54
|
-
/* @__PURE__ */ jsxs("span", { className: "rds-visually-hidden", children: [
|
|
55
|
-
", ",
|
|
56
|
-
detailsLabel
|
|
57
|
-
] }),
|
|
58
|
-
/* @__PURE__ */ jsx("span", { className: "rds-form-actions__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronUpIcon, {}) })
|
|
59
|
-
]
|
|
60
|
-
}
|
|
61
|
-
)
|
|
62
|
-
] }) : helper
|
|
63
|
-
}
|
|
64
|
-
),
|
|
65
|
-
/* @__PURE__ */ jsx("div", { className: "rds-form-actions__actions", children: layout === "stacked" ? actions.reverse() : actions })
|
|
66
|
-
]
|
|
67
|
-
})
|
|
68
|
-
);
|
|
10
|
+
var _b = _a, { children, placement = "docked", leading, className } = _b, rest = __objRest(_b, ["children", "placement", "leading", "className"]);
|
|
11
|
+
return /* @__PURE__ */ jsx(BottomBar, __spreadProps(__spreadValues({}, rest), { placement, leading, className: ["rds-form-actions", className].filter(Boolean).join(" "), children: /* @__PURE__ */ jsx("div", { className: "rds-form-actions__actions", children }) }));
|
|
69
12
|
}
|
|
70
13
|
export {
|
|
71
14
|
FormActions
|
|
@@ -69,7 +69,7 @@ export { Tabs, TabsContent, TabsContentProps, TabsList, TabsListProps, TabsProps
|
|
|
69
69
|
export { PageHeader, PageHeaderHeading, PageHeaderProps } from './page-header.js';
|
|
70
70
|
export { Sidebar, SidebarHeader, SidebarItem, SidebarItemProps, SidebarProps, SidebarSection, SidebarSectionProps, SidebarSeparator } from './sidebar.js';
|
|
71
71
|
export { NavigationMenu, NavigationMenuEntry, NavigationMenuLink, NavigationMenuLinkComponent, NavigationMenuProps } from './navigation-menu.js';
|
|
72
|
-
export { SavingBar, SavingBarProps, SavingBarStatus } from './saving-bar.js';
|
|
72
|
+
export { SavingBar, SavingBarPlacement, SavingBarProps, SavingBarStatus } from './saving-bar.js';
|
|
73
73
|
export { SavingBarProvider, SavingBarRegistration, SavingBarRoot, usePageSavingBar } from './saving-bar-context.js';
|
|
74
74
|
export { FAB, FABConfig, FABProps, FABProvider, FABRoot, useFAB, usePageFAB } from './fab.js';
|
|
75
75
|
export { Table, TableAlign, TableBody, TableCell, TableCellProps, TableHead, TableHeadProps, TableHeader, TableProps, TableRow, TableSort, TableStatus, TableStatusProps } from './table.js';
|
|
@@ -84,7 +84,7 @@ export { Avatar, AvatarContent, AvatarGroup, AvatarGroupProps, AvatarGroupSize,
|
|
|
84
84
|
export { IconButton, IconButtonProps } from './icon-button.js';
|
|
85
85
|
export { ButtonGroup, ButtonGroupOrientation, ButtonGroupProps } from './button-group.js';
|
|
86
86
|
export { ActionBar, ActionBarProps } from './action-bar.js';
|
|
87
|
-
export { FormActions,
|
|
87
|
+
export { FormActions, FormActionsPlacement, FormActionsProps } from './form-actions.js';
|
|
88
88
|
export { Breadcrumb, BreadcrumbItem, BreadcrumbLinkComponent, BreadcrumbProps, BreadcrumbTone } from './breadcrumb.js';
|
|
89
89
|
export { Pagination, PaginationLinkComponent, PaginationProps, pageItems } from './pagination.js';
|
|
90
90
|
export { Popover, PopoverAlign, PopoverClose, PopoverProps, PopoverSide } from './popover.js';
|
|
@@ -136,6 +136,7 @@ import '@radix-ui/react-dialog';
|
|
|
136
136
|
import './internal/modal.js';
|
|
137
137
|
import './internal/menu.js';
|
|
138
138
|
import './internal/fieldset.js';
|
|
139
|
+
import './internal/bottom-bar.js';
|
|
139
140
|
import '@radix-ui/react-toggle';
|
|
140
141
|
import '@radix-ui/react-tooltip';
|
|
141
142
|
import '@radix-ui/react-popover';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type BottomBarPlacement = 'docked' | 'floating';
|
|
5
|
+
interface BottomBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
/**
|
|
7
|
+
* docked (the container's width, against the foot, square corners) or floating (from lg 1024 up: off the foot by
|
|
8
|
+
* bottom-bar/floating/margin, at most bottom-bar/floating/max-width, centred, bottom-bar/floating/radius and the
|
|
9
|
+
* elevation/overlay shadow). Below 1024 floating is exactly docked (Figma: `placement`).
|
|
10
|
+
*/
|
|
11
|
+
placement?: BottomBarPlacement;
|
|
12
|
+
/**
|
|
13
|
+
* The leading slot, on the left: one ~44 control, never running text. Present (even `null` or `false` while its
|
|
14
|
+
* control is hidden), the slot is there and reserves bottom-bar/leading/min-width, so what follows does not jump
|
|
15
|
+
* when the control comes and goes; left out (`undefined`), there is no slot. Whoever fills it says its own changes.
|
|
16
|
+
*/
|
|
17
|
+
leading?: ReactNode;
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* BottomBar — Figma [RDS] Actions/.bottom-bar, private: the shell at the foot of the screen under the FormActions and
|
|
22
|
+
* the SavingBar. Not exported from the package. It owns the colour (bottom-bar/background, the brand's primary), the
|
|
23
|
+
* height (64 plus env(safe-area-inset-bottom) compact, 68 expanded), the padding and gap, the place (sticky at the
|
|
24
|
+
* bottom of the scrolling container), the placement, the leading slot, the Button tokens in the bar's colours and the
|
|
25
|
+
* on-screen keyboard (compact: it slides away while a field is being typed in). Whoever builds on it puts the
|
|
26
|
+
* actions. Styles: bottom-bar.css.
|
|
27
|
+
*/
|
|
28
|
+
declare function BottomBar({ placement, leading, className, children, ...rest }: BottomBarProps): react.JSX.Element;
|
|
29
|
+
|
|
30
|
+
export { BottomBar, type BottomBarPlacement, type BottomBarProps };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
__objRest,
|
|
4
|
+
__spreadProps,
|
|
5
|
+
__spreadValues
|
|
6
|
+
} from "../../chunk-ORMEWXMH.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { useOnScreenKeyboard } from "./use-on-screen-keyboard.js";
|
|
9
|
+
function BottomBar(_a) {
|
|
10
|
+
var _b = _a, { placement = "docked", leading, className, children } = _b, rest = __objRest(_b, ["placement", "leading", "className", "children"]);
|
|
11
|
+
const keyboard = useOnScreenKeyboard();
|
|
12
|
+
return /* @__PURE__ */ jsxs(
|
|
13
|
+
"div",
|
|
14
|
+
__spreadProps(__spreadValues({}, rest), {
|
|
15
|
+
className: ["rds-bottom-bar", `rds-bottom-bar--${placement}`, keyboard && "rds-bottom-bar--keyboard", className].filter(Boolean).join(" "),
|
|
16
|
+
children: [
|
|
17
|
+
leading !== void 0 && /* @__PURE__ */ jsx("div", { className: "rds-bottom-bar__leading", children: leading }),
|
|
18
|
+
children
|
|
19
|
+
]
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
BottomBar
|
|
25
|
+
};
|
|
@@ -12,8 +12,6 @@ declare function CheckIcon(): react.JSX.Element;
|
|
|
12
12
|
declare function MinusIcon(): react.JSX.Element;
|
|
13
13
|
/** A chevron pointing down: opens a list. */
|
|
14
14
|
declare function ChevronDownIcon(): react.JSX.Element;
|
|
15
|
-
/** A chevron pointing up: opens a panel that comes up from below (the SavingBar's details). */
|
|
16
|
-
declare function ChevronUpIcon(): react.JSX.Element;
|
|
17
15
|
/** An open eye: shows what is hidden (a password). */
|
|
18
16
|
declare function EyeIcon(): react.JSX.Element;
|
|
19
17
|
/** A crossed eye: hides again what was shown. */
|
|
@@ -115,4 +113,4 @@ declare function RectangleHorizontalIcon(): react.JSX.Element;
|
|
|
115
113
|
/** A dashed square: a free crop, no fixed aspect. */
|
|
116
114
|
declare function SquareDashedIcon(): react.JSX.Element;
|
|
117
115
|
|
|
118
|
-
export { AlertIcon, ArrowDownIcon, ArrowLeftIcon, ArrowUpDownIcon, ArrowUpIcon, BadgeCheckIcon, BuildingIcon, CalendarIcon, CameraIcon, CheckCheckIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon,
|
|
116
|
+
export { AlertIcon, ArrowDownIcon, ArrowLeftIcon, ArrowUpDownIcon, ArrowUpIcon, BadgeCheckIcon, BuildingIcon, CalendarIcon, CameraIcon, CheckCheckIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, CircleCheckIcon, ClockIcon, CloseIcon, CopyIcon, ExternalLinkIcon, EyeIcon, EyeOffIcon, FileTextIcon, ImageIcon, ImageOffIcon, ImagePlusIcon, InfoIcon, LockIcon, MapPinIcon, MegaphoneIcon, MenuIcon, MicIcon, MinusIcon, MoreVerticalIcon, PaperclipIcon, PencilIcon, PhoneIcon, PlusIcon, QuoteIcon, RectangleHorizontalIcon, RectangleVerticalIcon, ReplyIcon, RotateCcwIcon, SearchIcon, SearchXIcon, SlidersIcon, SmileIcon, SparklesIcon, SquareDashedIcon, SquareIcon, StarFilledIcon, StarIcon, TableIcon, ThumbsUpIcon, TriangleAlertIcon, UploadIcon, UserIcon };
|
|
@@ -21,9 +21,6 @@ function MinusIcon() {
|
|
|
21
21
|
function ChevronDownIcon() {
|
|
22
22
|
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) });
|
|
23
23
|
}
|
|
24
|
-
function ChevronUpIcon() {
|
|
25
|
-
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m18 15-6-6-6 6" }) });
|
|
26
|
-
}
|
|
27
24
|
function EyeIcon() {
|
|
28
25
|
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
29
26
|
/* @__PURE__ */ jsx("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }),
|
|
@@ -290,7 +287,6 @@ export {
|
|
|
290
287
|
ChevronDownIcon,
|
|
291
288
|
ChevronLeftIcon,
|
|
292
289
|
ChevronRightIcon,
|
|
293
|
-
ChevronUpIcon,
|
|
294
290
|
CircleCheckIcon,
|
|
295
291
|
ClockIcon,
|
|
296
292
|
CloseIcon,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Whether focusing this element brings up the on-screen keyboard: a text field, a text area or contenteditable. */
|
|
2
|
+
declare function isEditable(el: Element | null): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Whether the phone's on-screen keyboard is open: compact screen (below 1024), an editable field focused, and the
|
|
5
|
+
* visible area (visualViewport, scaled back from a pinch zoom) well short of the layout viewport. For the bars at the
|
|
6
|
+
* foot of the screen (FormActions, SavingBar), which leave while it is open so they do not cover the field being
|
|
7
|
+
* typed in. Without visualViewport (old browsers) or on the server it is always false: the bar stays.
|
|
8
|
+
* `enabled` false skips the listeners (a layout that does not sit at the foot).
|
|
9
|
+
*/
|
|
10
|
+
declare function useOnScreenKeyboard(enabled?: boolean): boolean;
|
|
11
|
+
|
|
12
|
+
export { isEditable, useOnScreenKeyboard };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "../../chunk-ORMEWXMH.js";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
const COMPACT = "(max-width: 1023px)";
|
|
5
|
+
const KEYBOARD_RATIO = 0.75;
|
|
6
|
+
const NO_KEYBOARD = /* @__PURE__ */ new Set(["button", "checkbox", "color", "file", "hidden", "image", "radio", "range", "reset", "submit"]);
|
|
7
|
+
function isEditable(el) {
|
|
8
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
9
|
+
if (el.isContentEditable) return true;
|
|
10
|
+
if (el instanceof HTMLTextAreaElement) return !el.readOnly && !el.disabled;
|
|
11
|
+
if (el instanceof HTMLInputElement) return !NO_KEYBOARD.has(el.type) && !el.readOnly && !el.disabled;
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
function useOnScreenKeyboard(enabled = true) {
|
|
15
|
+
const [open, setOpen] = useState(false);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const viewport = window.visualViewport;
|
|
18
|
+
if (!enabled || !viewport || typeof window.matchMedia !== "function") {
|
|
19
|
+
setOpen(false);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const compact = window.matchMedia(COMPACT);
|
|
23
|
+
let frame = 0;
|
|
24
|
+
const measure = () => compact.matches && isEditable(document.activeElement) && viewport.height * viewport.scale < window.innerHeight * KEYBOARD_RATIO;
|
|
25
|
+
const check = () => {
|
|
26
|
+
cancelAnimationFrame(frame);
|
|
27
|
+
frame = requestAnimationFrame(() => setOpen(measure()));
|
|
28
|
+
};
|
|
29
|
+
check();
|
|
30
|
+
viewport.addEventListener("resize", check);
|
|
31
|
+
window.addEventListener("resize", check);
|
|
32
|
+
document.addEventListener("focusin", check);
|
|
33
|
+
document.addEventListener("focusout", check);
|
|
34
|
+
compact.addEventListener("change", check);
|
|
35
|
+
return () => {
|
|
36
|
+
cancelAnimationFrame(frame);
|
|
37
|
+
viewport.removeEventListener("resize", check);
|
|
38
|
+
window.removeEventListener("resize", check);
|
|
39
|
+
document.removeEventListener("focusin", check);
|
|
40
|
+
document.removeEventListener("focusout", check);
|
|
41
|
+
compact.removeEventListener("change", check);
|
|
42
|
+
};
|
|
43
|
+
}, [enabled]);
|
|
44
|
+
return enabled && open;
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
isEditable,
|
|
48
|
+
useOnScreenKeyboard
|
|
49
|
+
};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
import { BottomBarPlacement } from './internal/bottom-bar.js';
|
|
3
4
|
|
|
4
5
|
type SavingBarStatus = 'unsaved' | 'saving' | 'error';
|
|
6
|
+
type SavingBarPlacement = BottomBarPlacement;
|
|
5
7
|
interface SavingBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
8
|
/**
|
|
7
9
|
* unsaved: the message and the actions; saving: `savingMessage`, Salvar in loading (`savingLabel`, the only
|
|
@@ -9,7 +11,7 @@ interface SavingBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
9
11
|
* becomes `retryLabel` (Figma: `status`).
|
|
10
12
|
*/
|
|
11
13
|
status?: SavingBarStatus;
|
|
12
|
-
/** The unsaved text (Figma: `message`). */
|
|
14
|
+
/** The unsaved text, at most 2 lines with an ellipsis (Figma: `message`). */
|
|
13
15
|
message?: ReactNode;
|
|
14
16
|
/** The text while saving. */
|
|
15
17
|
savingMessage?: ReactNode;
|
|
@@ -21,35 +23,37 @@ interface SavingBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
21
23
|
onSave: () => void;
|
|
22
24
|
/**
|
|
23
25
|
* Shows Descartar and is called when it is pressed (Figma: `showDiscard`). Expanded only: in the compact
|
|
24
|
-
* arrangement (the phone) the bar has only the main button, and Descartar belongs to the
|
|
25
|
-
*
|
|
26
|
+
* arrangement (the phone) the bar has only the main button, and Descartar belongs to the way out, in the
|
|
27
|
+
* confirmation when leaving with changes (AlertDialog).
|
|
26
28
|
*/
|
|
27
29
|
onDiscard?: () => void;
|
|
28
30
|
saveLabel?: string;
|
|
29
31
|
retryLabel?: string;
|
|
30
32
|
discardLabel?: string;
|
|
31
33
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
+
* docked (default: the container's width, against the foot) or floating (from 1024 up: off the foot, rounded, the
|
|
35
|
+
* overlay shadow, 24 of margin, at most 768, centred). Below 1024 floating is docked (Figma: `placement`).
|
|
34
36
|
*/
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
placement?: SavingBarPlacement;
|
|
38
|
+
/**
|
|
39
|
+
* One ~44 control on the left of the message, never running text (Figma: slot `leading`, `showLeading`). Present,
|
|
40
|
+
* even `null` while its control is hidden, it reserves 44 so the message and the buttons do not jump. Whoever fills
|
|
41
|
+
* it says its own changes (its own aria-live); the bar does not repeat them.
|
|
42
|
+
*/
|
|
43
|
+
leading?: ReactNode;
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
|
-
* SavingBar — Figma [RDS] Actions/SavingBar. Shows at the foot of the screen while a change is not
|
|
45
|
-
* saved
|
|
46
|
-
* in light
|
|
47
|
-
*
|
|
48
|
-
* with the bar's colours (
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
46
|
+
* SavingBar — Figma [RDS] Actions/SavingBar. Shows at the foot of the screen while a change is not saved, and leaves
|
|
47
|
+
* once saved (a Toast confirms). Built on the private BottomBar (.rds-bottom-bar), the FormActions' shell: the brand's
|
|
48
|
+
* primary colour (bottom-bar/background) in light, dark and every brand, 64 plus the safe area compact, 68 expanded,
|
|
49
|
+
* 24 at the sides, sticky at the bottom of the scrolling container. Its Buttons are Salvar in tone action fill and
|
|
50
|
+
* Descartar in tone neutral ghost, with the bar's colours (bottom-bar/button/*). Below 1024 (compact, the phone) it is
|
|
51
|
+
* one row: the message on the left and only Salvar on the right; Descartar is in the confirmation when leaving. While
|
|
52
|
+
* saving, the indicator is Salvar in loading, alone (with the LoadingOverlay too). On the phone it leaves while the
|
|
53
|
+
* on-screen keyboard is open and comes back on blur, never taking the focus. The content needs room below as tall as
|
|
54
|
+
* the bar (`scroll-padding-bottom`, plus `env(safe-area-inset-bottom)`). Styles: saving-bar.css,
|
|
55
|
+
* internal/bottom-bar.css.
|
|
52
56
|
*/
|
|
53
|
-
declare function SavingBar({ status, message, savingMessage, savingLabel, errorMessage, onSave, onDiscard, saveLabel, retryLabel, discardLabel,
|
|
57
|
+
declare function SavingBar({ status, message, savingMessage, savingLabel, errorMessage, onSave, onDiscard, saveLabel, retryLabel, discardLabel, placement, leading, className, ...rest }: SavingBarProps): react.JSX.Element;
|
|
54
58
|
|
|
55
|
-
export { SavingBar, type SavingBarProps, type SavingBarStatus };
|
|
59
|
+
export { SavingBar, type SavingBarPlacement, type SavingBarProps, type SavingBarStatus };
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
} from "../chunk-ORMEWXMH.js";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { Button } from "./button.js";
|
|
9
|
-
import {
|
|
9
|
+
import { BottomBar } from "./internal/bottom-bar.js";
|
|
10
|
+
import { AlertIcon } from "./internal/icons.js";
|
|
10
11
|
function SavingBar(_a) {
|
|
11
12
|
var _b = _a, {
|
|
12
13
|
status = "unsaved",
|
|
@@ -19,10 +20,8 @@ function SavingBar(_a) {
|
|
|
19
20
|
saveLabel = "Salvar",
|
|
20
21
|
retryLabel = "Tentar de novo",
|
|
21
22
|
discardLabel = "Descartar",
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
detailsExpanded,
|
|
25
|
-
detailsControls,
|
|
23
|
+
placement = "docked",
|
|
24
|
+
leading,
|
|
26
25
|
className
|
|
27
26
|
} = _b, rest = __objRest(_b, [
|
|
28
27
|
"status",
|
|
@@ -35,51 +34,32 @@ function SavingBar(_a) {
|
|
|
35
34
|
"saveLabel",
|
|
36
35
|
"retryLabel",
|
|
37
36
|
"discardLabel",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"detailsExpanded",
|
|
41
|
-
"detailsControls",
|
|
37
|
+
"placement",
|
|
38
|
+
"leading",
|
|
42
39
|
"className"
|
|
43
40
|
]);
|
|
44
41
|
const saving = status === "saving";
|
|
45
42
|
const error = status === "error";
|
|
46
43
|
const text = saving ? savingMessage : error ? errorMessage : message;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"div",
|
|
44
|
+
return /* @__PURE__ */ jsxs(
|
|
45
|
+
BottomBar,
|
|
50
46
|
__spreadProps(__spreadValues({
|
|
51
47
|
role: "region",
|
|
52
48
|
"aria-label": "Salvar altera\xE7\xF5es"
|
|
53
49
|
}, rest), {
|
|
50
|
+
placement,
|
|
51
|
+
leading,
|
|
54
52
|
className: ["rds-savingbar", className].filter(Boolean).join(" "),
|
|
55
|
-
children:
|
|
56
|
-
/* @__PURE__ */ jsxs("p", { className:
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ jsxs("p", { className: "rds-savingbar__message", role: "status", children: [
|
|
57
55
|
error && /* @__PURE__ */ jsx("span", { className: "rds-savingbar__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AlertIcon, {}) }),
|
|
58
|
-
/* @__PURE__ */ jsx("span", { className: "rds-savingbar__text", children: text })
|
|
59
|
-
details && /* @__PURE__ */ jsxs(
|
|
60
|
-
"button",
|
|
61
|
-
{
|
|
62
|
-
type: "button",
|
|
63
|
-
className: "rds-savingbar__details",
|
|
64
|
-
"aria-expanded": detailsExpanded != null ? detailsExpanded : false,
|
|
65
|
-
"aria-controls": detailsControls,
|
|
66
|
-
onClick: onDetails,
|
|
67
|
-
children: [
|
|
68
|
-
/* @__PURE__ */ jsx("span", { className: "rds-savingbar__text", children: text }),
|
|
69
|
-
/* @__PURE__ */ jsxs("span", { className: "rds-visually-hidden", children: [
|
|
70
|
-
", ",
|
|
71
|
-
detailsLabel
|
|
72
|
-
] }),
|
|
73
|
-
/* @__PURE__ */ jsx("span", { className: "rds-savingbar__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronUpIcon, {}) })
|
|
74
|
-
]
|
|
75
|
-
}
|
|
76
|
-
)
|
|
56
|
+
/* @__PURE__ */ jsx("span", { className: "rds-savingbar__text", children: text })
|
|
77
57
|
] }),
|
|
78
58
|
/* @__PURE__ */ jsxs("div", { className: "rds-savingbar__actions", children: [
|
|
79
59
|
onDiscard && /* @__PURE__ */ jsx(Button, { className: "rds-savingbar__discard", tone: "neutral", variant: "ghost", disabled: saving, onClick: onDiscard, children: discardLabel }),
|
|
80
60
|
/* @__PURE__ */ jsx(Button, { tone: "action", variant: "fill", loading: saving, loadingLabel: savingLabel, onClick: onSave, children: saving ? savingLabel : error ? retryLabel : saveLabel })
|
|
81
61
|
] })
|
|
82
|
-
]
|
|
62
|
+
]
|
|
83
63
|
})
|
|
84
64
|
);
|
|
85
65
|
}
|
package/package.json
CHANGED
|
@@ -4650,195 +4650,24 @@ input:focus-visible + .rds-checkbox__box {
|
|
|
4650
4650
|
}
|
|
4651
4651
|
|
|
4652
4652
|
/* components/form-actions.css */
|
|
4653
|
-
/* @tokens
|
|
4653
|
+
/* @tokens bottom-bar */
|
|
4654
4654
|
/*
|
|
4655
|
-
* FormActions (
|
|
4656
|
-
*
|
|
4657
|
-
*
|
|
4658
|
-
* bar is the foot of a create form, the SavingBar's skeleton but neutral: form-actions/bar/background
|
|
4659
|
-
* (surface/page) with the hairline form-actions/border on top (drawn inside, so it adds no height), full
|
|
4660
|
-
* width (at least 320), 16 between the helper and the actions (8 apart). Two arrangements by the screen
|
|
4661
|
-
* width, as the SavingBar (layout/compact below lg 1024): expanded, 12 24 padding (68 = 12 + 44 + 12), the
|
|
4662
|
-
* helper grows (at least 160) beside Cancelar and the primary; compact, one row, 10 24 padding
|
|
4663
|
-
* (64 = 10 + 44 + 10) plus the safe area at the bottom, the helper (flex 1, at most 2 lines) on the left and
|
|
4664
|
-
* only the last action (the primary) on the right.
|
|
4665
|
-
* Details (showDetails, onDetails in code): compact, the helper becomes a text button with chevron-up 20 at
|
|
4666
|
-
* the right (12 apart); hover underlines, focus draws form-actions/focus/ring (focus/ring-width, radius/focus);
|
|
4667
|
-
* the touch area is 44 tall without changing the drawing. Expanded, the helper stays text.
|
|
4655
|
+
* FormActions: no tokens of its own; the shell (internal/bottom-bar.css, bottom-bar/*) brings the colour, the height,
|
|
4656
|
+
* the padding, the place and the Buttons' colours. Here only the actions: on the right, 8 apart, Cancelar then the
|
|
4657
|
+
* primary. Compact (below lg 1024, by the screen width as the Figma viewport mode), only the last one, the primary.
|
|
4668
4658
|
*/
|
|
4669
|
-
.rds-form-actions {
|
|
4670
|
-
box-sizing: border-box;
|
|
4671
|
-
display: flex;
|
|
4672
|
-
align-items: center;
|
|
4673
|
-
background: var(--form-actions-background);
|
|
4674
|
-
color: var(--form-actions-text);
|
|
4675
|
-
font-family: var(--type-font-stack);
|
|
4676
|
-
}
|
|
4677
|
-
|
|
4678
|
-
.rds-form-actions__helper {
|
|
4679
|
-
flex: 1;
|
|
4680
|
-
min-width: 0;
|
|
4681
|
-
margin: 0;
|
|
4682
|
-
font-size: var(--type-label-size);
|
|
4683
|
-
line-height: var(--type-label-line);
|
|
4684
|
-
}
|
|
4685
|
-
|
|
4686
4659
|
.rds-form-actions__actions {
|
|
4687
4660
|
display: flex;
|
|
4688
|
-
flex-wrap: wrap;
|
|
4689
|
-
gap: var(--space-8);
|
|
4690
|
-
}
|
|
4691
|
-
|
|
4692
|
-
/* inline: floats and hugs its content; the page decides where. */
|
|
4693
|
-
.rds-form-actions--inline {
|
|
4694
|
-
display: inline-flex;
|
|
4695
|
-
flex-wrap: wrap;
|
|
4696
|
-
justify-content: flex-end;
|
|
4697
|
-
gap: var(--space-24);
|
|
4698
|
-
max-width: 100%;
|
|
4699
|
-
padding: var(--space-16) var(--space-24);
|
|
4700
|
-
border: var(--border-width) solid var(--form-actions-border);
|
|
4701
|
-
border-radius: var(--radius-container);
|
|
4702
|
-
box-shadow: var(--elevation-raised);
|
|
4703
|
-
}
|
|
4704
|
-
|
|
4705
|
-
/* stacked: edge to edge at the foot, the buttons full width. */
|
|
4706
|
-
.rds-form-actions--stacked {
|
|
4707
|
-
flex-direction: column;
|
|
4708
|
-
align-items: stretch;
|
|
4709
|
-
gap: var(--space-8);
|
|
4710
|
-
width: 100%;
|
|
4711
|
-
padding: var(--space-16);
|
|
4712
|
-
border-top: var(--border-width) solid var(--form-actions-border);
|
|
4713
|
-
}
|
|
4714
|
-
|
|
4715
|
-
.rds-form-actions--stacked .rds-form-actions__actions {
|
|
4716
|
-
flex-direction: column;
|
|
4717
|
-
}
|
|
4718
|
-
|
|
4719
|
-
.rds-form-actions--stacked .rds-form-actions__actions > * {
|
|
4720
|
-
width: 100%;
|
|
4721
|
-
}
|
|
4722
|
-
|
|
4723
|
-
/* bar: the foot of a create form, the SavingBar's skeleton, neutral. */
|
|
4724
|
-
.rds-form-actions--bar {
|
|
4725
|
-
gap: var(--space-16);
|
|
4726
|
-
justify-content: flex-end;
|
|
4727
|
-
width: 100%;
|
|
4728
|
-
min-width: min(320px, 100%);
|
|
4729
|
-
padding: var(--space-12) var(--space-24);
|
|
4730
|
-
background: var(--form-actions-bar-background);
|
|
4731
|
-
/* The hairline on top, inside: the bar stays 68 / 64 tall. */
|
|
4732
|
-
box-shadow: inset 0 var(--border-width) 0 var(--form-actions-border);
|
|
4733
|
-
}
|
|
4734
|
-
|
|
4735
|
-
.rds-form-actions--bar .rds-form-actions__helper {
|
|
4736
|
-
display: flex;
|
|
4737
|
-
flex: 1 1 160px;
|
|
4738
|
-
align-items: center;
|
|
4739
|
-
min-width: 160px;
|
|
4740
|
-
}
|
|
4741
|
-
|
|
4742
|
-
.rds-form-actions--bar .rds-form-actions__actions {
|
|
4743
|
-
flex: none;
|
|
4744
|
-
flex-wrap: nowrap;
|
|
4745
|
-
}
|
|
4746
|
-
|
|
4747
|
-
.rds-form-actions__text {
|
|
4748
|
-
display: -webkit-box;
|
|
4749
|
-
flex: 1;
|
|
4750
|
-
min-width: 0;
|
|
4751
|
-
overflow: hidden;
|
|
4752
|
-
-webkit-box-orient: vertical;
|
|
4753
|
-
-webkit-line-clamp: 2;
|
|
4754
|
-
line-clamp: 2;
|
|
4755
|
-
}
|
|
4756
|
-
|
|
4757
|
-
.rds-form-actions__icon {
|
|
4758
|
-
display: inline-flex;
|
|
4759
4661
|
flex: none;
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
}
|
|
4763
|
-
|
|
4764
|
-
.rds-form-actions__icon > svg {
|
|
4765
|
-
width: 100%;
|
|
4766
|
-
height: 100%;
|
|
4767
|
-
}
|
|
4768
|
-
|
|
4769
|
-
.rds-form-actions__details {
|
|
4770
|
-
position: relative;
|
|
4771
|
-
display: none;
|
|
4772
|
-
flex: 1;
|
|
4773
|
-
align-items: center;
|
|
4774
|
-
gap: var(--space-12);
|
|
4775
|
-
min-width: 0;
|
|
4776
|
-
margin: 0;
|
|
4777
|
-
padding: 0;
|
|
4778
|
-
border: 0;
|
|
4779
|
-
border-radius: var(--radius-focus);
|
|
4780
|
-
background: none;
|
|
4781
|
-
color: inherit;
|
|
4782
|
-
font: inherit;
|
|
4783
|
-
text-align: start;
|
|
4784
|
-
cursor: pointer;
|
|
4785
|
-
}
|
|
4786
|
-
|
|
4787
|
-
/* The touch area: 44 tall around the helper, invisible. */
|
|
4788
|
-
.rds-form-actions__details::after {
|
|
4789
|
-
content: '';
|
|
4790
|
-
position: absolute;
|
|
4791
|
-
top: 50%;
|
|
4792
|
-
right: 0;
|
|
4793
|
-
left: 0;
|
|
4794
|
-
min-height: 44px;
|
|
4795
|
-
height: 100%;
|
|
4796
|
-
transform: translateY(-50%);
|
|
4797
|
-
}
|
|
4798
|
-
|
|
4799
|
-
.rds-form-actions__details:hover .rds-form-actions__text {
|
|
4800
|
-
text-decoration: underline;
|
|
4801
|
-
}
|
|
4802
|
-
|
|
4803
|
-
.rds-form-actions__details:focus-visible {
|
|
4804
|
-
outline: var(--focus-ring-width) solid var(--form-actions-focus-ring);
|
|
4805
|
-
outline-offset: var(--focus-ring-width);
|
|
4662
|
+
gap: var(--space-8);
|
|
4663
|
+
margin-inline-start: auto;
|
|
4806
4664
|
}
|
|
4807
4665
|
|
|
4808
|
-
/* Compact: by the screen width (the Figma viewport mode), as the SavingBar, not a container query. */
|
|
4809
4666
|
@media (max-width: 1023px) {
|
|
4810
|
-
.
|
|
4811
|
-
|
|
4812
|
-
padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
|
|
4813
|
-
}
|
|
4814
|
-
|
|
4815
|
-
.rds-form-actions--bar .rds-form-actions__helper {
|
|
4816
|
-
flex: 1 1 0;
|
|
4817
|
-
min-width: 0;
|
|
4818
|
-
}
|
|
4819
|
-
|
|
4820
|
-
/* Only the primary (the last action): Cancelar is the topbar's back or X, or the details Drawer's footer. */
|
|
4821
|
-
.rds-form-actions--bar .rds-form-actions__actions > :not(:last-child) {
|
|
4667
|
+
/* Only the primary (the last action): Cancelar is the topbar's X. */
|
|
4668
|
+
.rds-form-actions__actions > :not(:last-child) {
|
|
4822
4669
|
display: none;
|
|
4823
4670
|
}
|
|
4824
|
-
|
|
4825
|
-
.rds-form-actions__helper--details > .rds-form-actions__text {
|
|
4826
|
-
display: none;
|
|
4827
|
-
}
|
|
4828
|
-
|
|
4829
|
-
.rds-form-actions__details {
|
|
4830
|
-
display: flex;
|
|
4831
|
-
}
|
|
4832
|
-
}
|
|
4833
|
-
|
|
4834
|
-
@media (forced-colors: active) {
|
|
4835
|
-
.rds-form-actions {
|
|
4836
|
-
border-color: CanvasText;
|
|
4837
|
-
}
|
|
4838
|
-
|
|
4839
|
-
.rds-form-actions--bar {
|
|
4840
|
-
border-top: var(--border-width) solid CanvasText;
|
|
4841
|
-
}
|
|
4842
4671
|
}
|
|
4843
4672
|
|
|
4844
4673
|
/* components/google-account-chooser.css */
|
|
@@ -5561,6 +5390,124 @@ input:focus-visible + .rds-checkbox__box {
|
|
|
5561
5390
|
}
|
|
5562
5391
|
}
|
|
5563
5392
|
|
|
5393
|
+
/* components/internal/bottom-bar.css */
|
|
5394
|
+
/* @tokens bottom-bar */
|
|
5395
|
+
/*
|
|
5396
|
+
* The shell at the foot of the screen (Actions collection, bottom-bar/*), private, under the FormActions and the
|
|
5397
|
+
* SavingBar. The brand's primary colour: bottom-bar/background (colors/primary/default) with bottom-bar/text
|
|
5398
|
+
* (text/on/primary), in light, dark and every brand. Full width (at least 320), sticky at the bottom of the scrolling
|
|
5399
|
+
* container, bottom-bar/padding-x (24) at the sides, bottom-bar/gap (16) between the parts.
|
|
5400
|
+
* Height, by the screen width as the Figma viewport mode (layout/compact below lg 1024): compact, bottom-bar/padding-y
|
|
5401
|
+
* (10) above and below, 64 = 10 + 44 + 10, plus the safe area at the bottom; expanded, 2 more inside each side, 68.
|
|
5402
|
+
* The leading slot reserves bottom-bar/leading/min-width (44, the md Button height).
|
|
5403
|
+
* floating, from 1024 only: bottom-bar/floating/margin off the foot and the sides, at most
|
|
5404
|
+
* bottom-bar/floating/max-width, centred, bottom-bar/floating/radius and elevation/overlay. Below 1024 it is docked:
|
|
5405
|
+
* no radius, no shadow, no margin, the full width.
|
|
5406
|
+
* The Buttons on it stay the Button: the action fill and the neutral ghost read their tokens, redeclared here in the
|
|
5407
|
+
* bar's colours (bottom-bar/button/*): the fill's hover at 85% over the bar (active 70%), the ghost's hover the label
|
|
5408
|
+
* colour at 15% (active 25%), disabled at 40% over the bar (the Figma layer opacity), the focus ring
|
|
5409
|
+
* bottom-bar/focus/ring (the default focus/ring would vanish on the band).
|
|
5410
|
+
*/
|
|
5411
|
+
.rds-bottom-bar {
|
|
5412
|
+
position: sticky;
|
|
5413
|
+
bottom: 0;
|
|
5414
|
+
z-index: var(--z-sticky);
|
|
5415
|
+
box-sizing: border-box;
|
|
5416
|
+
display: flex;
|
|
5417
|
+
align-items: center;
|
|
5418
|
+
gap: var(--bottom-bar-gap);
|
|
5419
|
+
width: 100%;
|
|
5420
|
+
min-width: min(320px, 100%);
|
|
5421
|
+
padding: calc(var(--bottom-bar-padding-y) + 2px) var(--bottom-bar-padding-x);
|
|
5422
|
+
background: var(--bottom-bar-background);
|
|
5423
|
+
color: var(--bottom-bar-text);
|
|
5424
|
+
font-family: var(--type-font-stack);
|
|
5425
|
+
|
|
5426
|
+
/* The Button tokens the action fill and the neutral ghost read, in the bar's colours. */
|
|
5427
|
+
--button-focus-ring: var(--bottom-bar-focus-ring);
|
|
5428
|
+
--button-action-fill-background-default: var(--bottom-bar-button-fill-background);
|
|
5429
|
+
--button-action-fill-background-hover: color-mix(in srgb, var(--bottom-bar-button-fill-background) 85%, var(--bottom-bar-background));
|
|
5430
|
+
--button-action-fill-background-active: color-mix(in srgb, var(--bottom-bar-button-fill-background) 70%, var(--bottom-bar-background));
|
|
5431
|
+
--button-action-fill-label: var(--bottom-bar-button-fill-label);
|
|
5432
|
+
--button-action-fill-icon: var(--bottom-bar-button-fill-label);
|
|
5433
|
+
--button-neutral-ghost-label: var(--bottom-bar-button-ghost-label);
|
|
5434
|
+
--button-neutral-ghost-icon: var(--bottom-bar-button-ghost-label);
|
|
5435
|
+
--button-neutral-ghost-background-hover: color-mix(in srgb, var(--bottom-bar-button-ghost-label) 15%, transparent);
|
|
5436
|
+
--button-neutral-ghost-background-active: color-mix(in srgb, var(--bottom-bar-button-ghost-label) 25%, transparent);
|
|
5437
|
+
--button-disabled-fill-background: color-mix(in srgb, var(--bottom-bar-button-fill-background) 40%, var(--bottom-bar-background));
|
|
5438
|
+
--button-disabled-fill-label: color-mix(in srgb, var(--bottom-bar-button-fill-label) 40%, var(--bottom-bar-background));
|
|
5439
|
+
--button-disabled-fill-icon: var(--button-disabled-fill-label);
|
|
5440
|
+
--button-disabled-ghost-label: color-mix(in srgb, var(--bottom-bar-button-ghost-label) 40%, var(--bottom-bar-background));
|
|
5441
|
+
--button-disabled-ghost-icon: var(--button-disabled-ghost-label);
|
|
5442
|
+
}
|
|
5443
|
+
|
|
5444
|
+
/* The leading slot: one ~44 control; its width stays reserved while the control comes and goes. */
|
|
5445
|
+
.rds-bottom-bar__leading {
|
|
5446
|
+
display: flex;
|
|
5447
|
+
flex: none;
|
|
5448
|
+
align-items: center;
|
|
5449
|
+
min-width: var(--bottom-bar-leading-min-width);
|
|
5450
|
+
}
|
|
5451
|
+
|
|
5452
|
+
/* Compact: by the screen width (the Figma viewport mode), not a container query. */
|
|
5453
|
+
@media (max-width: 1023px) {
|
|
5454
|
+
.rds-bottom-bar {
|
|
5455
|
+
padding-block: var(--bottom-bar-padding-y);
|
|
5456
|
+
padding-bottom: calc(var(--bottom-bar-padding-y) + env(safe-area-inset-bottom, 0px));
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
|
|
5460
|
+
/* floating: only from lg 1024; below it the bar is docked, square, edge to edge. */
|
|
5461
|
+
@media (min-width: 1024px) {
|
|
5462
|
+
.rds-bottom-bar--floating {
|
|
5463
|
+
bottom: var(--bottom-bar-floating-margin);
|
|
5464
|
+
width: calc(100% - 2 * var(--bottom-bar-floating-margin));
|
|
5465
|
+
max-width: var(--bottom-bar-floating-max-width);
|
|
5466
|
+
margin-inline: auto;
|
|
5467
|
+
margin-bottom: var(--bottom-bar-floating-margin);
|
|
5468
|
+
border-radius: var(--bottom-bar-floating-radius);
|
|
5469
|
+
box-shadow: var(--elevation-overlay);
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5472
|
+
|
|
5473
|
+
@media (forced-colors: active) {
|
|
5474
|
+
.rds-bottom-bar {
|
|
5475
|
+
border-top: var(--border-width) solid CanvasText;
|
|
5476
|
+
}
|
|
5477
|
+
}
|
|
5478
|
+
|
|
5479
|
+
@media (forced-colors: active) and (min-width: 1024px) {
|
|
5480
|
+
.rds-bottom-bar--floating {
|
|
5481
|
+
border: var(--border-width) solid CanvasText;
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
|
|
5485
|
+
/*
|
|
5486
|
+
* The on-screen keyboard (compact only, the class comes from useOnScreenKeyboard): the bar slides down out of the way
|
|
5487
|
+
* while a field is being typed in, and comes back when the keyboard closes. visibility takes it out of the tab order
|
|
5488
|
+
* and the accessibility tree meanwhile; it waits for the slide to end before hiding.
|
|
5489
|
+
*/
|
|
5490
|
+
@media (max-width: 1023px) {
|
|
5491
|
+
.rds-bottom-bar--keyboard {
|
|
5492
|
+
visibility: hidden;
|
|
5493
|
+
transform: translateY(100%);
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
|
|
5497
|
+
@media (max-width: 1023px) and (prefers-reduced-motion: no-preference) {
|
|
5498
|
+
.rds-bottom-bar {
|
|
5499
|
+
transition:
|
|
5500
|
+
transform var(--motion-duration-base) var(--motion-easing-enter),
|
|
5501
|
+
visibility 0s;
|
|
5502
|
+
}
|
|
5503
|
+
|
|
5504
|
+
.rds-bottom-bar--keyboard {
|
|
5505
|
+
transition:
|
|
5506
|
+
transform var(--motion-duration-base) var(--motion-easing-exit),
|
|
5507
|
+
visibility 0s var(--motion-duration-base);
|
|
5508
|
+
}
|
|
5509
|
+
}
|
|
5510
|
+
|
|
5564
5511
|
/* components/internal/choice.css */
|
|
5565
5512
|
/* @tokens internal */
|
|
5566
5513
|
/*
|
|
@@ -8662,61 +8609,15 @@ input:focus-visible + .rds-radio__circle {
|
|
|
8662
8609
|
}
|
|
8663
8610
|
|
|
8664
8611
|
/* components/saving-bar.css */
|
|
8665
|
-
/* @tokens
|
|
8612
|
+
/* @tokens bottom-bar */
|
|
8666
8613
|
/*
|
|
8667
|
-
* SavingBar
|
|
8668
|
-
*
|
|
8669
|
-
*
|
|
8670
|
-
*
|
|
8671
|
-
*
|
|
8672
|
-
*
|
|
8673
|
-
* savingbar/button/fill/label, its hover the fill at 85% over the bar (active 70%); the Descartar label is
|
|
8674
|
-
* savingbar/button/ghost/label, its hover the label colour at 15% (active 25%); the focus ring is savingbar/focus/ring;
|
|
8675
|
-
* disabled (Descartar while saving) is the Button at 40% over the bar, as the Figma layer opacity. While saving, Salvar
|
|
8676
|
-
* is in loading (its loader in the label colour): the only indicator, none in the message.
|
|
8677
|
-
* Two arrangements, by the screen width, as the Figma viewport mode (layout/compact below lg 1024): expanded from 1024,
|
|
8678
|
-
* 12 24 padding (68 tall), the message grows (at least 160) beside Descartar and Salvar; compact below it, one row,
|
|
8679
|
-
* 10 24 padding (64 = 10 + 44 + 10) plus the safe area at the bottom, the message (flex 1, at most 2 lines) on the
|
|
8680
|
-
* left and only the main button on the right: Descartar is not shown (it lives in the details Drawer's footer or in
|
|
8681
|
-
* the confirmation when leaving).
|
|
8682
|
-
* Details (showDetails, onDetails in code): in compact, the unsaved message becomes a text button with chevron-up 20 at
|
|
8683
|
-
* the right (12 apart) that opens the Drawer with what is missing; hover underlines, focus draws the ring
|
|
8684
|
-
* savingbar/focus/ring (focus/ring-width, radius/focus) 4 around the message box, inside stroke, as the Figma
|
|
8685
|
-
* focus-ring frame. The touch area is 44 tall without changing the drawing. Expanded, the message stays text.
|
|
8614
|
+
* SavingBar: no tokens of its own; the shell (internal/bottom-bar.css, bottom-bar/*) brings the colour, the height,
|
|
8615
|
+
* the padding, the place and the Buttons' colours. Here the message (label, medium, in bottom-bar/text, at most 2
|
|
8616
|
+
* lines with an ellipsis; on error the alert 20 before it, 12 apart) and the actions (8 apart, Descartar then Salvar).
|
|
8617
|
+
* Expanded (from lg 1024) the message grows (at least 160) beside Descartar and Salvar; compact (below 1024, by the
|
|
8618
|
+
* screen width as the Figma viewport mode) one row, the message (flex 1) on the left and only Salvar on the right:
|
|
8619
|
+
* Descartar is not shown (it lives in the confirmation when leaving).
|
|
8686
8620
|
*/
|
|
8687
|
-
.rds-savingbar {
|
|
8688
|
-
box-sizing: border-box;
|
|
8689
|
-
width: 100%;
|
|
8690
|
-
min-width: min(320px, 100%);
|
|
8691
|
-
padding: var(--space-12) var(--space-24);
|
|
8692
|
-
background: var(--savingbar-background);
|
|
8693
|
-
color: var(--savingbar-text);
|
|
8694
|
-
font-family: var(--type-font-stack);
|
|
8695
|
-
|
|
8696
|
-
/* The Button tokens Salvar (action fill) and Descartar (neutral ghost) read, in the bar's colours. */
|
|
8697
|
-
--button-focus-ring: var(--savingbar-focus-ring);
|
|
8698
|
-
--button-action-fill-background-default: var(--savingbar-button-fill-background);
|
|
8699
|
-
--button-action-fill-background-hover: color-mix(in srgb, var(--savingbar-button-fill-background) 85%, var(--savingbar-background));
|
|
8700
|
-
--button-action-fill-background-active: color-mix(in srgb, var(--savingbar-button-fill-background) 70%, var(--savingbar-background));
|
|
8701
|
-
--button-action-fill-label: var(--savingbar-button-fill-label);
|
|
8702
|
-
--button-action-fill-icon: var(--savingbar-button-fill-label);
|
|
8703
|
-
--button-neutral-ghost-label: var(--savingbar-button-ghost-label);
|
|
8704
|
-
--button-neutral-ghost-icon: var(--savingbar-button-ghost-label);
|
|
8705
|
-
--button-neutral-ghost-background-hover: color-mix(in srgb, var(--savingbar-button-ghost-label) 15%, transparent);
|
|
8706
|
-
--button-neutral-ghost-background-active: color-mix(in srgb, var(--savingbar-button-ghost-label) 25%, transparent);
|
|
8707
|
-
--button-disabled-fill-background: color-mix(in srgb, var(--savingbar-button-fill-background) 40%, var(--savingbar-background));
|
|
8708
|
-
--button-disabled-fill-label: color-mix(in srgb, var(--savingbar-button-fill-label) 40%, var(--savingbar-background));
|
|
8709
|
-
--button-disabled-fill-icon: var(--button-disabled-fill-label);
|
|
8710
|
-
--button-disabled-ghost-label: color-mix(in srgb, var(--savingbar-button-ghost-label) 40%, var(--savingbar-background));
|
|
8711
|
-
--button-disabled-ghost-icon: var(--button-disabled-ghost-label);
|
|
8712
|
-
}
|
|
8713
|
-
|
|
8714
|
-
.rds-savingbar__row {
|
|
8715
|
-
display: flex;
|
|
8716
|
-
align-items: center;
|
|
8717
|
-
gap: var(--space-16);
|
|
8718
|
-
}
|
|
8719
|
-
|
|
8720
8621
|
.rds-savingbar__message {
|
|
8721
8622
|
display: flex;
|
|
8722
8623
|
flex: 1 1 160px;
|
|
@@ -8757,51 +8658,7 @@ input:focus-visible + .rds-radio__circle {
|
|
|
8757
8658
|
gap: var(--space-8);
|
|
8758
8659
|
}
|
|
8759
8660
|
|
|
8760
|
-
.rds-savingbar__details {
|
|
8761
|
-
position: relative;
|
|
8762
|
-
display: none;
|
|
8763
|
-
flex: 1;
|
|
8764
|
-
align-items: center;
|
|
8765
|
-
gap: var(--space-12);
|
|
8766
|
-
min-width: 0;
|
|
8767
|
-
margin: 0;
|
|
8768
|
-
padding: 0;
|
|
8769
|
-
border: 0;
|
|
8770
|
-
border-radius: var(--radius-focus);
|
|
8771
|
-
background: none;
|
|
8772
|
-
color: inherit;
|
|
8773
|
-
font: inherit;
|
|
8774
|
-
text-align: start;
|
|
8775
|
-
cursor: pointer;
|
|
8776
|
-
}
|
|
8777
|
-
|
|
8778
|
-
/* The touch area: 44 tall around the message, invisible. */
|
|
8779
|
-
.rds-savingbar__details::after {
|
|
8780
|
-
content: '';
|
|
8781
|
-
position: absolute;
|
|
8782
|
-
top: 50%;
|
|
8783
|
-
right: 0;
|
|
8784
|
-
left: 0;
|
|
8785
|
-
min-height: 44px;
|
|
8786
|
-
height: 100%;
|
|
8787
|
-
transform: translateY(-50%);
|
|
8788
|
-
}
|
|
8789
|
-
|
|
8790
|
-
.rds-savingbar__details:hover .rds-savingbar__text {
|
|
8791
|
-
text-decoration: underline;
|
|
8792
|
-
}
|
|
8793
|
-
|
|
8794
|
-
.rds-savingbar__details:focus-visible {
|
|
8795
|
-
outline: var(--focus-ring-width) solid var(--savingbar-focus-ring);
|
|
8796
|
-
outline-offset: var(--focus-ring-width);
|
|
8797
|
-
}
|
|
8798
|
-
|
|
8799
8661
|
@media (max-width: 1023px) {
|
|
8800
|
-
.rds-savingbar {
|
|
8801
|
-
padding-block: 10px;
|
|
8802
|
-
padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
|
|
8803
|
-
}
|
|
8804
|
-
|
|
8805
8662
|
.rds-savingbar__message {
|
|
8806
8663
|
flex: 1 1 0;
|
|
8807
8664
|
min-width: 0;
|
|
@@ -8810,20 +8667,6 @@ input:focus-visible + .rds-radio__circle {
|
|
|
8810
8667
|
.rds-savingbar .rds-savingbar__discard {
|
|
8811
8668
|
display: none;
|
|
8812
8669
|
}
|
|
8813
|
-
|
|
8814
|
-
.rds-savingbar__message--details > .rds-savingbar__text {
|
|
8815
|
-
display: none;
|
|
8816
|
-
}
|
|
8817
|
-
|
|
8818
|
-
.rds-savingbar__details {
|
|
8819
|
-
display: flex;
|
|
8820
|
-
}
|
|
8821
|
-
}
|
|
8822
|
-
|
|
8823
|
-
@media (forced-colors: active) {
|
|
8824
|
-
.rds-savingbar {
|
|
8825
|
-
border-top: var(--border-width) solid CanvasText;
|
|
8826
|
-
}
|
|
8827
8670
|
}
|
|
8828
8671
|
|
|
8829
8672
|
/* components/section-card.css */
|
|
@@ -10289,8 +10132,18 @@ input:focus-visible + .rds-switch__track {
|
|
|
10289
10132
|
* A line under every row but the last: the Table's border closes it. Corners in radius/container; the sort button in
|
|
10290
10133
|
* radius/xs. The status rows hold an Empty, 32 24 around it.
|
|
10291
10134
|
*/
|
|
10135
|
+
/*
|
|
10136
|
+
* The scroll region is a one-column grid, minmax(0, 1fr), so it does not hand the table's min-content up to the page:
|
|
10137
|
+
* as a plain block it did, and any ancestor sized by its content (a flex item without min-width: 0, a grid item in an
|
|
10138
|
+
* auto track, a start-aligned stack, an inline-block) grew to the table's width, so a Card holding a wide table went
|
|
10139
|
+
* past the screen and was cut instead of the table scrolling. Under a min-content constraint the fr track is 0; under
|
|
10140
|
+
* max-content it is the table's max-content (a hugging parent still hugs the table); with a definite width it fills,
|
|
10141
|
+
* and the table, never narrower than its min-content, scrolls inside.
|
|
10142
|
+
*/
|
|
10292
10143
|
.rds-table {
|
|
10293
10144
|
box-sizing: border-box;
|
|
10145
|
+
display: grid;
|
|
10146
|
+
grid-template-columns: minmax(0, 1fr);
|
|
10294
10147
|
max-width: 100%;
|
|
10295
10148
|
overflow-x: auto;
|
|
10296
10149
|
border: var(--border-width) solid var(--table-border);
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
--button-inverse-focus-ring: var(--text-on-primary-strong);
|
|
74
74
|
--button-inverse-disabled-fill-background: var(--colors-primary-light);
|
|
75
75
|
--button-inverse-disabled-label: var(--text-disabled-invert);
|
|
76
|
-
--
|
|
76
|
+
--bottom-bar-background: var(--colors-primary-default);
|
|
77
77
|
--toggle-label: var(--text-body);
|
|
78
78
|
--toggle-border: var(--border-strong);
|
|
79
79
|
--toggle-background-hover: var(--surface-muted);
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
--toggle-label-disabled: var(--text-disabled);
|
|
84
84
|
--toggle-border-pressed: var(--colors-primary-default);
|
|
85
85
|
--toggle-border-disabled: var(--border-default);
|
|
86
|
-
--
|
|
86
|
+
--bottom-bar-text: var(--text-on-primary);
|
|
87
87
|
--fab-cta-background-default: var(--colors-primary-default);
|
|
88
88
|
--fab-cta-background-hover: var(--colors-primary-active);
|
|
89
89
|
--fab-cta-text: var(--text-on-primary);
|
|
@@ -93,9 +93,6 @@
|
|
|
93
93
|
--chip-label: var(--text-body);
|
|
94
94
|
--chip-icon: var(--text-muted);
|
|
95
95
|
--chip-label-disabled: var(--text-disabled);
|
|
96
|
-
--form-actions-background: var(--surface-card);
|
|
97
|
-
--form-actions-border: var(--border-default);
|
|
98
|
-
--form-actions-text: var(--text-subtle);
|
|
99
96
|
--button-size-sm-height: 36px;
|
|
100
97
|
--button-size-sm-padding-x: var(--space-16);
|
|
101
98
|
--button-size-sm-padding-y: var(--space-8);
|
|
@@ -120,12 +117,17 @@
|
|
|
120
117
|
--button-size-lg-icon-only: var(--size-24);
|
|
121
118
|
--button-size-lg-text-size: var(--type-body-size);
|
|
122
119
|
--button-size-lg-text-line: var(--type-body-line);
|
|
123
|
-
--
|
|
124
|
-
--
|
|
125
|
-
--
|
|
126
|
-
--
|
|
127
|
-
--
|
|
128
|
-
--
|
|
120
|
+
--bottom-bar-focus-ring: var(--text-on-primary);
|
|
121
|
+
--bottom-bar-button-fill-background: var(--text-on-primary);
|
|
122
|
+
--bottom-bar-button-fill-label: var(--colors-primary-default);
|
|
123
|
+
--bottom-bar-button-ghost-label: var(--text-on-primary);
|
|
124
|
+
--bottom-bar-padding-x: var(--space-24);
|
|
125
|
+
--bottom-bar-padding-y: 10px;
|
|
126
|
+
--bottom-bar-gap: var(--space-16);
|
|
127
|
+
--bottom-bar-leading-min-width: var(--button-size-md-height);
|
|
128
|
+
--bottom-bar-floating-radius: var(--radius-container);
|
|
129
|
+
--bottom-bar-floating-margin: var(--space-24);
|
|
130
|
+
--bottom-bar-floating-max-width: 768px;
|
|
129
131
|
|
|
130
132
|
/* blocks */
|
|
131
133
|
--topnav-background: var(--surface-card);
|