@cloudgrid-io/ui 0.27.1 → 0.27.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radio-group.d.ts","sourceRoot":"","sources":["../../src/ui/radio-group.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,EAAE,UAAU,IAAI,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAwE9E,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,KAAK,EACL,YAAY,EACZ,aAAa,EACb,GAAG,KAAK,EACT,EAAE,mBAAmB,CAAC,KAAK,qBAsB3B;AAED,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,qBAgBhE;AAED,iBAAS,WAAW,CAAC,EACnB,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,GAAG;IACzD,sDAAsD;IACtD,KAAK,EAAE,OAAO,CAAA;IACd,2CAA2C;IAC3C,KAAK,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,mFAAmF;IACnF,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B,qBA+DA;AAED,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,CAAA"}
@@ -0,0 +1,101 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Radio as RadioPrimitive } from "@base-ui/react/radio";
5
+ import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group";
6
+ import { cn } from "../lib/cloudgrid-cn.js";
7
+ /**
8
+ * One of a set. `RadioGroup` is the set, `Radio` is the control, and
9
+ * `RadioOption` is the row the visibility editor is built out of: control,
10
+ * label, one line of description, and a slot that appears only while that
11
+ * option is the selected one (#226).
12
+ *
13
+ * ## Why this is Base UI's radio and not a styled input
14
+ *
15
+ * A radio group has ONE tab stop and roving focus: arrows move the selection,
16
+ * Space selects, Tab leaves the group. A column of `<input type="radio">`
17
+ * elements with the same `name` gets that from the browser; a column of styled
18
+ * divs does not, and the difference is invisible in a screenshot and in review.
19
+ * `OnboardingQuestion` (#119) already made this argument for its `single` mode
20
+ * and reached for the same primitive.
21
+ *
22
+ * ## The option row, and the name it must NOT have
23
+ *
24
+ * `RadioOption` wraps the control and both lines of text in a `<label>`, which
25
+ * is what makes the whole row clickable rather than the 16px circle.
26
+ *
27
+ * It then passes `aria-labelledby` and `aria-describedby` EXPLICITLY, and that
28
+ * is the load-bearing part. Base UI names a `role="radio"` span by looking for
29
+ * an associated `<label>` and using its id, which is right for its own demo
30
+ * where the label holds one word. Here the label holds the description too, so
31
+ * the fallback would announce "These spaces. Members of the spaces you choose."
32
+ * as the option's NAME on every row. The name is the label, the description is a
33
+ * description, and nothing goes red if they are collapsed into one.
34
+ *
35
+ * ## The child slot is a sibling, so the group has to carry its value
36
+ *
37
+ * The nested space list in the frame sits UNDER its option, indented, not inside
38
+ * the control. So neither `Radio.Root`'s state-function `className` nor its
39
+ * `render` prop can reach it, and Base UI does not export the group's context.
40
+ *
41
+ * `RadioGroup` mirrors the selected value into a context of its own for that one
42
+ * question: is this option the selected one. The Base UI group stays the source
43
+ * of truth in both directions — pass `value` for a controlled group,
44
+ * `defaultValue` for an uncontrolled one — so the mirror never becomes a second
45
+ * place the answer lives. Same shape `OnboardingQuestion` uses for its Continue
46
+ * button.
47
+ *
48
+ * The slot is not rendered at all when the option is unselected, rather than
49
+ * rendered and hidden. A hidden slot keeps its checkboxes in the tab order,
50
+ * which is a set of controls a keyboard can reach and a reader cannot see.
51
+ *
52
+ * ## Tokens, and the one utility this deliberately does not copy
53
+ *
54
+ * `checkbox.tsx` grows its hit area with `after:-inset-x-3 after:-inset-y-2`.
55
+ * That is right for a checkbox sitting on its own and wrong here: the rows in
56
+ * this frame are about 37px apart, so a 32px invisible overlay per control
57
+ * overlaps its neighbour and a click near the boundary selects the wrong option.
58
+ * The row is the target instead, and it is wider and taller than any overlay.
59
+ *
60
+ * Colour comes from `--primary` through the shadcn bridge, which is declared per
61
+ * surface scope, so the selected ring and dot are brand purple on the page and
62
+ * brand lime on both dark grounds. Selection is never colour alone: the dot is a
63
+ * shape that is either there or not.
64
+ */
65
+ /**
66
+ * The selected value, for `RadioOption`'s slot and nothing else.
67
+ *
68
+ * `undefined` means "no RadioGroup above me", which is also what "nothing
69
+ * selected" looks like. Both answer `false` to every option, which is the
70
+ * correct behaviour for a `Radio` used outside a group.
71
+ */
72
+ const RadioGroupValueContext = React.createContext(undefined);
73
+ function RadioGroup({ className, value, defaultValue, onValueChange, ...props }) {
74
+ // The mirror. Initialised from `defaultValue` and updated on every change, so
75
+ // an uncontrolled group can still answer the slot's question. A controlled
76
+ // group ignores it entirely: `value` is read first.
77
+ const [uncontrolled, setUncontrolled] = React.useState(defaultValue);
78
+ const selected = value !== undefined ? value : uncontrolled;
79
+ return (_jsx(RadioGroupValueContext.Provider, { value: selected, children: _jsx(RadioGroupPrimitive, { "data-slot": "radio-group", className: cn("grid gap-3", className), value: value, defaultValue: defaultValue, onValueChange: (next, details) => {
80
+ setUncontrolled(next);
81
+ onValueChange?.(next, details);
82
+ }, ...props }) }));
83
+ }
84
+ function Radio({ className, ...props }) {
85
+ return (_jsx(RadioPrimitive.Root, { "data-slot": "radio", className: cn("peer flex size-4 shrink-0 items-center justify-center rounded-pill border border-input p-0 transition-colors outline-none group-has-disabled/field:opacity-50 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-checked:border-primary data-checked:text-primary data-disabled:cursor-not-allowed data-disabled:opacity-50", className), ...props, children: _jsx(RadioPrimitive.Indicator, { "data-slot": "radio-indicator", className: "size-2 shrink-0 rounded-pill bg-current" }) }));
86
+ }
87
+ function RadioOption({ value, label, description, disabled, className, children, ...props }) {
88
+ const selected = React.useContext(RadioGroupValueContext);
89
+ const checked = selected === value;
90
+ const id = React.useId();
91
+ const labelId = `${id}-label`;
92
+ const descriptionId = `${id}-description`;
93
+ return (_jsxs("div", { "data-slot": "radio-option", "data-checked": checked ? "" : undefined, "data-disabled": disabled ? "" : undefined, className: cn("grid gap-1.5", className), ...props, children: [_jsxs("label", { className: cn("flex cursor-pointer items-start gap-2.5 select-none", disabled && "cursor-not-allowed opacity-50"), children: [_jsx(Radio, { value: value, disabled: disabled, "aria-labelledby": labelId, "aria-describedby": description ? descriptionId : undefined,
94
+ // Optically centred on the label's first line rather than its box.
95
+ className: "mt-0.5" }), _jsxs("span", { className: "grid gap-0.5", children: [_jsx("span", { id: labelId, "data-slot": "radio-option-label", className: "text-body-sm text-ink", children: label }), description ? (_jsx("span", { id: descriptionId, "data-slot": "radio-option-description", className: "text-caption text-faint", children: description })) : null] })] }), children && checked ? (_jsx("div", { "data-slot": "radio-option-detail",
96
+ // Indented to the label's edge: the control is 16px and the gap is
97
+ // 10px, so the text column starts at 26px.
98
+ className: "grid gap-2 ps-[26px]", children: children })) : null] }));
99
+ }
100
+ export { Radio, RadioGroup, RadioOption };
101
+ //# sourceMappingURL=radio-group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radio-group.js","sourceRoot":"","sources":["../../src/ui/radio-group.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/ui",
3
- "version": "0.27.1",
3
+ "version": "0.27.2",
4
4
  "description": "CloudGrid design system: React components, brand design tokens, Tailwind theme, self-hosted fonts.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",