@dreamlake/dockit 0.2.9 → 0.2.11
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/dist/components/Topbar.js +66 -23
- package/package.json +1 -1
|
@@ -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,54 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
153
152
|
transition: "opacity 0.25s ease"
|
|
154
153
|
});
|
|
155
154
|
const iconButtonMode = isBelowLg && !searchOpen;
|
|
156
|
-
const
|
|
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);
|
|
163
|
+
const [searchPinRight, setSearchPinRight] = useState(null);
|
|
160
164
|
useLayoutEffect(() => {
|
|
161
165
|
const tabsEl = tabsRef.current;
|
|
162
166
|
if (!tabsEl) return;
|
|
167
|
+
const content = document.querySelector("main.doc-content");
|
|
163
168
|
const measure = () => {
|
|
164
|
-
const
|
|
165
|
-
const
|
|
169
|
+
const w = tabsEl.getBoundingClientRect().width;
|
|
170
|
+
const rect = content?.getBoundingClientRect();
|
|
171
|
+
const cs = content ? getComputedStyle(content) : null;
|
|
172
|
+
const fallbackInset = Math.max(SEARCH_GUTTER, (window.innerWidth - SEARCH_MAX_W) / 2);
|
|
173
|
+
const textLeft = rect ? rect.left + parseFloat(cs.paddingLeft) : fallbackInset;
|
|
174
|
+
const textRight = rect ? rect.right - parseFloat(cs.paddingRight) : window.innerWidth - fallbackInset;
|
|
175
|
+
const pin = textLeft - 10;
|
|
176
|
+
const brand = brandRef.current?.getBoundingClientRect();
|
|
177
|
+
if (brand) brandMaxRight.current = Math.max(brandMaxRight.current, brand.right);
|
|
178
|
+
const brandClear = brandMaxRight.current + 16 <= pin;
|
|
166
179
|
const actions = actionsRef.current?.getBoundingClientRect();
|
|
167
|
-
const
|
|
168
|
-
|
|
180
|
+
const actionsLeft = actions ? actions.left : window.innerWidth;
|
|
181
|
+
const pinnedSearchLeft = textRight - closedWidth;
|
|
182
|
+
const packedSearchLeft = actionsLeft - 8 - closedWidth;
|
|
183
|
+
const fits = (searchLeft) => w > 0 && brandClear && pin + w + 16 <= searchLeft;
|
|
184
|
+
const fullPin = textRight + 16 <= actionsLeft && fits(pinnedSearchLeft);
|
|
185
|
+
setTabsW(w);
|
|
186
|
+
setPinLeft(pin);
|
|
187
|
+
setSearchPinRight(fullPin ? window.innerWidth - textRight : null);
|
|
188
|
+
setTabsFit(fullPin || fits(packedSearchLeft));
|
|
169
189
|
};
|
|
170
190
|
measure();
|
|
171
191
|
const ro = new ResizeObserver(measure);
|
|
172
192
|
ro.observe(tabsEl);
|
|
193
|
+
if (content) ro.observe(content);
|
|
194
|
+
if (brandRef.current) ro.observe(brandRef.current);
|
|
173
195
|
if (actionsRef.current) ro.observe(actionsRef.current);
|
|
174
196
|
window.addEventListener("resize", measure);
|
|
175
197
|
return () => {
|
|
176
198
|
ro.disconnect();
|
|
177
199
|
window.removeEventListener("resize", measure);
|
|
178
200
|
};
|
|
179
|
-
}, []);
|
|
180
|
-
const
|
|
181
|
-
const closedWidth = isXlUp ? CLOSED_WIDTH_XL : isLgUp ? CLOSED_WIDTH_LG : CLOSED_WIDTH_MD;
|
|
201
|
+
}, [closedWidth]);
|
|
202
|
+
const edgePinned = isLgUp && !iconButtonMode && tabsFit;
|
|
182
203
|
const searchStyle = iconButtonMode ? {
|
|
183
204
|
// In-flow grid item — sits at the end of column 2 next to TabStrip.
|
|
184
205
|
// Square footprint, icon-only, click opens the palette directly.
|
|
@@ -223,6 +244,22 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
223
244
|
placeSelf: "stretch",
|
|
224
245
|
boxShadow: "0 4px 14px rgb(0 0 0 / 0.06)",
|
|
225
246
|
transition: `left ${EXPAND_MS}ms ${EXPAND_EASE}, right ${EXPAND_MS}ms ${EXPAND_EASE}, background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease`
|
|
247
|
+
} : searchPinRight != null ? {
|
|
248
|
+
// Closed, right-aligned to the content column's right
|
|
249
|
+
// edge (mirrors the pinned tab strip on the left edge).
|
|
250
|
+
// (gridArea/placeSelf: see the note above about
|
|
251
|
+
// fixed-positioned grid items.)
|
|
252
|
+
position: "fixed",
|
|
253
|
+
top: 6,
|
|
254
|
+
right: searchPinRight,
|
|
255
|
+
width: closedWidth,
|
|
256
|
+
maxWidth: closedWidth,
|
|
257
|
+
height: 28,
|
|
258
|
+
padding: "6px 10px",
|
|
259
|
+
borderRadius: 12,
|
|
260
|
+
gridArea: "auto",
|
|
261
|
+
placeSelf: "stretch",
|
|
262
|
+
transition: `width ${EXPAND_MS}ms ${EXPAND_EASE}, background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease`
|
|
226
263
|
} : {
|
|
227
264
|
gridColumn: 2,
|
|
228
265
|
gridRow: 1,
|
|
@@ -256,6 +293,7 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
256
293
|
/* @__PURE__ */ jsxs(
|
|
257
294
|
"a",
|
|
258
295
|
{
|
|
296
|
+
ref: brandRef,
|
|
259
297
|
href: "/",
|
|
260
298
|
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
299
|
style: {
|
|
@@ -354,10 +392,19 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
354
392
|
"aria-label": "Breadcrumb",
|
|
355
393
|
className: "hidden md:flex items-center gap-1.5 text-doc-template-muted uppercase pointer-events-none overflow-hidden",
|
|
356
394
|
style: {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
395
|
+
// With the tab strip pinned to the content column's left
|
|
396
|
+
// edge, the breadcrumb moves out of the grid too and starts
|
|
397
|
+
// just right of the strip — otherwise it would fade in
|
|
398
|
+
// underneath the tabs. (gridArea/placeSelf: see the note on
|
|
399
|
+
// `searchStyle` about fixed-positioned grid items.)
|
|
400
|
+
...edgePinned ? {
|
|
401
|
+
position: "fixed",
|
|
402
|
+
top: 0,
|
|
403
|
+
height: 40,
|
|
404
|
+
left: pinLeft + tabsW + 24,
|
|
405
|
+
gridArea: "auto",
|
|
406
|
+
placeSelf: "stretch"
|
|
407
|
+
} : { gridColumn: 2, gridRow: 1, justifySelf: "start", marginLeft: 8 },
|
|
361
408
|
fontFamily: "var(--font-doc-template-mono)",
|
|
362
409
|
fontSize: 10,
|
|
363
410
|
fontWeight: 500,
|
|
@@ -383,16 +430,9 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
383
430
|
/* @__PURE__ */ jsxs(
|
|
384
431
|
"div",
|
|
385
432
|
{
|
|
386
|
-
ref: clusterRef,
|
|
387
433
|
style: {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
top: 0,
|
|
391
|
-
right: SEARCH_INSET,
|
|
392
|
-
height: 40,
|
|
393
|
-
gridArea: "auto",
|
|
394
|
-
placeSelf: "stretch"
|
|
395
|
-
} : { gridColumn: 2, gridRow: 1 },
|
|
434
|
+
gridColumn: 2,
|
|
435
|
+
gridRow: 1,
|
|
396
436
|
display: "flex",
|
|
397
437
|
alignItems: "center",
|
|
398
438
|
justifyContent: "flex-end",
|
|
@@ -405,7 +445,10 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
405
445
|
{
|
|
406
446
|
ref: tabsRef,
|
|
407
447
|
className: "flex items-stretch",
|
|
408
|
-
style: {
|
|
448
|
+
style: {
|
|
449
|
+
...palFade(searchOpen),
|
|
450
|
+
...edgePinned ? { position: "fixed", top: 0, left: pinLeft, height: 40 } : {}
|
|
451
|
+
},
|
|
409
452
|
children: /* @__PURE__ */ jsx(TabStrip, {})
|
|
410
453
|
}
|
|
411
454
|
),
|
package/package.json
CHANGED