@rojaostudio/ds 2.0.0-next.24 → 2.0.0-next.26

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.
@@ -1,28 +1,60 @@
1
1
  import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
3
 
4
- type FormActionsLayout = 'inline' | 'stacked';
4
+ type FormActionsLayout = 'inline' | 'stacked' | 'bar';
5
5
  interface FormActionsProps extends HTMLAttributes<HTMLDivElement> {
6
6
  /**
7
7
  * The buttons (Figma: slot `actions`, up to 3), always in reading order: Cancelar first, the primary
8
8
  * last. In `stacked` the primary goes on top, and it is moved first in the page too, so the focus
9
- * order follows what is seen.
9
+ * order follows what is seen. In `bar`, compact (below 1024), only the last one (the primary) is shown.
10
10
  */
11
11
  children: ReactNode;
12
- /** A short supporting sentence beside the actions (Figma: `showHelper` + `helper`). */
12
+ /**
13
+ * A short supporting sentence beside the actions (Figma: `showHelper` + `helper`). It is a live region
14
+ * (`role="status"`): when it changes (say, "Falta preço e prazo"), it is said without moving focus.
15
+ */
13
16
  helper?: ReactNode;
14
17
  /**
15
- * inline (desktop: floats and hugs its content, sentence left, actions right) or stacked (phone: edge
16
- * to edge at the foot, primary full width on top) (Figma: `layout`).
18
+ * inline (desktop: floats and hugs its content, sentence left, actions right), stacked (phone, a task
19
+ * with no way out in the topbar: edge to edge at the foot, primary full width on top) or bar (the foot of
20
+ * a create form: the SavingBar's skeleton, neutral; expanded from 1024, compact in one row below it)
21
+ * (Figma: `layout`).
17
22
  */
18
23
  layout?: FormActionsLayout;
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;
19
35
  }
20
36
  /**
21
37
  * FormActions — Figma [RDS] Actions/FormActions. The actions at the end of a create form or a task,
22
38
  * there from the first second. Where it sticks is the page's job: `position: sticky` at the bottom of
23
- * the scrolling container, with `scroll-padding-bottom` equal to its height, so a focused field is never
24
- * hidden behind it (WCAG 2.4.11). Styles: form-actions.css.
39
+ * the scrolling container. Styles: form-actions.css.
40
+ *
41
+ * `layout="bar"` is the foot of a create form: the SavingBar's skeleton (68 expanded, 64 plus the safe area
42
+ * compact, 24 at the sides, the primary in the same place), but neutral: form-actions/bar/background
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.
47
+ *
48
+ * 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
+ * first pending one.
51
+ * - The scrolling container takes `scroll-padding-bottom` equal to the bar's height plus
52
+ * `env(safe-area-inset-bottom)`, so a focused field is never hidden behind it (WCAG 2.4.11).
53
+ * - On the phone the bar leaves while the on-screen keyboard is open (it would cover the field being typed).
54
+ * - Compact has no Cancelar: the way out is back or X in the topbar, with an AlertDialog "Sair sem criar?"
55
+ * ([Continuar editando] [Sair sem criar]) when something was typed. Cancelar can also go in the footer of
56
+ * the details Drawer.
25
57
  */
26
- declare function FormActions({ children, helper, layout, className, ...rest }: FormActionsProps): react.JSX.Element;
58
+ declare function FormActions({ children, helper, layout, onDetails, detailsLabel, detailsExpanded, detailsControls, className, ...rest }: FormActionsProps): react.JSX.Element;
27
59
 
28
60
  export { FormActions, type FormActionsLayout, type FormActionsProps };
@@ -3,17 +3,65 @@ import {
3
3
  __spreadProps,
4
4
  __spreadValues
5
5
  } from "../chunk-ORMEWXMH.js";
6
- import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { Children } from "react";
8
+ import { ChevronUpIcon } from "./internal/icons.js";
8
9
  function FormActions(_a) {
9
- var _b = _a, { children, helper, layout = "inline", className } = _b, rest = __objRest(_b, ["children", "helper", "layout", "className"]);
10
+ var _b = _a, {
11
+ children,
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
+ ]);
10
29
  const actions = Children.toArray(children);
