@deepseek-ai/dsh-client-ui-dockkit 0.1.5-alpha.1 → 0.1.5-alpha.2
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/README.i18n.yaml +2 -2
- package/README.md +9 -9
- package/README.zh.md +9 -9
- package/lib/components/dockkit.module.css +405 -99
- package/lib/index.js +370 -161
- package/lib/types/components/DockSurface.d.ts +12 -4
- package/lib/types/components/FloatLayer.d.ts +4 -2
- package/lib/types/components/TabMenu.d.ts +2 -1
- package/lib/types/components/TabTitle.d.ts +6 -0
- package/lib/types/components/measure.d.ts +5 -1
- package/lib/types/components/render.d.ts +4 -2
- package/lib/types/contract/adapter.d.ts +6 -2
- package/lib/types/engine/geometry.d.ts +21 -8
- package/lib/types/engine/planner.d.ts +6 -3
- package/package.json +5 -7
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment as Fragment$1, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { Children, Fragment as Fragment$1, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
3
|
import clsx from "clsx";
|
|
4
|
+
import { IconCloseFill14, IconCloseOutline16, IconPanelLeftOutline16, IconPlusOutline16, Tooltip } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
4
5
|
import { createPortal } from "react-dom";
|
|
5
6
|
import css from "./components/dockkit.module.css";
|
|
6
7
|
//#region lib/types/engine/tree.js
|
|
@@ -1231,21 +1232,23 @@ function insertionIndex(tabRects, x) {
|
|
|
1231
1232
|
}
|
|
1232
1233
|
/**
|
|
1233
1234
|
* The minimums where no computed style can be read, mirroring
|
|
1234
|
-
* `dockkit.module.css`: `.splitRow > .divider`
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1237
|
-
*
|
|
1235
|
+
* `dockkit.module.css`: `.splitRow > .divider` takes no layout width (its
|
|
1236
|
+
* hairline is painted over the seam); `.tab` is 80px of content plus
|
|
1237
|
+
* 10px + 10px of padding (content-box), 100px; 12px above and below one 13px
|
|
1238
|
+
* secondary line at 1.6 line-height — the inset a body draws for itself, as
|
|
1239
|
+
* `.empty` does — is 45px, held to 48px.
|
|
1238
1240
|
*/
|
|
1239
1241
|
const SPLIT_MINIMUMS = {
|
|
1240
|
-
divider:
|
|
1241
|
-
chip:
|
|
1242
|
+
divider: 0,
|
|
1243
|
+
chip: 100,
|
|
1242
1244
|
body: 48
|
|
1243
1245
|
};
|
|
1244
1246
|
/**
|
|
1245
1247
|
* The room rule. After an equal split each half must hold what cannot shrink:
|
|
1246
|
-
* horizontally the strip's fixed part — its width minus the chip box
|
|
1247
|
-
* fill, which is the padding, the gaps, and every
|
|
1248
|
-
* plus one chip at its minimum; vertically
|
|
1248
|
+
* horizontally the strip's fixed part — its width minus the chip box, the
|
|
1249
|
+
* fill, and `splitControlWidth`, which is the padding, the gaps, and every
|
|
1250
|
+
* control a half would still draw — plus one chip at its minimum; vertically
|
|
1251
|
+
* the strip plus a minimum body. The
|
|
1249
1252
|
* borders are what the pane's box exceeds the strip's by. An unmeasured pane
|
|
1250
1253
|
* (no layout, as under jsdom) fits: the rule only blocks on a positive reading.
|
|
1251
1254
|
* @param measure - the pane's rectangles.
|
|
@@ -1259,7 +1262,7 @@ function halvesFit(measure, minimums = SPLIT_MINIMUMS) {
|
|
|
1259
1262
|
column: true
|
|
1260
1263
|
};
|
|
1261
1264
|
const borders = Math.max(0, pane.width - strip.width);
|
|
1262
|
-
const fixed = Math.max(0, strip.width - measure.chipsWidth - measure.fillWidth);
|
|
1265
|
+
const fixed = Math.max(0, strip.width - measure.chipsWidth - measure.fillWidth - (measure.splitControlWidth ?? 0));
|
|
1263
1266
|
const halfWidth = (pane.width - minimums.divider) / 2 - borders;
|
|
1264
1267
|
const halfHeight = (pane.height - minimums.divider) / 2 - borders;
|
|
1265
1268
|
return {
|
|
@@ -1565,16 +1568,19 @@ function planPlaceTab(state, tabId, toPaneId, index) {
|
|
|
1565
1568
|
/**
|
|
1566
1569
|
* Resolve a tab release on a pane body: the centre moves the tab in, an edge
|
|
1567
1570
|
* splits the pane and seats the tab in the new half. A pane's only tab released
|
|
1568
|
-
* on that pane changes nothing
|
|
1569
|
-
*
|
|
1571
|
+
* on that pane's centre changes nothing; released on its edge it splits, and
|
|
1572
|
+
* the factory's tab backfills the pane the drag would otherwise empty — without
|
|
1573
|
+
* a factory that release also changes nothing, since the split would empty the
|
|
1574
|
+
* pane and seat the tab beside where it already was.
|
|
1570
1575
|
* @param state - current layout.
|
|
1571
1576
|
* @param mint - id source for a pane an edge release creates.
|
|
1572
1577
|
* @param tabId - the dragged tab.
|
|
1573
1578
|
* @param targetPaneId - pane under the pointer.
|
|
1574
1579
|
* @param zone - dock region the pointer released in.
|
|
1580
|
+
* @param makeTab - builds the tab that backfills a pane its only tab splits away from.
|
|
1575
1581
|
* @returns the operations, or none when the release changes nothing.
|
|
1576
1582
|
*/
|
|
1577
|
-
function planDropTab(state, mint, tabId, targetPaneId, zone) {
|
|
1583
|
+
function planDropTab(state, mint, tabId, targetPaneId, zone, makeTab) {
|
|
1578
1584
|
const source = findTabPane(state, tabId);
|
|
1579
1585
|
const target = getPane(state, targetPaneId);
|
|
1580
1586
|
if (target.host !== "dock") return NOTHING;
|
|
@@ -1583,17 +1589,26 @@ function planDropTab(state, mint, tabId, targetPaneId, zone) {
|
|
|
1583
1589
|
if (source.id === targetPaneId) return NOTHING;
|
|
1584
1590
|
return [tabInto(source, tabId, targetPaneId, target.tabs.length)];
|
|
1585
1591
|
}
|
|
1586
|
-
|
|
1592
|
+
const vacates = source.id === targetPaneId && source.tabs.length === 1;
|
|
1593
|
+
if (vacates && makeTab === void 0) return NOTHING;
|
|
1587
1594
|
if (!canSplit(state)) return NOTHING;
|
|
1588
1595
|
const newPaneId = mint("pane");
|
|
1589
|
-
|
|
1596
|
+
const ops = [{
|
|
1590
1597
|
type: "split",
|
|
1591
1598
|
paneId: targetPaneId,
|
|
1592
1599
|
axis: split.axis,
|
|
1593
1600
|
direction: split.direction,
|
|
1594
1601
|
newPaneId,
|
|
1595
1602
|
newSplitId: mint("split")
|
|
1596
|
-
}
|
|
1603
|
+
}];
|
|
1604
|
+
if (vacates && makeTab !== void 0) ops.push({
|
|
1605
|
+
type: "openTab",
|
|
1606
|
+
paneId: targetPaneId,
|
|
1607
|
+
tab: makeTab(mint("tab")),
|
|
1608
|
+
index: source.tabs.length
|
|
1609
|
+
});
|
|
1610
|
+
ops.push(tabInto(source, tabId, newPaneId, 0));
|
|
1611
|
+
return ops;
|
|
1597
1612
|
}
|
|
1598
1613
|
/**
|
|
1599
1614
|
* Take a tab out into a floating panel.
|
|
@@ -2052,11 +2067,29 @@ function dividerSize(root) {
|
|
|
2052
2067
|
return thickness > 0 ? thickness : SPLIT_MINIMUMS.divider;
|
|
2053
2068
|
}
|
|
2054
2069
|
/**
|
|
2070
|
+
* The rendered split control's footprint in the strip's fixed part: its box
|
|
2071
|
+
* plus the strip's own gap, both of which the strip sheds when the control
|
|
2072
|
+
* hides. 0 while the control is hidden or unmeasured.
|
|
2073
|
+
*/
|
|
2074
|
+
function splitControlFootprint(pane) {
|
|
2075
|
+
const control = pane.querySelector("[data-dockkit-split-button]");
|
|
2076
|
+
if (control === null) return 0;
|
|
2077
|
+
const width = control.getBoundingClientRect().width;
|
|
2078
|
+
if (!(width > 0)) return 0;
|
|
2079
|
+
const strip = pane.querySelector("[data-dockkit-strip]");
|
|
2080
|
+
/* v8 ignore next -- the control only renders inside a strip. */
|
|
2081
|
+
return width + (strip === null ? 0 : px(getComputedStyle(strip).columnGap));
|
|
2082
|
+
}
|
|
2083
|
+
/**
|
|
2055
2084
|
* Measure every docked pane under `root`.
|
|
2056
2085
|
* @param root - the docked surface's element.
|
|
2086
|
+
* @param splitHiddenWhenBlocked - whether the embedder hides blocked split
|
|
2087
|
+
* controls (`hideSplitWhenBlocked`); the room rule then leaves the control's
|
|
2088
|
+
* footprint out of each strip's fixed part, so the reading cannot flip with
|
|
2089
|
+
* the control's visibility (see `PaneMeasure.splitControlWidth`).
|
|
2057
2090
|
* @returns each pane's fit, keyed by pane id.
|
|
2058
2091
|
*/
|
|
2059
|
-
function measurePaneFits(root) {
|
|
2092
|
+
function measurePaneFits(root, splitHiddenWhenBlocked = false) {
|
|
2060
2093
|
const minimums = {
|
|
2061
2094
|
divider: dividerSize(root),
|
|
2062
2095
|
chip: chipMinimum(root),
|
|
@@ -2067,7 +2100,8 @@ function measurePaneFits(root) {
|
|
|
2067
2100
|
pane: rectOf(pane),
|
|
2068
2101
|
strip: rectOf(pane.querySelector("[data-dockkit-strip]")),
|
|
2069
2102
|
chipsWidth: rectOf(pane.querySelector("[data-dockkit-strip-tabs]")).width,
|
|
2070
|
-
fillWidth: rectOf(pane.querySelector("[data-dockkit-strip-fill]")).width
|
|
2103
|
+
fillWidth: rectOf(pane.querySelector("[data-dockkit-strip-fill]")).width,
|
|
2104
|
+
splitControlWidth: splitHiddenWhenBlocked ? splitControlFootprint(pane) : 0
|
|
2071
2105
|
}, minimums));
|
|
2072
2106
|
return fits;
|
|
2073
2107
|
}
|
|
@@ -2189,8 +2223,10 @@ function useGesture(reset) {
|
|
|
2189
2223
|
* The per-tab context menu, opened by a secondary press on the chip. It carries
|
|
2190
2224
|
* the close gesture and whatever the embedder appends; the copy and float
|
|
2191
2225
|
* gestures have no menu item — copying is an embedder API, floating is a drag
|
|
2192
|
-
* released clear of the surface.
|
|
2193
|
-
*
|
|
2226
|
+
* released clear of the surface. A menu that would hold no item at all renders
|
|
2227
|
+
* no popup, so a secondary press on a chip with nothing to offer shows nothing.
|
|
2228
|
+
* Presentational — it renders what its props supply and dismisses itself on
|
|
2229
|
+
* outside presses.
|
|
2194
2230
|
*
|
|
2195
2231
|
* It renders in a portal, positioned against the control that opened it. The tab
|
|
2196
2232
|
* strip clips its overflow on purpose (so it never becomes a scroll container
|
|
@@ -2215,11 +2251,11 @@ function placeMenu(anchor, menu) {
|
|
|
2215
2251
|
function TabMenu({ labels, anchor, onClose, onDismiss, extras }) {
|
|
2216
2252
|
const self = useRef(null);
|
|
2217
2253
|
const [position, setPosition] = useState(void 0);
|
|
2254
|
+
const hasItems = onClose !== void 0 || Children.toArray(extras).some((item) => item !== "");
|
|
2218
2255
|
useLayoutEffect(() => {
|
|
2219
|
-
/* v8 ignore next -- the ref is attached by effect time: the menu renders unconditionally. */
|
|
2220
2256
|
if (self.current === null) return;
|
|
2221
2257
|
setPosition(placeMenu(anchor, self.current));
|
|
2222
|
-
}, [anchor]);
|
|
2258
|
+
}, [anchor, hasItems]);
|
|
2223
2259
|
useEffect(() => {
|
|
2224
2260
|
const menu = self.current;
|
|
2225
2261
|
/* v8 ignore next -- the ref is attached by effect time: the menu renders unconditionally. */
|
|
@@ -2232,7 +2268,8 @@ function TabMenu({ labels, anchor, onClose, onDismiss, extras }) {
|
|
|
2232
2268
|
return () => {
|
|
2233
2269
|
window.removeEventListener("pointerdown", onPointerDown, true);
|
|
2234
2270
|
};
|
|
2235
|
-
}, [onDismiss]);
|
|
2271
|
+
}, [onDismiss, hasItems]);
|
|
2272
|
+
if (!hasItems) return null;
|
|
2236
2273
|
return createPortal(jsxs("div", {
|
|
2237
2274
|
className: css.menu,
|
|
2238
2275
|
ref: self,
|
|
@@ -2249,7 +2286,7 @@ function TabMenu({ labels, anchor, onClose, onDismiss, extras }) {
|
|
|
2249
2286
|
onClick: (event) => {
|
|
2250
2287
|
event.stopPropagation();
|
|
2251
2288
|
},
|
|
2252
|
-
children: [jsx("button", {
|
|
2289
|
+
children: [onClose !== void 0 && jsx("button", {
|
|
2253
2290
|
type: "button",
|
|
2254
2291
|
role: "menuitem",
|
|
2255
2292
|
className: css.menuItem,
|
|
@@ -2260,70 +2297,142 @@ function TabMenu({ labels, anchor, onClose, onDismiss, extras }) {
|
|
|
2260
2297
|
}), document.body);
|
|
2261
2298
|
}
|
|
2262
2299
|
//#endregion
|
|
2300
|
+
//#region lib/types/components/TabTitle.js
|
|
2301
|
+
/**
|
|
2302
|
+
* A chip's title: one line, clipped at the chip's inset, never ellipsized.
|
|
2303
|
+
* While the text is wider than its box the span carries
|
|
2304
|
+
* `data-dockkit-tab-clipped`, and the stylesheet fades the text out at the
|
|
2305
|
+
* clipped edge in place of an ellipsis. Written to the DOM directly rather
|
|
2306
|
+
* than through state: a reading changes nothing that renders, only how the
|
|
2307
|
+
* stylesheet paints it. Re-read after every commit (the text may have
|
|
2308
|
+
* changed) and whenever the span's box resizes (the chip shrank or grew).
|
|
2309
|
+
*/
|
|
2310
|
+
/** Set or clear the span's `data-dockkit-tab-clipped` from its current geometry. */
|
|
2311
|
+
function markClipped(element) {
|
|
2312
|
+
if (element.scrollWidth > element.clientWidth + 1) element.dataset.dockkitTabClipped = "";
|
|
2313
|
+
else delete element.dataset.dockkitTabClipped;
|
|
2314
|
+
}
|
|
2315
|
+
/** The title span of a strip chip or a floating panel's header chip. */
|
|
2316
|
+
function TabTitle({ children }) {
|
|
2317
|
+
const span = useRef(null);
|
|
2318
|
+
useLayoutEffect(() => {
|
|
2319
|
+
/* v8 ignore next -- the span is rendered unconditionally. */
|
|
2320
|
+
if (span.current !== null) markClipped(span.current);
|
|
2321
|
+
});
|
|
2322
|
+
useLayoutEffect(() => {
|
|
2323
|
+
const element = span.current;
|
|
2324
|
+
/* v8 ignore next -- the span is rendered unconditionally. */
|
|
2325
|
+
if (element === null || typeof ResizeObserver === "undefined") return void 0;
|
|
2326
|
+
const observer = new ResizeObserver(() => {
|
|
2327
|
+
markClipped(element);
|
|
2328
|
+
});
|
|
2329
|
+
observer.observe(element);
|
|
2330
|
+
return () => {
|
|
2331
|
+
observer.disconnect();
|
|
2332
|
+
};
|
|
2333
|
+
}, []);
|
|
2334
|
+
return jsx("span", {
|
|
2335
|
+
ref: span,
|
|
2336
|
+
className: css.tabTitle,
|
|
2337
|
+
"data-dockkit-tab-title": true,
|
|
2338
|
+
children
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
//#endregion
|
|
2263
2342
|
//#region lib/types/components/TabPanel.js
|
|
2264
2343
|
/**
|
|
2265
2344
|
* One pane: its tab strip (drag source, drop target, split control) and the
|
|
2266
2345
|
* active tab's body with the dock preview overlay. Presentational; every gesture
|
|
2267
2346
|
* leaves through `PaneCallbacks`, and the body itself comes from `renderTab`.
|
|
2268
2347
|
*
|
|
2269
|
-
* A chip is a capsule carrying one control, its close,
|
|
2270
|
-
*
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2273
|
-
*
|
|
2274
|
-
*
|
|
2275
|
-
*
|
|
2276
|
-
|
|
2277
|
-
|
|
2348
|
+
* A chip is a capsule carrying one control, its close, shown over its right
|
|
2349
|
+
* end while the chip is active, hovered, or focused; the context menu
|
|
2350
|
+
* (secondary press) carries the same close plus whatever the embedder appends.
|
|
2351
|
+
* Both close routes draw only while the embedder's `canCloseTab` allows, and
|
|
2352
|
+
* a pane's lone chip whose close is withheld draws quiet — no capsule, no
|
|
2353
|
+
* hover fill — since there is nothing to select against and nothing to do to
|
|
2354
|
+
* it.
|
|
2355
|
+
* Between neighbouring chips sits a slot: a fixed-width box drawing a
|
|
2356
|
+
* hairline, blank beside the active chip, and the drop caret when a drag
|
|
2357
|
+
* targets that index, so a caret never widens the row; the two end slots
|
|
2358
|
+
* exist only while targeted. The chips sit in their own box, the strip's one
|
|
2359
|
+
* shrinking part: in a narrow pane their titles fade at the clipped edge down
|
|
2360
|
+
* to the chip's floor and then the chips scroll there, keeping the active one
|
|
2361
|
+
* in view, so the add control after them (drawn while the embedder's
|
|
2362
|
+
* `canAddTab` allows), the pane's split control, and the embedder's chrome keep
|
|
2363
|
+
* their width and their place at the strip's end.
|
|
2364
|
+
*/
|
|
2365
|
+
/**
|
|
2366
|
+
* The ic_ds_panel_left_outline_16 frame alone: its outer and inner rounded
|
|
2367
|
+
* rectangles as one even-odd ring, without the divider. The glyphs below draw
|
|
2368
|
+
* inside it so they read as siblings of the panel controls beside them.
|
|
2369
|
+
*/
|
|
2370
|
+
const PANEL_FRAME = "M9.67272 0.522841C10.8339 0.522841 11.76 0.522714 12.4963 0.602493C13.2453 0.683657 13.8789 0.854248 14.4264 1.25197C14.7504 1.48739 15.0355 1.77247 15.2709 2.0965C15.6686 2.64394 15.8392 3.27758 15.9204 4.02655C16.0002 4.7629 16 5.68895 16 6.85014V9.14986C16 10.3111 16.0002 11.2371 15.9204 11.9735C15.8392 12.7224 15.6686 13.3561 15.2709 13.9035C15.0355 14.2275 14.7504 14.5126 14.4264 14.748C13.8789 15.1458 13.2453 15.3163 12.4963 15.3975C11.76 15.4773 10.8339 15.4772 9.67272 15.4772H6.3273C5.16611 15.4772 4.24006 15.4773 3.50371 15.3975C2.75474 15.3163 2.1211 15.1458 1.57366 14.748C1.24963 14.5126 0.964549 14.2275 0.729131 13.9035C0.331407 13.3561 0.160817 12.7224 0.0796529 11.9735C-0.000126137 11.2371 1.25338e-09 10.3111 1.25338e-09 9.14986V6.85014C1.25329e-09 5.68895 -0.000126137 4.7629 0.0796529 4.02655C0.160817 3.27758 0.331407 2.64394 0.729131 2.0965C0.964549 1.77247 1.24963 1.48739 1.57366 1.25197C2.1211 0.854248 2.75474 0.683657 3.50371 0.602493C4.24006 0.522714 5.16611 0.522841 6.3273 0.522841H9.67272ZM4.1828 14.0873L5.54303 14.1118C5.78636 14.1128 6.04709 14.1169 6.3273 14.1169H9.67272C10.8639 14.1169 11.7032 14.1164 12.3493 14.0465C12.9824 13.9779 13.3497 13.8494 13.6268 13.6482C13.8354 13.4966 14.0195 13.3125 14.1711 13.1039C14.3723 12.8268 14.5007 12.4595 14.5693 11.8264C14.6393 11.1803 14.6398 10.341 14.6398 9.14986V6.85014C14.6398 5.65896 14.6393 4.81967 14.5693 4.1736C14.5007 3.54048 14.3723 3.17318 14.1711 2.89609C14.0195 2.68747 13.8354 2.50337 13.6268 2.35179C13.3497 2.1506 12.9824 2.02212 12.3493 1.95353C11.7032 1.88358 10.8639 1.88307 9.67272 1.88307H6.3273C6.04709 1.88307 5.78636 1.8862 5.54303 1.88715L4.1828 1.91166C3.99125 1.9216 3.8148 1.93577 3.65076 1.95353C3.01764 2.02212 2.65034 2.1506 2.37325 2.35179C2.16463 2.50337 1.98052 2.68747 1.82895 2.89609C1.62776 3.17318 1.49928 3.54048 1.43069 4.1736C1.36074 4.81967 1.36023 5.65896 1.36023 6.85014V9.14986C1.36023 10.341 1.36074 11.1803 1.43069 11.8264C1.49928 12.4595 1.62776 12.8268 1.82895 13.1039C1.98052 13.3125 2.16463 13.4966 2.37325 13.6482C2.65034 13.8494 3.01764 13.9779 3.65076 14.0465C3.81478 14.0642 3.99127 14.0774 4.1828 14.0873Z";
|
|
2371
|
+
/** The split control's glyph: the panel frame with its divider moved to the centre. */
|
|
2278
2372
|
function SplitGlyph() {
|
|
2279
|
-
return jsxs("svg", {
|
|
2280
|
-
width: "14",
|
|
2281
|
-
height: "14",
|
|
2282
|
-
viewBox: "0 0 14 14",
|
|
2283
|
-
fill: "none",
|
|
2284
|
-
"aria-hidden": "true",
|
|
2285
|
-
children: [jsx("rect", {
|
|
2286
|
-
x: "1.5",
|
|
2287
|
-
y: "2",
|
|
2288
|
-
width: "11",
|
|
2289
|
-
height: "10",
|
|
2290
|
-
rx: "1.5",
|
|
2291
|
-
stroke: "currentColor"
|
|
2292
|
-
}), jsx("path", {
|
|
2293
|
-
d: "M7 2v10",
|
|
2294
|
-
stroke: "currentColor"
|
|
2295
|
-
})]
|
|
2296
|
-
});
|
|
2297
|
-
}
|
|
2298
|
-
/** The add control's glyph. */
|
|
2299
|
-
function PlusGlyph() {
|
|
2300
2373
|
return jsx("svg", {
|
|
2301
|
-
width: "
|
|
2302
|
-
height: "
|
|
2303
|
-
viewBox: "0 0
|
|
2374
|
+
width: "16",
|
|
2375
|
+
height: "16",
|
|
2376
|
+
viewBox: "0 0 16 16",
|
|
2304
2377
|
fill: "none",
|
|
2305
2378
|
"aria-hidden": "true",
|
|
2306
2379
|
children: jsx("path", {
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2380
|
+
fillRule: "evenodd",
|
|
2381
|
+
clipRule: "evenodd",
|
|
2382
|
+
d: `${PANEL_FRAME}M7.31989 1.88307H8.68012V14.1169H7.31989V1.88307Z`,
|
|
2383
|
+
fill: "currentColor"
|
|
2311
2384
|
})
|
|
2312
2385
|
});
|
|
2313
2386
|
}
|
|
2314
|
-
/**
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2387
|
+
/**
|
|
2388
|
+
* The drop hint's fill per zone: the half or the whole a release would fill
|
|
2389
|
+
* drawn solid, so the hint names its zone before its caption is read. A half
|
|
2390
|
+
* is drawn out to the frame's outer edge, under the ring, so its visible edge
|
|
2391
|
+
* is exactly the ring's inner edge with no seam at the corners; the whole sits
|
|
2392
|
+
* one stroke inside the frame so the ring stays visible around it.
|
|
2393
|
+
*/
|
|
2394
|
+
const ZONE_FILL = {
|
|
2395
|
+
center: "M4.56 3.48H11.44A1.6 1.6 0 0 1 13.04 5.08V10.92A1.6 1.6 0 0 1 11.44 12.52H4.56A1.6 1.6 0 0 1 2.96 10.92V5.08A1.6 1.6 0 0 1 4.56 3.48Z",
|
|
2396
|
+
left: "M4 0.523H8V15.477H4A4 4 0 0 1 0 11.477V4.523A4 4 0 0 1 4 0.523Z",
|
|
2397
|
+
right: "M8 0.523H12A4 4 0 0 1 16 4.523V11.477A4 4 0 0 1 12 15.477H8Z",
|
|
2398
|
+
top: "M0 8V4.523A4 4 0 0 1 4 0.523H12A4 4 0 0 1 16 4.523V8Z",
|
|
2399
|
+
bottom: "M0 8H16V11.477A4 4 0 0 1 12 15.477H4A4 4 0 0 1 0 11.477Z"
|
|
2400
|
+
};
|
|
2401
|
+
/** The drop hint's glyph: the panel frame with the zone's fill drawn solid. */
|
|
2402
|
+
function ZoneGlyph({ zone }) {
|
|
2403
|
+
return jsxs("svg", {
|
|
2404
|
+
width: "16",
|
|
2405
|
+
height: "16",
|
|
2406
|
+
viewBox: "0 0 16 16",
|
|
2320
2407
|
fill: "none",
|
|
2321
2408
|
"aria-hidden": "true",
|
|
2322
|
-
children: jsx("path", {
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2409
|
+
children: [jsx("path", {
|
|
2410
|
+
fillRule: "evenodd",
|
|
2411
|
+
clipRule: "evenodd",
|
|
2412
|
+
d: PANEL_FRAME,
|
|
2413
|
+
fill: "currentColor"
|
|
2414
|
+
}), jsx("path", {
|
|
2415
|
+
d: ZONE_FILL[zone],
|
|
2416
|
+
fill: "currentColor"
|
|
2417
|
+
})]
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
/**
|
|
2421
|
+
* One landing card, inset inside the region a release would fill: a dashed
|
|
2422
|
+
* frame, the zone's glyph, and its caption. `active` is the region under the
|
|
2423
|
+
* pointer; a sibling shown for orientation only draws quieter.
|
|
2424
|
+
*/
|
|
2425
|
+
function DockHint({ zone, active, labels }) {
|
|
2426
|
+
return jsx("div", {
|
|
2427
|
+
className: css.dockHint,
|
|
2428
|
+
"data-dockkit-dock-zone": zone,
|
|
2429
|
+
"data-dockkit-drop-active": active || void 0,
|
|
2430
|
+
children: jsxs("div", {
|
|
2431
|
+
className: css.dockHintCard,
|
|
2432
|
+
children: [jsx(ZoneGlyph, { zone }), jsx("span", {
|
|
2433
|
+
className: css.dockHintLabel,
|
|
2434
|
+
children: labels.dropZone[zone]
|
|
2435
|
+
})]
|
|
2327
2436
|
})
|
|
2328
2437
|
});
|
|
2329
2438
|
}
|
|
@@ -2348,10 +2457,80 @@ function chipToFocus(key, tabs, tabId) {
|
|
|
2348
2457
|
function selects(key) {
|
|
2349
2458
|
return key === "Enter" || key === " ";
|
|
2350
2459
|
}
|
|
2351
|
-
/**
|
|
2352
|
-
|
|
2460
|
+
/**
|
|
2461
|
+
* Which sides of the chip box hold chips scrolled out of view, as the
|
|
2462
|
+
* `data-dockkit-strip-scroll` value the stylesheet fades: `undefined` while
|
|
2463
|
+
* every chip is in view.
|
|
2464
|
+
*/
|
|
2465
|
+
function hiddenSides(box) {
|
|
2466
|
+
const start = box.scrollLeft > 1;
|
|
2467
|
+
const end = box.scrollLeft + box.clientWidth < box.scrollWidth - 1;
|
|
2468
|
+
if (start && end) return "start end";
|
|
2469
|
+
if (start) return "start";
|
|
2470
|
+
if (end) return "end";
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* Keep the chip box's `data-dockkit-strip-scroll` current: read after each
|
|
2474
|
+
* commit that can change the chips, on scroll, and on resize. Written to the
|
|
2475
|
+
* DOM directly rather than through state because a reading never changes
|
|
2476
|
+
* what renders, only how the stylesheet fades it.
|
|
2477
|
+
*
|
|
2478
|
+
* Known gap: a content-width change that alters neither `tabs` nor the box's
|
|
2479
|
+
* outer size — a live `renderTabTitle` growing a chip, or a drop-caret slot
|
|
2480
|
+
* mounting mid-drag — keeps the fade at its last reading until the next
|
|
2481
|
+
* scroll or resize. The fade is orientation chrome, so a stale edge fades a
|
|
2482
|
+
* few frames late rather than hiding anything.
|
|
2483
|
+
*/
|
|
2484
|
+
function useStripScrollFades(box, tabs) {
|
|
2485
|
+
useLayoutEffect(() => {
|
|
2486
|
+
const element = box.current;
|
|
2487
|
+
/* v8 ignore next -- the box is rendered unconditionally with the strip. */
|
|
2488
|
+
if (element === null) return void 0;
|
|
2489
|
+
const apply = () => {
|
|
2490
|
+
const sides = hiddenSides(element);
|
|
2491
|
+
if (sides === void 0) delete element.dataset.dockkitStripScroll;
|
|
2492
|
+
else element.dataset.dockkitStripScroll = sides;
|
|
2493
|
+
};
|
|
2494
|
+
apply();
|
|
2495
|
+
element.addEventListener("scroll", apply, { passive: true });
|
|
2496
|
+
const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(apply);
|
|
2497
|
+
observer?.observe(element);
|
|
2498
|
+
return () => {
|
|
2499
|
+
element.removeEventListener("scroll", apply);
|
|
2500
|
+
observer?.disconnect();
|
|
2501
|
+
};
|
|
2502
|
+
}, [box, tabs]);
|
|
2503
|
+
}
|
|
2504
|
+
/**
|
|
2505
|
+
* Bring the active chip into the chip box's view whenever the active tab or
|
|
2506
|
+
* the row of chips changes: a tab opened or selected past the box's edge, or
|
|
2507
|
+
* moved there by a close or a reorder, scrolls the box to it, with the fade
|
|
2508
|
+
* band (24px) cleared so the chip is not under it. A chip already in view
|
|
2509
|
+
* moves nothing. Direct DOM, like the fades above: the box's scroll position
|
|
2510
|
+
* renders nothing.
|
|
2511
|
+
*/
|
|
2512
|
+
function useActiveChipInView(box, chips, tabs, activeTabId) {
|
|
2513
|
+
useLayoutEffect(() => {
|
|
2514
|
+
const element = box.current;
|
|
2515
|
+
const chip = activeTabId === void 0 ? void 0 : chips.get(activeTabId);
|
|
2516
|
+
/* v8 ignore next -- the box and the active tab's chip are rendered with the strip. */
|
|
2517
|
+
if (element === null || chip === void 0) return;
|
|
2518
|
+
const bounds = element.getBoundingClientRect();
|
|
2519
|
+
const rect = chip.getBoundingClientRect();
|
|
2520
|
+
if (rect.left < bounds.left) element.scrollLeft += rect.left - bounds.left - STRIP_FADE;
|
|
2521
|
+
else if (rect.right > bounds.right) element.scrollLeft += rect.right - bounds.right + STRIP_FADE;
|
|
2522
|
+
}, [
|
|
2523
|
+
box,
|
|
2524
|
+
chips,
|
|
2525
|
+
tabs,
|
|
2526
|
+
activeTabId
|
|
2527
|
+
]);
|
|
2528
|
+
}
|
|
2529
|
+
/** Width of the chip box's fade at a hidden side; mirrors the stylesheet's 24px. */
|
|
2530
|
+
const STRIP_FADE = 24;
|
|
2531
|
+
/** Why the split control cannot act right now. */
|
|
2532
|
+
function splitBlockedTitle(labels, block) {
|
|
2353
2533
|
switch (block) {
|
|
2354
|
-
case void 0: return labels.splitPane;
|
|
2355
2534
|
case "budget": return labels.splitPaneDisabled;
|
|
2356
2535
|
case "width": return labels.splitPaneNarrow;
|
|
2357
2536
|
}
|
|
@@ -2360,6 +2539,9 @@ function splitTitle(labels, block) {
|
|
|
2360
2539
|
function TabPanel({ state, pane, callbacks }) {
|
|
2361
2540
|
const [menu, setMenu] = useState(void 0);
|
|
2362
2541
|
const [chips] = useState(() => /* @__PURE__ */ new Map());
|
|
2542
|
+
const stripTabs = useRef(null);
|
|
2543
|
+
useStripScrollFades(stripTabs, pane.tabs);
|
|
2544
|
+
useActiveChipInView(stripTabs, chips, pane.tabs, pane.activeTabId);
|
|
2363
2545
|
const active = pane.activeTabId === void 0 ? void 0 : getTab(state, pane.activeTabId);
|
|
2364
2546
|
const block = callbacks.splitBlock(pane.id);
|
|
2365
2547
|
const target = callbacks.dropTarget;
|
|
@@ -2390,21 +2572,25 @@ function TabPanel({ state, pane, callbacks }) {
|
|
|
2390
2572
|
"data-dockkit-strip": pane.id,
|
|
2391
2573
|
children: [
|
|
2392
2574
|
jsxs("div", {
|
|
2575
|
+
ref: stripTabs,
|
|
2393
2576
|
className: css.stripTabs,
|
|
2394
2577
|
role: "presentation",
|
|
2395
2578
|
"data-dockkit-strip-tabs": pane.id,
|
|
2396
2579
|
children: [pane.tabs.map((tabId, index) => {
|
|
2397
2580
|
const tab = getTab(state, tabId);
|
|
2398
2581
|
const selected = tabId === pane.activeTabId;
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2582
|
+
const closable = callbacks.canCloseTab(tabId);
|
|
2583
|
+
const quiet = !closable && pane.tabs.length === 1;
|
|
2584
|
+
return jsxs(Fragment$1, { children: [(index > 0 || stripIndex === index) && jsx("div", {
|
|
2585
|
+
className: clsx(css.slot, stripIndex === index && css.slotCaret),
|
|
2586
|
+
"data-dockkit-caret": stripIndex === index ? index : void 0
|
|
2402
2587
|
}), jsxs("div", {
|
|
2403
2588
|
role: "tab",
|
|
2404
2589
|
"aria-selected": selected,
|
|
2405
2590
|
tabIndex: selected ? 0 : -1,
|
|
2406
|
-
className: clsx(css.tab, selected && css.tabActive, callbacks.draggingTabId === tabId && css.tabDragging),
|
|
2591
|
+
className: clsx(css.tab, selected && css.tabActive, quiet && css.tabQuiet, callbacks.draggingTabId === tabId && css.tabDragging),
|
|
2407
2592
|
"data-dockkit-tab": tabId,
|
|
2593
|
+
"data-dockkit-tab-quiet": quiet || void 0,
|
|
2408
2594
|
ref: (element) => {
|
|
2409
2595
|
if (element === null) chips.delete(tabId);
|
|
2410
2596
|
else chips.set(tabId, element);
|
|
@@ -2439,12 +2625,8 @@ function TabPanel({ state, pane, callbacks }) {
|
|
|
2439
2625
|
});
|
|
2440
2626
|
},
|
|
2441
2627
|
children: [
|
|
2442
|
-
jsx(
|
|
2443
|
-
|
|
2444
|
-
"data-dockkit-tab-title": true,
|
|
2445
|
-
children: callbacks.renderTabTitle?.(tab) ?? tab.title
|
|
2446
|
-
}),
|
|
2447
|
-
jsx("button", {
|
|
2628
|
+
jsx(TabTitle, { children: callbacks.renderTabTitle?.(tab) ?? tab.title }),
|
|
2629
|
+
closable && jsx("button", {
|
|
2448
2630
|
type: "button",
|
|
2449
2631
|
className: css.tabClose,
|
|
2450
2632
|
"aria-label": callbacks.labels.closeTab,
|
|
@@ -2456,15 +2638,15 @@ function TabPanel({ state, pane, callbacks }) {
|
|
|
2456
2638
|
event.stopPropagation();
|
|
2457
2639
|
callbacks.onCloseTab(tabId);
|
|
2458
2640
|
},
|
|
2459
|
-
children: jsx(
|
|
2641
|
+
children: jsx(IconCloseFill14, { size: 14 })
|
|
2460
2642
|
}),
|
|
2461
2643
|
menu?.tabId === tabId && jsx(TabMenu, {
|
|
2462
2644
|
labels: callbacks.labels,
|
|
2463
2645
|
anchor: menu.anchor,
|
|
2464
|
-
onClose: () => {
|
|
2646
|
+
onClose: closable ? () => {
|
|
2465
2647
|
setMenu(void 0);
|
|
2466
2648
|
callbacks.onCloseTab(tabId);
|
|
2467
|
-
},
|
|
2649
|
+
} : void 0,
|
|
2468
2650
|
onDismiss: () => {
|
|
2469
2651
|
setMenu(void 0);
|
|
2470
2652
|
},
|
|
@@ -2475,39 +2657,49 @@ function TabPanel({ state, pane, callbacks }) {
|
|
|
2475
2657
|
]
|
|
2476
2658
|
})] }, tabId);
|
|
2477
2659
|
}), stripIndex === pane.tabs.length && jsx("div", {
|
|
2478
|
-
className: css.
|
|
2660
|
+
className: clsx(css.slot, css.slotCaret),
|
|
2479
2661
|
"data-dockkit-caret": stripIndex
|
|
2480
2662
|
})]
|
|
2481
2663
|
}),
|
|
2482
|
-
callbacks.canAddTab(pane.id) && jsx(
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2664
|
+
callbacks.canAddTab(pane.id) && jsx(Tooltip, {
|
|
2665
|
+
label: callbacks.labels.addTab,
|
|
2666
|
+
side: "bottom",
|
|
2667
|
+
delayMs: 500,
|
|
2668
|
+
children: jsx("button", {
|
|
2669
|
+
type: "button",
|
|
2670
|
+
className: css.addTab,
|
|
2671
|
+
"aria-label": callbacks.labels.addTab,
|
|
2672
|
+
"data-dockkit-add-tab": pane.id,
|
|
2673
|
+
onClick: (event) => {
|
|
2674
|
+
event.stopPropagation();
|
|
2675
|
+
callbacks.onAddTab(pane.id);
|
|
2676
|
+
},
|
|
2677
|
+
children: jsx(IconPlusOutline16, { size: 14 })
|
|
2678
|
+
})
|
|
2493
2679
|
}),
|
|
2494
2680
|
jsx("div", {
|
|
2495
2681
|
className: css.stripFill,
|
|
2496
2682
|
"data-dockkit-strip-fill": true
|
|
2497
2683
|
}),
|
|
2498
|
-
!(callbacks.
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
title: splitTitle(callbacks.labels, block),
|
|
2684
|
+
!(callbacks.hideSplitWhenBlocked && block !== void 0) && jsx(Tooltip, {
|
|
2685
|
+
label: callbacks.labels.splitPane,
|
|
2686
|
+
side: "bottom",
|
|
2687
|
+
delayMs: 500,
|
|
2503
2688
|
disabled: block !== void 0,
|
|
2504
|
-
"
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
callbacks.
|
|
2509
|
-
|
|
2510
|
-
|
|
2689
|
+
children: jsx("button", {
|
|
2690
|
+
type: "button",
|
|
2691
|
+
className: css.iconButton,
|
|
2692
|
+
"aria-label": callbacks.labels.splitPane,
|
|
2693
|
+
title: block === void 0 ? void 0 : splitBlockedTitle(callbacks.labels, block),
|
|
2694
|
+
disabled: block !== void 0,
|
|
2695
|
+
"data-dockkit-split-button": pane.id,
|
|
2696
|
+
"data-dockkit-split-blocked": block,
|
|
2697
|
+
onClick: (event) => {
|
|
2698
|
+
event.stopPropagation();
|
|
2699
|
+
callbacks.onSplitPane(pane.id);
|
|
2700
|
+
},
|
|
2701
|
+
children: jsx(SplitGlyph, {})
|
|
2702
|
+
})
|
|
2511
2703
|
}),
|
|
2512
2704
|
pane.id === callbacks.chromePaneId && callbacks.chrome !== void 0 && jsx("div", {
|
|
2513
2705
|
className: css.stripChrome,
|
|
@@ -2523,18 +2715,22 @@ function TabPanel({ state, pane, callbacks }) {
|
|
|
2523
2715
|
children: [active === void 0 ? jsx("p", {
|
|
2524
2716
|
className: css.empty,
|
|
2525
2717
|
children: callbacks.labels.emptyPane
|
|
2526
|
-
}) : callbacks.renderTab(active), zone !== void 0 &&
|
|
2527
|
-
className: css.
|
|
2528
|
-
"data-dockkit-dock-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
}))
|
|
2718
|
+
}) : callbacks.renderTab(active), zone !== void 0 && jsxs(Fragment, { children: [jsx("div", {
|
|
2719
|
+
className: css.dockScrim,
|
|
2720
|
+
"data-dockkit-dock-scrim": true
|
|
2721
|
+
}), callbacks.horizontalDrops && zone !== "center" ? jsxs(Fragment, { children: [jsx(DockHint, {
|
|
2722
|
+
zone: "left",
|
|
2723
|
+
active: zone === "left",
|
|
2724
|
+
labels: callbacks.labels
|
|
2725
|
+
}), jsx(DockHint, {
|
|
2726
|
+
zone: "right",
|
|
2727
|
+
active: zone === "right",
|
|
2728
|
+
labels: callbacks.labels
|
|
2729
|
+
})] }) : jsx(DockHint, {
|
|
2730
|
+
zone,
|
|
2731
|
+
active: true,
|
|
2732
|
+
labels: callbacks.labels
|
|
2733
|
+
})] })]
|
|
2538
2734
|
})]
|
|
2539
2735
|
});
|
|
2540
2736
|
}
|
|
@@ -2596,7 +2792,7 @@ const NO_PREVIEW = {
|
|
|
2596
2792
|
};
|
|
2597
2793
|
/** Nothing measured yet: every pane fits until a reading says otherwise. */
|
|
2598
2794
|
const NO_FITS = /* @__PURE__ */ new Map();
|
|
2599
|
-
/** The default
|
|
2795
|
+
/** The default policy for the omitted callbacks: every pane offers the add control, every tab its close. */
|
|
2600
2796
|
const ALWAYS = () => true;
|
|
2601
2797
|
/**
|
|
2602
2798
|
* Resolve where a pointer sits inside the docked surface. An edge zone is only
|
|
@@ -2642,7 +2838,7 @@ function sameSizes(a, b) {
|
|
|
2642
2838
|
});
|
|
2643
2839
|
}
|
|
2644
2840
|
/** The split tree and the gestures over it. */
|
|
2645
|
-
function DockSurface({ state, canSplit, canAddTab, intents, labels, renderTab, renderTabTitle, renderTabMenuItems, chrome, onRoom, dropZones = "edges", minPaneFraction = MIN_PANE_FRACTION,
|
|
2841
|
+
function DockSurface({ state, canSplit, canAddTab, canCloseTab, intents, labels, renderTab, renderTabTitle, renderTabMenuItems, chrome, onRoom, dropZones = "edges", minPaneFraction = MIN_PANE_FRACTION, hideSplitWhenBlocked = false }) {
|
|
2646
2842
|
const surface = useRef(null);
|
|
2647
2843
|
const [preview, setPreview] = useState(NO_PREVIEW);
|
|
2648
2844
|
const [fits, setFits] = useState(NO_FITS);
|
|
@@ -2658,10 +2854,10 @@ function DockSurface({ state, canSplit, canAddTab, intents, labels, renderTab, r
|
|
|
2658
2854
|
}, []);
|
|
2659
2855
|
const remeasure = useCallback(() => {
|
|
2660
2856
|
withSurface((root) => {
|
|
2661
|
-
const next = measurePaneFits(root);
|
|
2857
|
+
const next = measurePaneFits(root, hideSplitWhenBlocked);
|
|
2662
2858
|
setFits((current) => sameFits(current, next) ? current : next);
|
|
2663
2859
|
});
|
|
2664
|
-
}, [withSurface]);
|
|
2860
|
+
}, [withSurface, hideSplitWhenBlocked]);
|
|
2665
2861
|
useLayoutEffect(() => {
|
|
2666
2862
|
remeasure();
|
|
2667
2863
|
});
|
|
@@ -2753,8 +2949,9 @@ function DockSurface({ state, canSplit, canAddTab, intents, labels, renderTab, r
|
|
|
2753
2949
|
});
|
|
2754
2950
|
},
|
|
2755
2951
|
splitBlock,
|
|
2756
|
-
|
|
2952
|
+
hideSplitWhenBlocked,
|
|
2757
2953
|
canAddTab: canAddTab ?? ALWAYS,
|
|
2954
|
+
canCloseTab: canCloseTab ?? ALWAYS,
|
|
2758
2955
|
dropTarget: preview.dropTarget,
|
|
2759
2956
|
horizontalDrops: dropZones === "horizontal",
|
|
2760
2957
|
draggingTabId: preview.draggingTabId,
|
|
@@ -2782,8 +2979,9 @@ function DockSurface({ state, canSplit, canAddTab, intents, labels, renderTab, r
|
|
|
2782
2979
|
//#region lib/types/components/FloatLayer.js
|
|
2783
2980
|
/**
|
|
2784
2981
|
* The floating layer: one overlay panel per floating pane, bottom-to-top in the
|
|
2785
|
-
* model's z order. A floating pane hosts exactly one tab
|
|
2786
|
-
* strip
|
|
2982
|
+
* model's z order. A floating pane hosts exactly one tab; its header is the
|
|
2983
|
+
* strip's row holding that tab's chip, never selectable or closable from the
|
|
2984
|
+
* chip, and the send-back and close controls. Pressing a panel's body raises it. Its grip
|
|
2787
2985
|
* and corner report through their gesture instead: a press released in place is
|
|
2788
2986
|
* a click and raises the panel; a drag records the move or resize, and that
|
|
2789
2987
|
* operation raises the panel itself, so one gesture is one intent. Raising a
|
|
@@ -2809,7 +3007,7 @@ function raised(state, paneId) {
|
|
|
2809
3007
|
return state.activePaneId === paneId && state.floats.at(-1) === paneId;
|
|
2810
3008
|
}
|
|
2811
3009
|
/** Every floating panel, in z order. */
|
|
2812
|
-
function FloatLayer({ state, intents, labels, renderTab, renderTabTitle }) {
|
|
3010
|
+
function FloatLayer({ state, intents, labels, renderTab, renderTabTitle, canCloseTab }) {
|
|
2813
3011
|
const [preview, setPreview] = useState(void 0);
|
|
2814
3012
|
const begin = useGesture(() => {
|
|
2815
3013
|
setPreview(void 0);
|
|
@@ -2864,42 +3062,53 @@ function FloatLayer({ state, intents, labels, renderTab, renderTabTitle }) {
|
|
|
2864
3062
|
},
|
|
2865
3063
|
children: [
|
|
2866
3064
|
jsxs("header", {
|
|
2867
|
-
className: css.floatHeader,
|
|
3065
|
+
className: clsx(css.tabStrip, css.floatHeader),
|
|
2868
3066
|
"data-dockkit-float-grip": paneId,
|
|
2869
3067
|
onPointerDown: (event) => {
|
|
2870
3068
|
drag("move", paneId, event);
|
|
2871
3069
|
},
|
|
2872
3070
|
children: [
|
|
2873
|
-
jsx("
|
|
2874
|
-
className: css.floatTitle,
|
|
3071
|
+
jsx("div", {
|
|
3072
|
+
className: clsx(css.tab, css.floatTitle),
|
|
2875
3073
|
"data-dockkit-float-title": true,
|
|
2876
|
-
children: renderTabTitle?.(tab) ?? tab.title
|
|
3074
|
+
children: jsx(TabTitle, { children: renderTabTitle?.(tab) ?? tab.title })
|
|
2877
3075
|
}),
|
|
2878
|
-
jsx("
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
"
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
3076
|
+
jsx("div", { className: css.stripFill }),
|
|
3077
|
+
jsx(Tooltip, {
|
|
3078
|
+
label: labels.dockFloat,
|
|
3079
|
+
side: "bottom",
|
|
3080
|
+
delayMs: 500,
|
|
3081
|
+
children: jsx("button", {
|
|
3082
|
+
type: "button",
|
|
3083
|
+
className: css.iconButton,
|
|
3084
|
+
"aria-label": labels.dockFloat,
|
|
3085
|
+
"data-dockkit-float-dock": paneId,
|
|
3086
|
+
onPointerDown: (event) => {
|
|
3087
|
+
event.stopPropagation();
|
|
3088
|
+
},
|
|
3089
|
+
onClick: () => {
|
|
3090
|
+
intents.unfloatPane(paneId);
|
|
3091
|
+
},
|
|
3092
|
+
children: jsx(IconPanelLeftOutline16, { className: css.dockGlyph })
|
|
3093
|
+
})
|
|
2890
3094
|
}),
|
|
2891
|
-
jsx(
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
"
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
3095
|
+
(canCloseTab?.(tab.id) ?? true) && jsx(Tooltip, {
|
|
3096
|
+
label: labels.closeFloat,
|
|
3097
|
+
side: "bottom",
|
|
3098
|
+
delayMs: 500,
|
|
3099
|
+
children: jsx("button", {
|
|
3100
|
+
type: "button",
|
|
3101
|
+
className: css.iconButton,
|
|
3102
|
+
"aria-label": labels.closeFloat,
|
|
3103
|
+
"data-dockkit-float-close": paneId,
|
|
3104
|
+
onPointerDown: (event) => {
|
|
3105
|
+
event.stopPropagation();
|
|
3106
|
+
},
|
|
3107
|
+
onClick: () => {
|
|
3108
|
+
intents.closeTab(tab.id);
|
|
3109
|
+
},
|
|
3110
|
+
children: jsx(IconCloseOutline16, {})
|
|
3111
|
+
})
|
|
2903
3112
|
})
|
|
2904
3113
|
]
|
|
2905
3114
|
}),
|