@dreamlake/dockit 0.2.9 → 0.2.10

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.
@@ -35,7 +35,6 @@ 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;
39
38
  function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
40
39
  const { urlPathname } = usePageContext();
41
40
  const current = findPage(urlPathname);
@@ -153,32 +152,45 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
153
152
  transition: "opacity 0.25s ease"
154
153
  });
155
154
  const iconButtonMode = isBelowLg && !searchOpen;
156
- const clusterRef = useRef(null);
155
+ const closedWidth = isXlUp ? CLOSED_WIDTH_XL : isLgUp ? CLOSED_WIDTH_LG : CLOSED_WIDTH_MD;
156
+ const brandRef = useRef(null);
157
+ const brandMaxRight = useRef(0);
157
158
  const tabsRef = useRef(null);
158
159
  const actionsRef = useRef(null);
159
160
  const [tabsFit, setTabsFit] = useState(false);
161
+ const [tabsW, setTabsW] = useState(0);
162
+ const [pinLeft, setPinLeft] = useState(0);
160
163
  useLayoutEffect(() => {
161
164
  const tabsEl = tabsRef.current;
162
165
  if (!tabsEl) return;
166
+ const content = document.querySelector("main.doc-content");
163
167
  const measure = () => {
164
- const tabsW = tabsEl.getBoundingClientRect().width;
165
- const inset = Math.max(SEARCH_GUTTER, (window.innerWidth - SEARCH_MAX_W) / 2);
168
+ const w = tabsEl.getBoundingClientRect().width;
169
+ const rect = content?.getBoundingClientRect();
170
+ const textLeft = rect ? rect.left + parseFloat(getComputedStyle(content).paddingLeft) : Math.max(SEARCH_GUTTER, (window.innerWidth - SEARCH_MAX_W) / 2);
171
+ const pin = textLeft - 10;
172
+ const brand = brandRef.current?.getBoundingClientRect();
173
+ if (brand) brandMaxRight.current = Math.max(brandMaxRight.current, brand.right);
174
+ const brandClear = brandMaxRight.current + 16 <= pin;
166
175
  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);
176
+ const searchLeft = (actions ? actions.left : window.innerWidth) - 8 - closedWidth;
177
+ setTabsW(w);
178
+ setPinLeft(pin);
179
+ setTabsFit(w > 0 && brandClear && pin + w + 16 <= searchLeft);
169
180
  };
170
181
  measure();
171
182
  const ro = new ResizeObserver(measure);
172
183
  ro.observe(tabsEl);
184
+ if (content) ro.observe(content);
185
+ if (brandRef.current) ro.observe(brandRef.current);
173
186
  if (actionsRef.current) ro.observe(actionsRef.current);
174
187
  window.addEventListener("resize", measure);
175
188
  return () => {
176
189
  ro.disconnect();
177
190
  window.removeEventListener("resize", measure);
178
191
  };
179
- }, []);
180
- const edgeAligned = isLgUp && !iconButtonMode && !searchOpen && tabsFit;
181
- const closedWidth = isXlUp ? CLOSED_WIDTH_XL : isLgUp ? CLOSED_WIDTH_LG : CLOSED_WIDTH_MD;
192
+ }, [closedWidth]);
193
+ const edgePinned = isLgUp && !iconButtonMode && tabsFit;
182
194
  const searchStyle = iconButtonMode ? {
183
195
  // In-flow grid item — sits at the end of column 2 next to TabStrip.
184
196
  // Square footprint, icon-only, click opens the palette directly.
@@ -256,6 +268,7 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
256
268
  /* @__PURE__ */ jsxs(
257
269
  "a",
258
270
  {
271
+ ref: brandRef,
259
272
  href: "/",
260
273
  className: "flex items-center gap-2.5 pl-[14px] md:pl-[18px] no-underline text-doc-template-ink hover:opacity-80 min-w-[72px] shrink-0",
261
274
  style: {
@@ -354,10 +367,19 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
354
367
  "aria-label": "Breadcrumb",
355
368
  className: "hidden md:flex items-center gap-1.5 text-doc-template-muted uppercase pointer-events-none overflow-hidden",
356
369
  style: {
357
- gridColumn: 2,
358
- gridRow: 1,
359
- justifySelf: "start",
360
- marginLeft: 8,
370
+ // With the tab strip pinned to the content column's left
371
+ // edge, the breadcrumb moves out of the grid too and starts
372
+ // just right of the strip — otherwise it would fade in
373
+ // underneath the tabs. (gridArea/placeSelf: see the note on
374
+ // `searchStyle` about fixed-positioned grid items.)
375
+ ...edgePinned ? {
376
+ position: "fixed",
377
+ top: 0,
378
+ height: 40,
379
+ left: pinLeft + tabsW + 24,
380
+ gridArea: "auto",
381
+ placeSelf: "stretch"
382
+ } : { gridColumn: 2, gridRow: 1, justifySelf: "start", marginLeft: 8 },
361
383
  fontFamily: "var(--font-doc-template-mono)",
362
384
  fontSize: 10,
363
385
  fontWeight: 500,
@@ -383,16 +405,9 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
383
405
  /* @__PURE__ */ jsxs(
384
406
  "div",
385
407
  {
386
- ref: clusterRef,
387
408
  style: {
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 },
409
+ gridColumn: 2,
410
+ gridRow: 1,
396
411
  display: "flex",
397
412
  alignItems: "center",
398
413
  justifyContent: "flex-end",
@@ -405,7 +420,10 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
405
420
  {
406
421
  ref: tabsRef,
407
422
  className: "flex items-stretch",
408
- style: { ...palFade(searchOpen), order: edgeAligned ? 1 : 0 },
423
+ style: {
424
+ ...palFade(searchOpen),
425
+ ...edgePinned ? { position: "fixed", top: 0, left: pinLeft, height: 40 } : {}
426
+ },
409
427
  children: /* @__PURE__ */ jsx(TabStrip, {})
410
428
  }
411
429
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamlake/dockit",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
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",