30
+ const bar = layout === "bar";
31
+ const details = bar && helper != null && helper !== false && onDetails !== void 0;
11
32
  return /* @__PURE__ */ jsxs(
12
33
  "div",
13
34
  __spreadProps(__spreadValues({}, rest), {
14
35
  className: ["rds-form-actions", `rds-form-actions--${layout}`, className].filter(Boolean).join(" "),
15
36
  children: [
16
- helper && /* @__PURE__ */ jsx("p", { className: "rds-form-actions__helper", children: helper }),
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
+ ),
17
65
  /* @__PURE__ */ jsx("div", { className: "rds-form-actions__actions", children: layout === "stacked" ? actions.reverse() : actions })
18
66
  ]
19
67
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rojaostudio/ds",
3
- "version": "2.0.0-next.24",
3
+ "version": "2.0.0-next.26",
4
4
  "type": "module",
5
5
  "description": "Rojão Design System — componentes React, estilos e alvo React Native. O motor vive em @rojaostudio/ds-core.",
6
6
  "license": "MIT",
@@ -190,7 +190,7 @@
190
190
  "@radix-ui/react-toggle": "^1.1.18",
191
191
  "@radix-ui/react-toggle-group": "^1.1.19",
192
192
  "@radix-ui/react-tooltip": "^1.2.16",
193
- "@rojaostudio/ds-core": "1.1.0-next.9"
193
+ "@rojaostudio/ds-core": "1.1.0-next.10"
194
194
  },
195
195
  "scripts": {
196
196
  "build": "pnpm run build:tokens && pnpm run build:themes && pnpm run build:rds && pnpm run build:native && tsup",
@@ -4639,6 +4639,16 @@ input:focus-visible + .rds-checkbox__box {
4639
4639
  * FormActions (Actions collection, form-actions/*): the actions at the end of a form, on the card
4640
4640
  * surface with a hairline. inline floats (container radius, raised elevation, 16 24 padding, 24 between
4641
4641
  * sentence and actions); stacked sticks to the foot edge to edge (16 padding, hairline on top).
4642
+ * bar is the foot of a create form, the SavingBar's skeleton but neutral: form-actions/bar/background
4643
+ * (surface/page) with the hairline form-actions/border on top (drawn inside, so it adds no height), full
4644
+ * width (at least 320), 16 between the helper and the actions (8 apart). Two arrangements by the screen
4645
+ * width, as the SavingBar (layout/compact below lg 1024): expanded, 12 24 padding (68 = 12 + 44 + 12), the
4646
+ * helper grows (at least 160) beside Cancelar and the primary; compact, one row, 10 24 padding
4647
+ * (64 = 10 + 44 + 10) plus the safe area at the bottom, the helper (flex 1, at most 2 lines) on the left and
4648
+ * only the last action (the primary) on the right.
4649
+ * Details (showDetails, onDetails in code): compact, the helper becomes a text button with chevron-up 20 at
4650
+ * the right (12 apart); hover underlines, focus draws form-actions/focus/ring (focus/ring-width, radius/focus);
4651
+ * the touch area is 44 tall without changing the drawing. Expanded, the helper stays text.
4642
4652
  */
4643
4653
  .rds-form-actions {
4644
4654
  box-sizing: border-box;
@@ -4694,10 +4704,125 @@ input:focus-visible + .rds-checkbox__box {
4694
4704
  width: 100%;
4695
4705
  }
4696
4706
 
4707
+ /* bar: the foot of a create form, the SavingBar's skeleton, neutral. */
4708
+ .rds-form-actions--bar {
4709
+ gap: var(--space-16);
4710
+ justify-content: flex-end;
4711
+ width: 100%;
4712
+ min-width: min(320px, 100%);
4713
+ padding: var(--space-12) var(--space-24);
4714
+ background: var(--form-actions-bar-background);
4715
+ /* The hairline on top, inside: the bar stays 68 / 64 tall. */
4716
+ box-shadow: inset 0 var(--border-width) 0 var(--form-actions-border);
4717
+ }
4718
+
4719
+ .rds-form-actions--bar .rds-form-actions__helper {
4720
+ display: flex;
4721
+ flex: 1 1 160px;
4722
+ align-items: center;
4723
+ min-width: 160px;
4724
+ }
4725
+
4726
+ .rds-form-actions--bar .rds-form-actions__actions {
4727
+ flex: none;
4728
+ flex-wrap: nowrap;
4729
+ }
4730
+
4731
+ .rds-form-actions__text {
4732
+ display: -webkit-box;
4733
+ flex: 1;
4734
+ min-width: 0;
4735
+ overflow: hidden;
4736
+ -webkit-box-orient: vertical;
4737
+ -webkit-line-clamp: 2;
4738
+ line-clamp: 2;
4739
+ }
4740
+
4741
+ .rds-form-actions__icon {
4742
+ display: inline-flex;
4743
+ flex: none;
4744
+ width: var(--size-20);
4745
+ height: var(--size-20);
4746
+ }
4747
+
4748
+ .rds-form-actions__icon > svg {
4749
+ width: 100%;
4750
+ height: 100%;
4751
+ }
4752
+
4753
+ .rds-form-actions__details {
4754
+ position: relative;
4755
+ display: none;
4756
+ flex: 1;
4757
+ align-items: center;
4758
+ gap: var(--space-12);
4759
+ min-width: 0;
4760
+ margin: 0;
4761
+ padding: 0;
4762
+ border: 0;
4763
+ border-radius: var(--radius-focus);
4764
+ background: none;
4765
+ color: inherit;
4766
+ font: inherit;
4767
+ text-align: start;
4768
+ cursor: pointer;
4769
+ }
4770
+
4771
+ /* The touch area: 44 tall around the helper, invisible. */
4772
+ .rds-form-actions__details::after {
4773
+ content: '';
4774
+ position: absolute;
4775
+ top: 50%;
4776
+ right: 0;
4777
+ left: 0;
4778
+ min-height: 44px;
4779
+ height: 100%;
4780
+ transform: translateY(-50%);
4781
+ }
4782
+
4783
+ .rds-form-actions__details:hover .rds-form-actions__text {
4784
+ text-decoration: underline;
4785
+ }
4786
+
4787
+ .rds-form-actions__details:focus-visible {
4788
+ outline: var(--focus-ring-width) solid var(--form-actions-focus-ring);
4789
+ outline-offset: var(--focus-ring-width);
4790
+ }
4791
+
4792
+ /* Compact: by the screen width (the Figma viewport mode), as the SavingBar, not a container query. */
4793
+ @media (max-width: 1023px) {
4794
+ .rds-form-actions--bar {
4795
+ padding-block: 10px;
4796
+ padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
4797
+ }
4798
+
4799
+ .rds-form-actions--bar .rds-form-actions__helper {
4800
+ flex: 1 1 0;
4801
+ min-width: 0;
4802
+ }
4803
+
4804
+ /* Only the primary (the last action): Cancelar is the topbar's back or X, or the details Drawer's footer. */
4805
+ .rds-form-actions--bar .rds-form-actions__actions > :not(:last-child) {
4806
+ display: none;
4807
+ }
4808
+
4809
+ .rds-form-actions__helper--details > .rds-form-actions__text {
4810
+ display: none;
4811
+ }
4812
+
4813
+ .rds-form-actions__details {
4814
+ display: flex;
4815
+ }
4816
+ }
4817
+
4697
4818
  @media (forced-colors: active) {
4698
4819
  .rds-form-actions {
4699
4820
  border-color: CanvasText;
4700
4821
  }
4822
+
4823
+ .rds-form-actions--bar {
4824
+ border-top: var(--border-width) solid CanvasText;
4825
+ }
4701
4826
  }
4702
4827
 
4703
4828
  /* components/google-account-chooser.css */
@@ -124,6 +124,8 @@
124
124
  --savingbar-button-fill-background: var(--text-on-primary);
125
125
  --savingbar-button-fill-label: var(--colors-primary-default);
126
126
  --savingbar-button-ghost-label: var(--text-on-primary);
127
+ --form-actions-bar-background: var(--surface-page);
128
+ --form-actions-focus-ring: var(--focus-ring);
127
129
 
128
130
  /* blocks */
129
131
  --topnav-background: var(--surface-card);