|
| 1 | +# Grouping controls (`<Group>`) |
| 2 | + |
| 3 | +What we want: **several controls reading as one object.** A search input and |
| 4 | +its button, a row of segmented buttons, a column of setting rows — when they |
| 5 | +belong together, the eye should see one frame with divisions inside it, not |
| 6 | +three boxes that happen to touch. Concretely that means: one border along each |
| 7 | +seam instead of two side by side, and a radius only on the four outer corners. |
| 8 | + |
| 9 | +`<Group>` is what produces that. Reach for it whenever you place controls |
| 10 | +against each other and any of them has a border or a radius — never hand-write |
| 11 | +negative margins, `border-radius: 0` overrides, or a `borderRadius` prop set to |
| 12 | +`0` on the middle members. Those spellings look right on the case you are |
| 13 | +looking at and break on the next one: a member added at the end, a member |
| 14 | +hidden by a condition (the "first" is then the second child), a switch from row |
| 15 | +to column. |
| 16 | + |
| 17 | +```jsx |
| 18 | +<Group> |
| 19 | + <Input name="search" placeholder="Search…" /> |
| 20 | + <Button>Go</Button> |
| 21 | +</Group> |
| 22 | +``` |
| 23 | + |
| 24 | +Stacked, one setting per row: |
| 25 | + |
| 26 | +```jsx |
| 27 | +<Group row> |
| 28 | + <Picker name="side" ui={…} expandX /> |
| 29 | + <Picker name="level" ui={…} expandX /> |
| 30 | + <Picker name="city" ui={…} expandX /> |
| 31 | +</Group> |
| 32 | +``` |
| 33 | + |
| 34 | +`row` (a `Box` prop — its children are laid out as rows, so they stack |
| 35 | +vertically) is the only prop that changes the arrangement; everything else is |
| 36 | +read off the members themselves. |
| 37 | + |
| 38 | +Live examples: `src/control/demos/15_group_demo.html`. |
| 39 | + |
| 40 | +## What Group does to its members |
| 41 | + |
| 42 | +- **Seams**: each member after the first is pulled back by one border width, so |
| 43 | + the two borders along a seam become one line. The width comes from the |
| 44 | + member's own `--border-width` when it declares one, else from the group's |
| 45 | + `--group-border-width` (default: `--navi-control-border-width`). |
| 46 | +- **Corners**: the first member loses the radius on the side that joins, the |
| 47 | + last one loses it on the other side, and any member in between loses all |
| 48 | + four. A single member keeps its own radius — a group of one looks like the |
| 49 | + control alone. |
| 50 | +- **Overlap order**: the member under the pointer, and the member holding |
| 51 | + focus, paints above its neighbours (`position: relative; z-index: 1`). |
| 52 | + Without it the border color change and the focus ring of the active member |
| 53 | + would be sliced by whichever neighbour is painted after it. The value is `1` |
| 54 | + and there is deliberately no `isolation: isolate` — see |
| 55 | + [z_index.md](./z_index.md). |
| 56 | + |
| 57 | +Nothing else: a group does not restyle its members, does not impose a size, |
| 58 | +and takes any `Box` prop for its own layout. |
| 59 | + |
| 60 | +## Writing a control that belongs in a group |
| 61 | + |
| 62 | +A group squares the corners of its **direct children** — it addresses the |
| 63 | +control root, never the elements inside it. So a control declares the radius |
| 64 | +of its frame on its own root element, and whatever inner element actually |
| 65 | +paints the frame takes `border-radius: inherit`: |
| 66 | + |
| 67 | +```css |
| 68 | +.navi_thing { |
| 69 | + /* The radius is declared here even though the frame below draws it */ |
| 70 | + border-radius: var(--thing-border-radius); |
| 71 | + |
| 72 | + .navi_thing_box { |
| 73 | + border: ...; |
| 74 | + border-radius: inherit; |
| 75 | + } |
| 76 | +} |
| 77 | +``` |
| 78 | + |
| 79 | +A control that declares its radius on an inner element instead is invisible to |
| 80 | +`Group`: it keeps round corners in the middle of the row, and no rule written |
| 81 | +in `group.jsx` can reach it without naming that private class — which is how a |
| 82 | +layout component ends up knowing the internals of every control. When you meet |
| 83 | +that symptom, fix the control, not the group. |
| 84 | + |
| 85 | +The controls shipped by navi all follow this: `Button`, `Input`, `Select`, |
| 86 | +`Picker`, `Picker.Spin`, checkbox, range. |
| 87 | + |
| 88 | +## When it is not a Group |
| 89 | + |
| 90 | +- Controls separated by space, each with its own frame — that is a `Box` with |
| 91 | + `spacing`, they were never one object. |
| 92 | +- A label and its control — that is `Field`. |
| 93 | +- Radio buttons or checkboxes sharing a name and a validation — that is |
| 94 | + `RadioGroup` / `CheckboxGroup`, which is about the value, not the frame. They |
| 95 | + can be put inside a `Group` if you also want them to share a frame. |
0 commit comments