@botiverse/raft-ui 0.67.0 → 0.68.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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [0.68.0](https://github.com/botiverse/raft-design/compare/v0.67.0...v0.68.0) (2026-11-06)
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- **MobileNavServerPopoverDestinations** (`@botiverse/raft-ui/wip`): more than 4 destinations take 3 columns, rows of 3 with the last row from the left (`data-layout="grid"`); each button keeps its height, icon, label size and hit area. 4 or fewer stay in one row (`data-layout="row"`). The count is of the element children, looking inside fragments. ([#1037](https://github.com/botiverse/raft-design/pull/1037))
|
|
6
|
+
- Why: a server tab that holds Artifacts, Members, Computers, Apps and Settings put five in a row, which truncated labels such as "Computadoras" at 360px; a third of the popover keeps them whole in every language.
|
|
7
|
+
- Affects: a server tab popover with more than 4 destinations; up to 4 are unchanged.
|
|
8
|
+
- Docs: https://raft-design.botiverse.build/components/mobile-nav/#the-server-tab
|
|
9
|
+
|
|
1
10
|
## [0.67.0](https://github.com/botiverse/raft-design/compare/v0.66.0...v0.67.0) (2026-11-06)
|
|
2
11
|
|
|
3
12
|
### Added
|
|
@@ -47,8 +47,17 @@ export type MobileNavServerPopoverRowLabelProps = ComponentPropsWithRef<"span">;
|
|
|
47
47
|
/** A row's label: takes the room and truncates. */
|
|
48
48
|
export declare function MobileNavServerPopoverRowLabel({ className, ...props }: MobileNavServerPopoverRowLabelProps): import("react").JSX.Element;
|
|
49
49
|
export type MobileNavServerPopoverDestinationsProps = ComponentPropsWithRef<"div">;
|
|
50
|
-
/**
|
|
51
|
-
|
|
50
|
+
/**
|
|
51
|
+
* The server's own pages in equal cells; a `group`, named with `aria-label`.
|
|
52
|
+
* Up to 4 sit side by side in one row (`data-layout="row"`). More than 4
|
|
53
|
+
* take 3 columns, rows of 3 with the last row starting at the left
|
|
54
|
+
* (`data-layout="grid"`), so each label keeps a third of the popover and
|
|
55
|
+
* stays whole in every language. The count is of the element children, taken
|
|
56
|
+
* in React, looking inside fragments (a product puts the pages a guest lacks
|
|
57
|
+
* in one): counting them in CSS would take a structural pseudo-class on the
|
|
58
|
+
* children, which the WebKit selector rules in CLAUDE.md forbid.
|
|
59
|
+
*/
|
|
60
|
+
export declare function MobileNavServerPopoverDestinations({ className, children, ...props }: MobileNavServerPopoverDestinationsProps): import("react").JSX.Element;
|
|
52
61
|
export type MobileNavServerPopoverDestinationProps = ComponentPropsWithRef<"button">;
|
|
53
62
|
/**
|
|
54
63
|
* One of the server's pages: a 20px icon over a short label in a `span`, which
|
|
@@ -2,7 +2,7 @@ import { tv } from "../../lib/tv.mjs";
|
|
|
2
2
|
import { MobileNavItem } from "./mobile-nav.mjs";
|
|
3
3
|
import "../../lib/touch-drag.mjs";
|
|
4
4
|
import { PopoverContent } from "../popover/popover.mjs";
|
|
5
|
-
import { createContext, use, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { Children, Fragment, createContext, isValidElement, use, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
import { Popover } from "@base-ui/react/popover";
|
|
8
8
|
//#region src/components/mobile-nav/mobile-nav-server.tsx
|
|
@@ -33,7 +33,7 @@ const mobileNavServer = tv({ slots: {
|
|
|
33
33
|
rowLabel: ["min-w-0 flex-1 truncate"],
|
|
34
34
|
rowBody: ["flex min-w-0 flex-1 flex-col"],
|
|
35
35
|
rowDescription: ["min-w-0 truncate", "font-mono text-mono-meta font-normal text-foreground-hint"],
|
|
36
|
-
destinations: ["grid shrink-0 grid-flow-col auto-cols-fr gap-1 p-1"],
|
|
36
|
+
destinations: ["grid shrink-0 grid-flow-col auto-cols-fr gap-1 p-1", "data-[layout=grid]:grid-flow-row data-[layout=grid]:grid-cols-3"],
|
|
37
37
|
destination: [
|
|
38
38
|
"relative flex min-w-0 cursor-default select-none flex-col items-center justify-center",
|
|
39
39
|
"outline-none transition-colors",
|
|
@@ -231,14 +231,28 @@ function MobileNavServerPopoverRowLabel({ className, ...props }) {
|
|
|
231
231
|
...props
|
|
232
232
|
});
|
|
233
233
|
}
|
|
234
|
-
/**
|
|
235
|
-
|
|
234
|
+
/** Past this many pages, the row becomes a grid of 3 columns. */
|
|
235
|
+
const DESTINATIONS_IN_ONE_ROW = 4;
|
|
236
|
+
/**
|
|
237
|
+
* The server's own pages in equal cells; a `group`, named with `aria-label`.
|
|
238
|
+
* Up to 4 sit side by side in one row (`data-layout="row"`). More than 4
|
|
239
|
+
* take 3 columns, rows of 3 with the last row starting at the left
|
|
240
|
+
* (`data-layout="grid"`), so each label keeps a third of the popover and
|
|
241
|
+
* stays whole in every language. The count is of the element children, taken
|
|
242
|
+
* in React, looking inside fragments (a product puts the pages a guest lacks
|
|
243
|
+
* in one): counting them in CSS would take a structural pseudo-class on the
|
|
244
|
+
* children, which the WebKit selector rules in CLAUDE.md forbid.
|
|
245
|
+
*/
|
|
246
|
+
function MobileNavServerPopoverDestinations({ className, children, ...props }) {
|
|
236
247
|
const { destinations } = mobileNavServer();
|
|
248
|
+
const count = countElements(children);
|
|
237
249
|
return /* @__PURE__ */ jsx("div", {
|
|
238
250
|
role: "group",
|
|
239
251
|
"data-slot": "mobile-nav-server-popover-destinations",
|
|
252
|
+
"data-layout": count > DESTINATIONS_IN_ONE_ROW ? "grid" : "row",
|
|
240
253
|
className: destinations({ className }),
|
|
241
|
-
...props
|
|
254
|
+
...props,
|
|
255
|
+
children
|
|
242
256
|
});
|
|
243
257
|
}
|
|
244
258
|
/**
|
|
@@ -279,6 +293,15 @@ function MobileNavServerPopoverRowDescription({ className, ...props }) {
|
|
|
279
293
|
...props
|
|
280
294
|
});
|
|
281
295
|
}
|
|
296
|
+
/** The element children, counting a fragment's elements rather than the fragment. */
|
|
297
|
+
function countElements(children) {
|
|
298
|
+
let count = 0;
|
|
299
|
+
for (const child of Children.toArray(children)) {
|
|
300
|
+
if (!isValidElement(child)) continue;
|
|
301
|
+
count += child.type === Fragment ? countElements(child.props.children) : 1;
|
|
302
|
+
}
|
|
303
|
+
return count;
|
|
304
|
+
}
|
|
282
305
|
function assignRef(ref, value) {
|
|
283
306
|
if (typeof ref === "function") ref(value);
|
|
284
307
|
else if (ref) ref.current = value;
|