@dreamlake/dockit 0.2.7 → 0.2.9

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.
@@ -51,7 +51,22 @@ function TabStrip() {
51
51
  ref: containerRef,
52
52
  "aria-label": "Documentation sections",
53
53
  className: "flex items-stretch relative m-0 p-0 list-none",
54
- style: { height: "100%" },
54
+ style: {
55
+ height: "100%",
56
+ // Labels never wrap. A second line would grow this row and push
57
+ // the topbar past its fixed 40px, so each tab keeps its
58
+ // intrinsic width (`shrink-0` on the <li>) and the strip clips
59
+ // whatever no longer fits rather than reflowing.
60
+ //
61
+ // `clip`, not `hidden`: the underbar below sits at bottom:-13px,
62
+ // deliberately outside this box. Setting `overflow-x: hidden`
63
+ // makes CSS compute the still-`visible` overflow-y to `auto`,
64
+ // which would clip that bar away (and risk a stray scrollbar).
65
+ // `clip` is the one overflow value that leaves the other axis
66
+ // genuinely visible.
67
+ overflowX: "clip",
68
+ overflowY: "visible"
69
+ },
55
70
  onMouseLeave: () => setHovered(null),
56
71
  children: [
57
72
  visibleTabs.map((tab) => {
@@ -59,7 +74,7 @@ function TabStrip() {
59
74
  return /* @__PURE__ */ jsx(
60
75
  "li",
61
76
  {
62
- className: "flex items-stretch",
77
+ className: "flex items-stretch shrink-0",
63
78
  onMouseEnter: () => setHovered(tab.id),
64
79
  children: /* @__PURE__ */ jsx(
65
80
  "a",
@@ -76,6 +91,7 @@ function TabStrip() {
76
91
  fontWeight: 600,
77
92
  letterSpacing: "0.12em",
78
93
  textTransform: "uppercase",
94
+ whiteSpace: "nowrap",
79
95
  color: isActive || hovered === tab.id ? "var(--color-doc-template-ink)" : "var(--color-doc-template-muted)",
80
96
  cursor: "pointer",
81
97
  transition: "color 0.18s ease"
@@ -35,6 +35,7 @@ const ICON_BUTTON_SIZE = 28;
35
35
  const SEARCH_MAX_W = 860;
36
36
  const SEARCH_GUTTER = 24;
37
37
  const SEARCH_INSET = `max(${SEARCH_GUTTER}px, calc((100vw - ${SEARCH_MAX_W}px) / 2))`;
38
+ const TABS_EDGE_MAX_W = 420;
38
39
  function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
39
40
  const { urlPathname } = usePageContext();
40
41
  const current = findPage(urlPathname);
@@ -152,6 +153,31 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
152
153
  transition: "opacity 0.25s ease"
153
154
  });
154
155
  const iconButtonMode = isBelowLg && !searchOpen;
156
+ const clusterRef = useRef(null);
157
+ const tabsRef = useRef(null);
158
+ const actionsRef = useRef(null);
159
+ const [tabsFit, setTabsFit] = useState(false);
160
+ useLayoutEffect(() => {
161
+ const tabsEl = tabsRef.current;
162
+ if (!tabsEl) return;
163
+ const measure = () => {
164
+ const tabsW = tabsEl.getBoundingClientRect().width;
165
+ const inset = Math.max(SEARCH_GUTTER, (window.innerWidth - SEARCH_MAX_W) / 2);
166
+ const actions = actionsRef.current?.getBoundingClientRect();
167
+ const actionsW = actions ? window.innerWidth - actions.left : 0;
168
+ setTabsFit(tabsW > 0 && tabsW <= TABS_EDGE_MAX_W && inset >= actionsW + 16);
169
+ };
170
+ measure();
171
+ const ro = new ResizeObserver(measure);
172
+ ro.observe(tabsEl);
173
+ if (actionsRef.current) ro.observe(actionsRef.current);
174
+ window.addEventListener("resize", measure);
175
+ return () => {
176
+ ro.disconnect();
177
+ window.removeEventListener("resize", measure);
178
+ };
179
+ }, []);
180
+ const edgeAligned = isLgUp && !iconButtonMode && !searchOpen && tabsFit;
155
181
  const closedWidth = isXlUp ? CLOSED_WIDTH_XL : isLgUp ? CLOSED_WIDTH_LG : CLOSED_WIDTH_MD;
156
182
  const searchStyle = iconButtonMode ? {
157
183
  // In-flow grid item — sits at the end of column 2 next to TabStrip.
@@ -357,9 +383,16 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
357
383
  /* @__PURE__ */ jsxs(
358
384
  "div",
359
385
  {
386
+ ref: clusterRef,
360
387
  style: {
361
- gridColumn: 2,
362
- gridRow: 1,
388
+ ...edgeAligned ? {
389
+ position: "fixed",
390
+ top: 0,
391
+ right: SEARCH_INSET,
392
+ height: 40,
393
+ gridArea: "auto",
394
+ placeSelf: "stretch"
395
+ } : { gridColumn: 2, gridRow: 1 },
363
396
  display: "flex",
364
397
  alignItems: "center",
365
398
  justifyContent: "flex-end",
@@ -367,7 +400,15 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
367
400
  minWidth: 0
368
401
  },
369
402
  children: [
370
- /* @__PURE__ */ jsx("div", { className: "flex items-stretch", style: palFade(searchOpen), children: /* @__PURE__ */ jsx(TabStrip, {}) }),
403
+ /* @__PURE__ */ jsx(
404
+ "div",
405
+ {
406
+ ref: tabsRef,
407
+ className: "flex items-stretch",
408
+ style: { ...palFade(searchOpen), order: edgeAligned ? 1 : 0 },
409
+ children: /* @__PURE__ */ jsx(TabStrip, {})
410
+ }
411
+ ),
371
412
  /* @__PURE__ */ jsxs(
372
413
  "label",
373
414
  {
@@ -456,6 +497,7 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
456
497
  /* @__PURE__ */ jsx(
457
498
  "div",
458
499
  {
500
+ ref: actionsRef,
459
501
  className: "flex items-stretch gap-2.5 md:gap-3.5 pr-[14px] md:pr-[22px]",
460
502
  style: { gridColumn: 3, gridRow: 1, justifySelf: "end", ...palFade(searchOpen) },
461
503
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3.5", children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamlake/dockit",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "The DreamLake documentation shell — Vike + React + Tailwind v4 + MDX docs framework with sidebar, TOC, search, and theming",
5
5
  "license": "MIT",
6
6
  "type": "module",