@dreamlake/dockit 0.2.10 → 0.2.12
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/TabStrip.js +21 -16
- package/dist/components/Topbar.js +28 -3
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { tabForUrl, TABS, urlInTab } from "../lib/tabs.js";
|
|
|
5
5
|
import { pages } from "../lib/navigation.js";
|
|
6
6
|
import { useHiddenToggle } from "../lib/use-hidden-toggle.js";
|
|
7
7
|
const SPRING = "cubic-bezier(0.4, 0.7, 0.3, 1)";
|
|
8
|
+
const SSR_BAR_BOTTOM = -13;
|
|
8
9
|
function TabStrip() {
|
|
9
10
|
const { urlPathname } = usePageContext();
|
|
10
11
|
const activeTab = tabForUrl(urlPathname);
|
|
@@ -21,28 +22,31 @@ function TabStrip() {
|
|
|
21
22
|
const [bar, setBar] = useState({
|
|
22
23
|
left: 0,
|
|
23
24
|
width: 0,
|
|
25
|
+
bottom: SSR_BAR_BOTTOM,
|
|
24
26
|
ready: false
|
|
25
27
|
});
|
|
26
28
|
const targetTab = hovered ?? activeTab;
|
|
27
|
-
|
|
29
|
+
function measure() {
|
|
28
30
|
const container = containerRef.current;
|
|
29
31
|
const target = linkRefs.current[targetTab];
|
|
30
32
|
if (!container || !target) return;
|
|
31
33
|
const cR = container.getBoundingClientRect();
|
|
32
34
|
const tR = target.getBoundingClientRect();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const hR = container.closest("header")?.getBoundingClientRect();
|
|
36
|
+
const next = {
|
|
37
|
+
left: tR.left - cR.left,
|
|
38
|
+
width: tR.width,
|
|
39
|
+
bottom: hR ? cR.bottom - hR.bottom : SSR_BAR_BOTTOM,
|
|
40
|
+
ready: true
|
|
41
|
+
};
|
|
42
|
+
setBar(
|
|
43
|
+
(prev) => prev.ready && prev.left === next.left && prev.width === next.width && prev.bottom === next.bottom ? prev : next
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
useLayoutEffect(measure);
|
|
35
47
|
useLayoutEffect(() => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const target = linkRefs.current[targetTab];
|
|
39
|
-
if (!container || !target) return;
|
|
40
|
-
const cR = container.getBoundingClientRect();
|
|
41
|
-
const tR = target.getBoundingClientRect();
|
|
42
|
-
setBar((b) => ({ ...b, left: tR.left - cR.left, width: tR.width }));
|
|
43
|
-
}
|
|
44
|
-
window.addEventListener("resize", onResize);
|
|
45
|
-
return () => window.removeEventListener("resize", onResize);
|
|
48
|
+
window.addEventListener("resize", measure);
|
|
49
|
+
return () => window.removeEventListener("resize", measure);
|
|
46
50
|
}, [targetTab]);
|
|
47
51
|
if (visibleTabs.length === 0) return null;
|
|
48
52
|
return /* @__PURE__ */ jsxs(
|
|
@@ -58,8 +62,9 @@ function TabStrip() {
|
|
|
58
62
|
// intrinsic width (`shrink-0` on the <li>) and the strip clips
|
|
59
63
|
// whatever no longer fits rather than reflowing.
|
|
60
64
|
//
|
|
61
|
-
// `clip`, not `hidden`: the underbar below sits at
|
|
62
|
-
// deliberately outside this box
|
|
65
|
+
// `clip`, not `hidden`: the underbar below sits at a negative
|
|
66
|
+
// bottom offset, deliberately outside this box (whenever the strip
|
|
67
|
+
// is shorter than the topbar). Setting `overflow-x: hidden`
|
|
63
68
|
// makes CSS compute the still-`visible` overflow-y to `auto`,
|
|
64
69
|
// which would clip that bar away (and risk a stray scrollbar).
|
|
65
70
|
// `clip` is the one overflow value that leaves the other axis
|
|
@@ -109,7 +114,7 @@ function TabStrip() {
|
|
|
109
114
|
"aria-hidden": true,
|
|
110
115
|
style: {
|
|
111
116
|
position: "absolute",
|
|
112
|
-
bottom:
|
|
117
|
+
bottom: bar.bottom,
|
|
113
118
|
left: 0,
|
|
114
119
|
height: 3,
|
|
115
120
|
background: "var(--color-doc-template-ink)",
|
|
@@ -160,6 +160,7 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
160
160
|
const [tabsFit, setTabsFit] = useState(false);
|
|
161
161
|
const [tabsW, setTabsW] = useState(0);
|
|
162
162
|
const [pinLeft, setPinLeft] = useState(0);
|
|
163
|
+
const [searchPinRight, setSearchPinRight] = useState(null);
|
|
163
164
|
useLayoutEffect(() => {
|
|
164
165
|
const tabsEl = tabsRef.current;
|
|
165
166
|
if (!tabsEl) return;
|
|
@@ -167,16 +168,24 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
167
168
|
const measure = () => {
|
|
168
169
|
const w = tabsEl.getBoundingClientRect().width;
|
|
169
170
|
const rect = content?.getBoundingClientRect();
|
|
170
|
-
const
|
|
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;
|
|
171
175
|
const pin = textLeft - 10;
|
|
172
176
|
const brand = brandRef.current?.getBoundingClientRect();
|
|
173
177
|
if (brand) brandMaxRight.current = Math.max(brandMaxRight.current, brand.right);
|
|
174
178
|
const brandClear = brandMaxRight.current + 16 <= pin;
|
|
175
179
|
const actions = actionsRef.current?.getBoundingClientRect();
|
|
176
|
-
const
|
|
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);
|
|
177
185
|
setTabsW(w);
|
|
178
186
|
setPinLeft(pin);
|
|
179
|
-
|
|
187
|
+
setSearchPinRight(fullPin ? window.innerWidth - textRight : null);
|
|
188
|
+
setTabsFit(fullPin || fits(packedSearchLeft));
|
|
180
189
|
};
|
|
181
190
|
measure();
|
|
182
191
|
const ro = new ResizeObserver(measure);
|
|
@@ -235,6 +244,22 @@ function Topbar({ searchOpen, onOpenSearch, onCloseSearch, query, setQuery }) {
|
|
|
235
244
|
placeSelf: "stretch",
|
|
236
245
|
boxShadow: "0 4px 14px rgb(0 0 0 / 0.06)",
|
|
237
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`
|
|
238
263
|
} : {
|
|
239
264
|
gridColumn: 2,
|
|
240
265
|
gridRow: 1,
|
package/package.json
CHANGED