@stll/ui 0.18.0 → 0.20.0
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/button-variants.d.ts +2 -2
- package/dist/components/composer.d.ts +108 -0
- package/dist/components/composer.js +132 -0
- package/dist/components/landing.d.ts +61 -0
- package/dist/components/landing.js +97 -0
- package/dist/components/sidebar.d.ts +109 -0
- package/dist/components/sidebar.js +361 -0
- package/dist/components/sidebar.logic.d.ts +34 -0
- package/dist/components/sidebar.logic.js +27 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -3
- package/dist/inspector/entity-tab.d.ts +54 -0
- package/dist/inspector/entity-tab.js +56 -0
- package/dist/inspector/entity-tab.logic.d.ts +28 -0
- package/dist/inspector/entity-tab.logic.js +32 -0
- package/dist/inspector/facet-bar.d.ts +46 -0
- package/dist/inspector/facet-bar.js +166 -0
- package/dist/inspector/facet-bar.logic.d.ts +51 -0
- package/dist/inspector/facet-bar.logic.js +53 -0
- package/dist/inspector/index.d.ts +3 -1
- package/dist/inspector/index.js +3 -1
- package/dist/inspector/tabs.d.ts +1 -1
- package/dist/kanban/band-peek.d.ts +26 -33
- package/dist/kanban/band-peek.js +24 -30
- package/dist/kanban/column-header.d.ts +8 -1
- package/dist/kanban/column-header.js +9 -2
- package/dist/kanban/drag-interactions.d.ts +9 -1
- package/dist/kanban/drag-interactions.js +10 -1
- package/dist/kanban/index.d.ts +2 -2
- package/dist/kanban/index.js +2 -2
- package/dist/kanban/sortable-interactions.d.ts +12 -0
- package/dist/kanban/subgroup-board.d.ts +33 -6
- package/dist/kanban/subgroup-board.js +75 -22
- package/dist/kanban/virtual-cell.d.ts +10 -1
- package/dist/kanban/virtual-cell.js +25 -3
- package/dist/lib/control-size.d.ts +1 -1
- package/dist/lib/slot.d.ts +28 -0
- package/dist/lib/slot.js +64 -0
- package/package.json +17 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ComponentProps, ReactNode } from "react";
|
|
2
|
+
//#region src/inspector/entity-tab.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* One rail tab for an open entity: a bordered `InspectorRailTab` cell
|
|
5
|
+
* showing the active tab's icon, with a tooltip carrying the full label.
|
|
6
|
+
* Generic over what "entity" means — the host supplies the icon dispatch
|
|
7
|
+
* (file type, task status, chat, …) and, for kinds that want it, the
|
|
8
|
+
* inactive-state glyph; this only owns the cell's shared shape and
|
|
9
|
+
* affordances.
|
|
10
|
+
*/
|
|
11
|
+
declare const InspectorEntityTab: ({ active, label, glyph, icon, onClose, onSelect, ...props }: InspectorEntityTabProps) => import("react").JSX.Element;
|
|
12
|
+
type InspectorEntityTabPassthroughProps = Omit<ComponentProps<"button">, "children" | "onAuxClick" | "onClick">;
|
|
13
|
+
type InspectorEntityTabProps = {
|
|
14
|
+
/** Whether this tab is the one currently shown in the pane. Drives the
|
|
15
|
+
* active spine, and — when `glyph` is given — swaps the cell's content
|
|
16
|
+
* between `glyph` and `icon`. */
|
|
17
|
+
active: boolean;
|
|
18
|
+
/** Full entity name. Both the button's accessible name and the tooltip
|
|
19
|
+
* text — the cell itself never has room to show it. */
|
|
20
|
+
label: string;
|
|
21
|
+
/**
|
|
22
|
+
* Short text shown in the cell instead of `icon` while the tab is
|
|
23
|
+
* inactive (e.g. a 3-character stem the host derives with
|
|
24
|
+
* `entityTabGlyph`). Omit it for an entity kind whose icon alone already
|
|
25
|
+
* identifies it (a status glyph, a colored dot, a logo): `icon` then
|
|
26
|
+
* renders in both states, dimmed while inactive instead of swapping to
|
|
27
|
+
* text.
|
|
28
|
+
*/
|
|
29
|
+
glyph?: string | undefined;
|
|
30
|
+
/** Shown in the cell while the tab is active — and, with no `glyph`,
|
|
31
|
+
* while inactive too (dimmed). */
|
|
32
|
+
icon: ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Middle-click (`onAuxClick` button 1) closes the tab. A keyboard- and
|
|
35
|
+
* pointer-reachable close affordance still has to exist — normally a
|
|
36
|
+
* "Close" item in the host's own context menu, wired to the same
|
|
37
|
+
* callback — this only covers the middle-click gesture.
|
|
38
|
+
*/
|
|
39
|
+
onClose?: (() => void) | undefined;
|
|
40
|
+
/** Fires on a (contained) left click: activate this tab. */
|
|
41
|
+
onSelect?: (() => void) | undefined;
|
|
42
|
+
} & InspectorEntityTabPassthroughProps;
|
|
43
|
+
/**
|
|
44
|
+
* Short abbreviation for a rail tab's inactive glyph: the filename stem,
|
|
45
|
+
* the first `length` characters; the cell uppercases it.
|
|
46
|
+
*
|
|
47
|
+
* A leading dot is part of the name, not an extension separator — a
|
|
48
|
+
* dotfile's own extension (a second dot further in) still drops, but the
|
|
49
|
+
* leading dot itself survives into the glyph: `entityTabGlyph(".gitignore")`
|
|
50
|
+
* is `".gi"`, not `""`.
|
|
51
|
+
*/
|
|
52
|
+
declare const entityTabGlyph: (name: string, length?: number) => string;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { InspectorEntityTab, InspectorEntityTabProps, entityTabGlyph };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { SIDE_RAIL_TAB_ICON_SIZE } from "./layout-tokens.js";
|
|
3
|
+
import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "../components/tooltip.js";
|
|
4
|
+
import { InspectorRailTab } from "./chrome.js";
|
|
5
|
+
import { containedEventHandler } from "../hooks/use-contained-handler.js";
|
|
6
|
+
import { resolveEntityTabActivateHandler, resolveEntityTabCloseHandler } from "./entity-tab.logic.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/inspector/entity-tab.tsx
|
|
9
|
+
/**
|
|
10
|
+
* One rail tab for an open entity: a bordered `InspectorRailTab` cell
|
|
11
|
+
* showing the active tab's icon, with a tooltip carrying the full label.
|
|
12
|
+
* Generic over what "entity" means — the host supplies the icon dispatch
|
|
13
|
+
* (file type, task status, chat, …) and, for kinds that want it, the
|
|
14
|
+
* inactive-state glyph; this only owns the cell's shared shape and
|
|
15
|
+
* affordances.
|
|
16
|
+
*/
|
|
17
|
+
const InspectorEntityTab = ({ active, label, glyph, icon, onClose, onSelect, ...props }) => /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(InspectorRailTab, {
|
|
18
|
+
...props,
|
|
19
|
+
active,
|
|
20
|
+
"aria-label": label,
|
|
21
|
+
onAuxClick: (event) => {
|
|
22
|
+
const close = resolveEntityTabCloseHandler(event.button, onClose);
|
|
23
|
+
if (close) {
|
|
24
|
+
event.preventDefault();
|
|
25
|
+
close();
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
onClick: containedEventHandler(resolveEntityTabActivateHandler(onSelect)),
|
|
29
|
+
children: !active && glyph !== void 0 ? /* @__PURE__ */ jsx("span", {
|
|
30
|
+
className: "text-[9px] leading-none font-semibold tracking-tight uppercase",
|
|
31
|
+
children: glyph
|
|
32
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
33
|
+
className: cn("flex items-center justify-center", SIDE_RAIL_TAB_ICON_SIZE, !active && "opacity-70"),
|
|
34
|
+
children: icon
|
|
35
|
+
})
|
|
36
|
+
}) }), /* @__PURE__ */ jsx(TooltipPopup, {
|
|
37
|
+
side: "left",
|
|
38
|
+
children: label
|
|
39
|
+
})] });
|
|
40
|
+
/**
|
|
41
|
+
* Short abbreviation for a rail tab's inactive glyph: the filename stem,
|
|
42
|
+
* the first `length` characters; the cell uppercases it.
|
|
43
|
+
*
|
|
44
|
+
* A leading dot is part of the name, not an extension separator — a
|
|
45
|
+
* dotfile's own extension (a second dot further in) still drops, but the
|
|
46
|
+
* leading dot itself survives into the glyph: `entityTabGlyph(".gitignore")`
|
|
47
|
+
* is `".gi"`, not `""`.
|
|
48
|
+
*/
|
|
49
|
+
const entityTabGlyph = (name, length = 3) => {
|
|
50
|
+
const leadingDot = name.startsWith(".") ? "." : "";
|
|
51
|
+
const rest = leadingDot === "" ? name : name.slice(1);
|
|
52
|
+
const dot = rest.lastIndexOf(".");
|
|
53
|
+
return (leadingDot + (dot === -1 ? rest : rest.slice(0, dot))).slice(0, length);
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
export { InspectorEntityTab, entityTabGlyph };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/inspector/entity-tab.logic.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Pure activation/close-gesture policy for `InspectorEntityTab`, kept out
|
|
4
|
+
* of the component so it's unit-testable without a DOM: each function
|
|
5
|
+
* takes only the plain values a `MouseEvent` carries (or the host's
|
|
6
|
+
* optional callbacks), never the event object itself.
|
|
7
|
+
*/
|
|
8
|
+
/** Whether a mouse button number is the tab's "close" gesture
|
|
9
|
+
* (middle-click). */
|
|
10
|
+
declare const isEntityTabCloseGesture: (button: number) => boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Resolves what an `onAuxClick` with this `button` should do: close the
|
|
13
|
+
* tab (the given `onClose`) when the gesture matches and the host
|
|
14
|
+
* supplied one, or nothing (`undefined`) otherwise. Returning `undefined`
|
|
15
|
+
* for "do nothing" (rather than a boolean) lets the component skip
|
|
16
|
+
* `preventDefault()` too when there's truly nothing to do, leaving the
|
|
17
|
+
* browser's own middle-click behavior alone.
|
|
18
|
+
*/
|
|
19
|
+
declare const resolveEntityTabCloseHandler: (button: number, onClose: (() => void) | undefined) => (() => void) | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Resolves the tab's click/activation handler: `onSelect` when the host
|
|
22
|
+
* supplied one, a stable no-op otherwise, so the component always has a
|
|
23
|
+
* callable handler to hand to `containedEventHandler` regardless of
|
|
24
|
+
* whether the host cares about selection.
|
|
25
|
+
*/
|
|
26
|
+
declare const resolveEntityTabActivateHandler: (onSelect: (() => void) | undefined) => (() => void);
|
|
27
|
+
//#endregion
|
|
28
|
+
export { isEntityTabCloseGesture, resolveEntityTabActivateHandler, resolveEntityTabCloseHandler };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/inspector/entity-tab.logic.ts
|
|
2
|
+
/**
|
|
3
|
+
* Pure activation/close-gesture policy for `InspectorEntityTab`, kept out
|
|
4
|
+
* of the component so it's unit-testable without a DOM: each function
|
|
5
|
+
* takes only the plain values a `MouseEvent` carries (or the host's
|
|
6
|
+
* optional callbacks), never the event object itself.
|
|
7
|
+
*/
|
|
8
|
+
/** The `MouseEvent.button` value for a middle-click (the wheel / auxiliary
|
|
9
|
+
* button) — the browser convention `onAuxClick` reports it under. */
|
|
10
|
+
const MIDDLE_CLICK_BUTTON = 1;
|
|
11
|
+
/** Whether a mouse button number is the tab's "close" gesture
|
|
12
|
+
* (middle-click). */
|
|
13
|
+
const isEntityTabCloseGesture = (button) => button === MIDDLE_CLICK_BUTTON;
|
|
14
|
+
/**
|
|
15
|
+
* Resolves what an `onAuxClick` with this `button` should do: close the
|
|
16
|
+
* tab (the given `onClose`) when the gesture matches and the host
|
|
17
|
+
* supplied one, or nothing (`undefined`) otherwise. Returning `undefined`
|
|
18
|
+
* for "do nothing" (rather than a boolean) lets the component skip
|
|
19
|
+
* `preventDefault()` too when there's truly nothing to do, leaving the
|
|
20
|
+
* browser's own middle-click behavior alone.
|
|
21
|
+
*/
|
|
22
|
+
const resolveEntityTabCloseHandler = (button, onClose) => isEntityTabCloseGesture(button) ? onClose : void 0;
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the tab's click/activation handler: `onSelect` when the host
|
|
25
|
+
* supplied one, a stable no-op otherwise, so the component always has a
|
|
26
|
+
* callable handler to hand to `containedEventHandler` regardless of
|
|
27
|
+
* whether the host cares about selection.
|
|
28
|
+
*/
|
|
29
|
+
const resolveEntityTabActivateHandler = (onSelect) => onSelect ?? entityTabNoop;
|
|
30
|
+
const entityTabNoop = () => void 0;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { isEntityTabCloseGesture, resolveEntityTabActivateHandler, resolveEntityTabCloseHandler };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/inspector/facet-bar.d.ts
|
|
2
|
+
type InspectorFacetBarProps<F extends string> = {
|
|
3
|
+
facet: F;
|
|
4
|
+
facets: readonly F[];
|
|
5
|
+
/** Display label per facet. */
|
|
6
|
+
labels: Record<F, string>;
|
|
7
|
+
/**
|
|
8
|
+
* Facets rendered but not interactive — visible so users can find
|
|
9
|
+
* them, but clicking does nothing (e.g. an AI-suggestions chip
|
|
10
|
+
* before any proposals exist).
|
|
11
|
+
*/
|
|
12
|
+
disabledFacets?: ReadonlySet<F> | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Bumping this replays a one-shot attention pulse on the active
|
|
15
|
+
* chip (e.g. after a background action changes it). Undefined or
|
|
16
|
+
* unchanged: no pulse.
|
|
17
|
+
*/
|
|
18
|
+
pulseSeq?: number | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Suffix appended to the active facet's label, e.g. `"v1"` →
|
|
21
|
+
* "Preview · v1". Hidden on inactive chips so the row stays
|
|
22
|
+
* scannable.
|
|
23
|
+
*/
|
|
24
|
+
activeBadge?: string | undefined;
|
|
25
|
+
/** Accessible name for the overflow chevron trigger. The host owns
|
|
26
|
+
* copy/translation; this only wires the affordance. */
|
|
27
|
+
overflowMenuLabel: string;
|
|
28
|
+
onChange: (next: F) => void;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Inspector subtab row: a single line of pill chips at toolbar-row
|
|
32
|
+
* height. Presentational and facet-agnostic — every inspector tab
|
|
33
|
+
* type (file-viewer facets, template-studio fields/clauses/history)
|
|
34
|
+
* drives it with its own facet union + labels so the row reads
|
|
35
|
+
* identically across the inspector.
|
|
36
|
+
*
|
|
37
|
+
* Labels are never truncated: when the row is too narrow for every
|
|
38
|
+
* chip, the ones that don't fit collapse into a trailing chevron
|
|
39
|
+
* (`˅`) dropdown. The active facet is pinned visible ahead of the rest
|
|
40
|
+
* so the current tab stays readable rather than hiding inside the menu
|
|
41
|
+
* — except at the narrowest widths, where not even the active chip fits
|
|
42
|
+
* beside the trigger; see `resolveFacetOverflow`'s narrow-width floor.
|
|
43
|
+
*/
|
|
44
|
+
declare const InspectorFacetBar: <F extends string>({ facet, facets, labels, disabledFacets, pulseSeq, activeBadge, overflowMenuLabel, onChange }: InspectorFacetBarProps<F>) => import("react").JSX.Element;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { InspectorFacetBar, InspectorFacetBarProps };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { TOOLBAR_ROW_HEIGHT } from "./layout-tokens.js";
|
|
3
|
+
import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "../components/tooltip.js";
|
|
4
|
+
import { Button } from "../components/button.js";
|
|
5
|
+
import { DropdownMenu as Menu, DropdownMenuContent as MenuPopup, DropdownMenuItem as MenuItem, DropdownMenuTrigger as MenuTrigger } from "../components/menu.js";
|
|
6
|
+
import { resolveFacetOverflow } from "./facet-bar.logic.js";
|
|
7
|
+
import { ChevronDownIcon } from "lucide-react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { useEffect, useRef, useState } from "react";
|
|
10
|
+
//#region src/inspector/facet-bar.tsx
|
|
11
|
+
/**
|
|
12
|
+
* Inspector subtab row: a single line of pill chips at toolbar-row
|
|
13
|
+
* height. Presentational and facet-agnostic — every inspector tab
|
|
14
|
+
* type (file-viewer facets, template-studio fields/clauses/history)
|
|
15
|
+
* drives it with its own facet union + labels so the row reads
|
|
16
|
+
* identically across the inspector.
|
|
17
|
+
*
|
|
18
|
+
* Labels are never truncated: when the row is too narrow for every
|
|
19
|
+
* chip, the ones that don't fit collapse into a trailing chevron
|
|
20
|
+
* (`˅`) dropdown. The active facet is pinned visible ahead of the rest
|
|
21
|
+
* so the current tab stays readable rather than hiding inside the menu
|
|
22
|
+
* — except at the narrowest widths, where not even the active chip fits
|
|
23
|
+
* beside the trigger; see `resolveFacetOverflow`'s narrow-width floor.
|
|
24
|
+
*/
|
|
25
|
+
const InspectorFacetBar = ({ facet, facets, labels, disabledFacets, pulseSeq, activeBadge, overflowMenuLabel, onChange }) => {
|
|
26
|
+
const lastPulseSeq = useRef(pulseSeq);
|
|
27
|
+
const containerRef = useRef(null);
|
|
28
|
+
const measureRef = useRef(null);
|
|
29
|
+
const [visibleCount, setVisibleCount] = useState(facets.length);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (pulseSeq === void 0 || pulseSeq === lastPulseSeq.current) return;
|
|
32
|
+
lastPulseSeq.current = pulseSeq;
|
|
33
|
+
const activeChip = containerRef.current?.querySelector("[data-facet-chip-active]");
|
|
34
|
+
if (activeChip instanceof HTMLElement) flashFacetChip(activeChip);
|
|
35
|
+
}, [pulseSeq]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const container = containerRef.current;
|
|
38
|
+
const measure = measureRef.current;
|
|
39
|
+
if (!container || !measure) return;
|
|
40
|
+
const recompute = () => {
|
|
41
|
+
const style = getComputedStyle(container);
|
|
42
|
+
const padX = (Number.parseFloat(style.paddingInlineStart) || 0) + (Number.parseFloat(style.paddingInlineEnd) || 0);
|
|
43
|
+
const gap = Number.parseFloat(style.columnGap) || 0;
|
|
44
|
+
const availableWidth = container.clientWidth - padX;
|
|
45
|
+
const cells = [...measure.children];
|
|
46
|
+
const triggerWidth = cells.at(-1)?.getBoundingClientRect().width ?? 0;
|
|
47
|
+
const chipWidths = cells.slice(0, facets.length).map((cell) => cell.getBoundingClientRect().width);
|
|
48
|
+
const policy = resolveFacetOverflow({
|
|
49
|
+
activeIndex: facets.indexOf(facet),
|
|
50
|
+
availableWidth,
|
|
51
|
+
chipWidths,
|
|
52
|
+
gap,
|
|
53
|
+
triggerWidth
|
|
54
|
+
});
|
|
55
|
+
setVisibleCount(policy.visibleCount);
|
|
56
|
+
};
|
|
57
|
+
const observer = new ResizeObserver(recompute);
|
|
58
|
+
observer.observe(container);
|
|
59
|
+
observer.observe(measure);
|
|
60
|
+
recompute();
|
|
61
|
+
return () => observer.disconnect();
|
|
62
|
+
}, [facets, facet]);
|
|
63
|
+
const overflowing = visibleCount < facets.length;
|
|
64
|
+
const activeIndex = facets.indexOf(facet);
|
|
65
|
+
let visibleFacets;
|
|
66
|
+
let overflowFacets;
|
|
67
|
+
if (!overflowing) {
|
|
68
|
+
visibleFacets = [...facets];
|
|
69
|
+
overflowFacets = [];
|
|
70
|
+
} else if (visibleCount === 0) {
|
|
71
|
+
visibleFacets = [];
|
|
72
|
+
overflowFacets = [...facets];
|
|
73
|
+
} else if (activeIndex < visibleCount) {
|
|
74
|
+
visibleFacets = facets.slice(0, visibleCount);
|
|
75
|
+
overflowFacets = facets.slice(visibleCount);
|
|
76
|
+
} else {
|
|
77
|
+
visibleFacets = [...facets.slice(0, visibleCount - 1), facet];
|
|
78
|
+
overflowFacets = facets.filter((value) => !visibleFacets.includes(value));
|
|
79
|
+
}
|
|
80
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
81
|
+
className: cn("bg-background/85 supports-[backdrop-filter]:bg-background/65 sticky top-0 z-10 flex shrink-0 items-center gap-0.5 overflow-hidden border-b px-1.5 backdrop-blur", TOOLBAR_ROW_HEIGHT),
|
|
82
|
+
ref: containerRef,
|
|
83
|
+
children: [
|
|
84
|
+
visibleFacets.map((value) => /* @__PURE__ */ jsx(FacetChip, {
|
|
85
|
+
activeBadge,
|
|
86
|
+
disabled: disabledFacets?.has(value) ?? false,
|
|
87
|
+
isActive: value === facet,
|
|
88
|
+
label: labels[value],
|
|
89
|
+
onSelect: () => onChange(value)
|
|
90
|
+
}, value)),
|
|
91
|
+
overflowing && /* @__PURE__ */ jsxs(Menu, { children: [/* @__PURE__ */ jsx(MenuTrigger, {
|
|
92
|
+
"aria-label": overflowMenuLabel,
|
|
93
|
+
className: "ms-auto",
|
|
94
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
95
|
+
size: "icon-sm",
|
|
96
|
+
type: "button",
|
|
97
|
+
variant: "outline"
|
|
98
|
+
}),
|
|
99
|
+
children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
|
|
100
|
+
}), /* @__PURE__ */ jsx(MenuPopup, {
|
|
101
|
+
align: "end",
|
|
102
|
+
side: "bottom",
|
|
103
|
+
children: overflowFacets.map((value) => /* @__PURE__ */ jsx(MenuItem, {
|
|
104
|
+
disabled: disabledFacets?.has(value) ?? false,
|
|
105
|
+
onClick: () => onChange(value),
|
|
106
|
+
children: labels[value]
|
|
107
|
+
}, value))
|
|
108
|
+
})] }),
|
|
109
|
+
/* @__PURE__ */ jsxs("div", {
|
|
110
|
+
"aria-hidden": "true",
|
|
111
|
+
className: "pointer-events-none invisible absolute flex w-max items-center gap-0.5",
|
|
112
|
+
ref: measureRef,
|
|
113
|
+
children: [facets.map((value) => /* @__PURE__ */ jsx("span", {
|
|
114
|
+
className: cn(CHIP_CLASS, INACTIVE_CHIP_CLASS),
|
|
115
|
+
children: labels[value]
|
|
116
|
+
}, value)), /* @__PURE__ */ jsx("span", { className: "size-8 shrink-0 rounded-lg border sm:size-7" })]
|
|
117
|
+
})
|
|
118
|
+
]
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
/** One-shot attention flash: a ring that appears with a couple of opacity
|
|
122
|
+
* dips over 1.4s, then fades — the same imperative Web Animations API
|
|
123
|
+
* technique the rail's `flashTabElement` uses, so a single call finishes on
|
|
124
|
+
* its own without any pulsing state to track or clean up. */
|
|
125
|
+
const flashFacetChip = (el) => {
|
|
126
|
+
const ring = "0 0 0 2px var(--color-foreground-disabled)";
|
|
127
|
+
el.animate([
|
|
128
|
+
{
|
|
129
|
+
boxShadow: ring,
|
|
130
|
+
opacity: 1
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
boxShadow: ring,
|
|
134
|
+
opacity: .5
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
boxShadow: ring,
|
|
138
|
+
opacity: 1
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
boxShadow: ring,
|
|
142
|
+
opacity: .5
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
boxShadow: "0 0 0 2px transparent",
|
|
146
|
+
opacity: 1
|
|
147
|
+
}
|
|
148
|
+
], {
|
|
149
|
+
duration: 1400,
|
|
150
|
+
easing: "ease-in-out"
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
const CHIP_CLASS = "shrink-0 rounded-md px-1.5 py-1 text-xs font-medium whitespace-nowrap transition-colors";
|
|
154
|
+
const INACTIVE_CHIP_CLASS = "text-muted-foreground hover:bg-muted hover:text-foreground";
|
|
155
|
+
const FacetChip = ({ label, isActive, disabled, activeBadge, onSelect }) => /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
156
|
+
render: /* @__PURE__ */ jsx("button", {
|
|
157
|
+
className: cn(CHIP_CLASS, isActive ? "bg-foreground text-background" : INACTIVE_CHIP_CLASS, disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"),
|
|
158
|
+
"data-facet-chip-active": isActive ? "" : void 0,
|
|
159
|
+
disabled,
|
|
160
|
+
onClick: onSelect,
|
|
161
|
+
type: "button"
|
|
162
|
+
}),
|
|
163
|
+
children: label
|
|
164
|
+
}), /* @__PURE__ */ jsx(TooltipPopup, { children: isActive && activeBadge !== void 0 ? `${label} · ${activeBadge}` : label })] });
|
|
165
|
+
//#endregion
|
|
166
|
+
export { InspectorFacetBar };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//#region src/inspector/facet-bar.logic.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Pure overflow policy for `InspectorFacetBar`: given the row's available
|
|
4
|
+
* width and each chip's natural (measured) width, decides how many chips
|
|
5
|
+
* stay visible and whether the overflow trigger renders — kept out of the
|
|
6
|
+
* component so it's unit-testable without a DOM or a `ResizeObserver`.
|
|
7
|
+
*/
|
|
8
|
+
type FacetOverflowInput = {
|
|
9
|
+
/** Width inside the row available to the chips (the trigger's own
|
|
10
|
+
* footprint is reserved out of this by the policy, not by the caller). */
|
|
11
|
+
availableWidth: number;
|
|
12
|
+
/** Each chip's natural width, in the same order as the facets they
|
|
13
|
+
* represent. */
|
|
14
|
+
chipWidths: readonly number[];
|
|
15
|
+
/** Index into `chipWidths` of the active chip — always kept visible
|
|
16
|
+
* whenever anything is. */
|
|
17
|
+
activeIndex: number;
|
|
18
|
+
/** The overflow trigger's own footprint, reserved alongside the chips
|
|
19
|
+
* whenever not every chip fits. */
|
|
20
|
+
triggerWidth: number;
|
|
21
|
+
/** Horizontal gap between adjacent chips, and between the last chip and
|
|
22
|
+
* the trigger. */
|
|
23
|
+
gap?: number;
|
|
24
|
+
};
|
|
25
|
+
type FacetOverflowPolicy = {
|
|
26
|
+
/** How many chips are visible. Counts a prefix of `chipWidths` in their
|
|
27
|
+
* given order when the active chip already falls inside that prefix;
|
|
28
|
+
* otherwise counts the active chip plus however many others (in order,
|
|
29
|
+
* skipping the active one) fit alongside it. `0` is the narrow-width
|
|
30
|
+
* floor: not even the active chip fits, so nothing but the trigger
|
|
31
|
+
* shows. */
|
|
32
|
+
visibleCount: number;
|
|
33
|
+
/** Whether the overflow trigger renders at all. */
|
|
34
|
+
showOverflowTrigger: boolean;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Decides the facet row's visible/overflow split.
|
|
38
|
+
*
|
|
39
|
+
* Narrow-width floor: if even the active chip cannot fit beside the
|
|
40
|
+
* trigger, showing it anyway would push the trigger past
|
|
41
|
+
* `availableWidth` — the row clips overflow, so the trigger (the only way
|
|
42
|
+
* to reach ANY facet from this row) would become unreachable. Rather than
|
|
43
|
+
* shrink the active chip below its natural width (this policy has no
|
|
44
|
+
* minimum-width input to shrink to), this drops the active chip from the
|
|
45
|
+
* visible row too and shows only the trigger: every facet, including the
|
|
46
|
+
* active one, still reaches through the overflow menu, and the trigger
|
|
47
|
+
* itself always stays on screen.
|
|
48
|
+
*/
|
|
49
|
+
declare const resolveFacetOverflow: ({ availableWidth, chipWidths, activeIndex, triggerWidth, gap }: FacetOverflowInput) => FacetOverflowPolicy;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { FacetOverflowInput, FacetOverflowPolicy, resolveFacetOverflow };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/inspector/facet-bar.logic.ts
|
|
2
|
+
const sumWithGaps = (widths, gap) => widths.reduce((sum, width, index) => sum + width + (index > 0 ? gap : 0), 0);
|
|
3
|
+
/**
|
|
4
|
+
* Decides the facet row's visible/overflow split.
|
|
5
|
+
*
|
|
6
|
+
* Narrow-width floor: if even the active chip cannot fit beside the
|
|
7
|
+
* trigger, showing it anyway would push the trigger past
|
|
8
|
+
* `availableWidth` — the row clips overflow, so the trigger (the only way
|
|
9
|
+
* to reach ANY facet from this row) would become unreachable. Rather than
|
|
10
|
+
* shrink the active chip below its natural width (this policy has no
|
|
11
|
+
* minimum-width input to shrink to), this drops the active chip from the
|
|
12
|
+
* visible row too and shows only the trigger: every facet, including the
|
|
13
|
+
* active one, still reaches through the overflow menu, and the trigger
|
|
14
|
+
* itself always stays on screen.
|
|
15
|
+
*/
|
|
16
|
+
const resolveFacetOverflow = ({ availableWidth, chipWidths, activeIndex, triggerWidth, gap = 0 }) => {
|
|
17
|
+
if (sumWithGaps(chipWidths, gap) <= availableWidth) return {
|
|
18
|
+
visibleCount: chipWidths.length,
|
|
19
|
+
showOverflowTrigger: false
|
|
20
|
+
};
|
|
21
|
+
let used = 0;
|
|
22
|
+
let count = 0;
|
|
23
|
+
for (const width of chipWidths) {
|
|
24
|
+
const add = width + (count > 0 ? gap : 0);
|
|
25
|
+
if (used + add + gap + triggerWidth > availableWidth) break;
|
|
26
|
+
used += add;
|
|
27
|
+
count += 1;
|
|
28
|
+
}
|
|
29
|
+
if (activeIndex < count) return {
|
|
30
|
+
visibleCount: count,
|
|
31
|
+
showOverflowTrigger: true
|
|
32
|
+
};
|
|
33
|
+
const activeWidth = chipWidths[activeIndex] ?? 0;
|
|
34
|
+
if (activeWidth + gap + triggerWidth > availableWidth) return {
|
|
35
|
+
visibleCount: 0,
|
|
36
|
+
showOverflowTrigger: true
|
|
37
|
+
};
|
|
38
|
+
used = activeWidth;
|
|
39
|
+
count = 0;
|
|
40
|
+
for (const [index, width] of chipWidths.entries()) {
|
|
41
|
+
if (index === activeIndex) continue;
|
|
42
|
+
const add = width + gap;
|
|
43
|
+
if (used + add + gap + triggerWidth > availableWidth) break;
|
|
44
|
+
used += add;
|
|
45
|
+
count += 1;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
visibleCount: count + 1,
|
|
49
|
+
showOverflowTrigger: true
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
export { resolveFacetOverflow };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailContent, InspectorRailFooter, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTitle } from "./chrome.js";
|
|
2
2
|
import { InspectorDock } from "./dock.js";
|
|
3
|
+
import { InspectorEntityTab, entityTabGlyph } from "./entity-tab.js";
|
|
4
|
+
import { InspectorFacetBar } from "./facet-bar.js";
|
|
3
5
|
import { InspectorTab, InspectorTabList, InspectorTabPanel, InspectorTabs } from "./tabs.js";
|
|
4
6
|
import { PROPERTY_ROW_GRID, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX } from "./layout-tokens.js";
|
|
5
7
|
import { INSPECTOR_CONTENT_MIN_WIDTH, INSPECTOR_EDITOR_MIN_WIDTH, INSPECTOR_PANE_DEFAULT_WIDTH, INSPECTOR_PANE_MAX_WIDTH, INSPECTOR_PANE_MIN_WIDTH, INSPECTOR_RAIL_WIDTH, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, shouldForceSidebarCollapsed } from "./pane-width.js";
|
|
6
8
|
import { INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, parsePersistedPaneWidth, resolveDragWidth, resolveKeyboardWidth, useInspectorPaneWidth } from "./use-pane-width.js";
|
|
7
|
-
export { INSPECTOR_CONTENT_MIN_WIDTH, INSPECTOR_EDITOR_MIN_WIDTH, INSPECTOR_PANE_DEFAULT_WIDTH, INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, INSPECTOR_PANE_MAX_WIDTH, INSPECTOR_PANE_MIN_WIDTH, INSPECTOR_RAIL_WIDTH, Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorDock, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailContent, InspectorRailFooter, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTab, InspectorTabList, InspectorTabPanel, InspectorTabs, InspectorTitle, PROPERTY_ROW_GRID, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX, parsePersistedPaneWidth, resolveDragWidth, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, resolveKeyboardWidth, shouldForceSidebarCollapsed, useInspectorPaneWidth };
|
|
9
|
+
export { INSPECTOR_CONTENT_MIN_WIDTH, INSPECTOR_EDITOR_MIN_WIDTH, INSPECTOR_PANE_DEFAULT_WIDTH, INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, INSPECTOR_PANE_MAX_WIDTH, INSPECTOR_PANE_MIN_WIDTH, INSPECTOR_RAIL_WIDTH, Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorDock, InspectorEmptyRow, InspectorEntityTab, InspectorFacetBar, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailContent, InspectorRailFooter, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTab, InspectorTabList, InspectorTabPanel, InspectorTabs, InspectorTitle, PROPERTY_ROW_GRID, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX, entityTabGlyph, parsePersistedPaneWidth, resolveDragWidth, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, resolveKeyboardWidth, shouldForceSidebarCollapsed, useInspectorPaneWidth };
|
package/dist/inspector/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import { PROPERTY_ROW_GRID, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZ
|
|
|
2
2
|
import { Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailContent, InspectorRailFooter, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTitle } from "./chrome.js";
|
|
3
3
|
import { INSPECTOR_CONTENT_MIN_WIDTH, INSPECTOR_EDITOR_MIN_WIDTH, INSPECTOR_PANE_DEFAULT_WIDTH, INSPECTOR_PANE_MAX_WIDTH, INSPECTOR_PANE_MIN_WIDTH, INSPECTOR_RAIL_WIDTH, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, shouldForceSidebarCollapsed } from "./pane-width.js";
|
|
4
4
|
import { InspectorDock } from "./dock.js";
|
|
5
|
+
import { InspectorEntityTab, entityTabGlyph } from "./entity-tab.js";
|
|
6
|
+
import { InspectorFacetBar } from "./facet-bar.js";
|
|
5
7
|
import { InspectorTab, InspectorTabList, InspectorTabPanel, InspectorTabs } from "./tabs.js";
|
|
6
8
|
import { INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, parsePersistedPaneWidth, resolveDragWidth, resolveKeyboardWidth, useInspectorPaneWidth } from "./use-pane-width.js";
|
|
7
|
-
export { INSPECTOR_CONTENT_MIN_WIDTH, INSPECTOR_EDITOR_MIN_WIDTH, INSPECTOR_PANE_DEFAULT_WIDTH, INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, INSPECTOR_PANE_MAX_WIDTH, INSPECTOR_PANE_MIN_WIDTH, INSPECTOR_RAIL_WIDTH, Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorDock, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailContent, InspectorRailFooter, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTab, InspectorTabList, InspectorTabPanel, InspectorTabs, InspectorTitle, PROPERTY_ROW_GRID, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX, parsePersistedPaneWidth, resolveDragWidth, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, resolveKeyboardWidth, shouldForceSidebarCollapsed, useInspectorPaneWidth };
|
|
9
|
+
export { INSPECTOR_CONTENT_MIN_WIDTH, INSPECTOR_EDITOR_MIN_WIDTH, INSPECTOR_PANE_DEFAULT_WIDTH, INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, INSPECTOR_PANE_MAX_WIDTH, INSPECTOR_PANE_MIN_WIDTH, INSPECTOR_RAIL_WIDTH, Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorDock, InspectorEmptyRow, InspectorEntityTab, InspectorFacetBar, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailContent, InspectorRailFooter, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTab, InspectorTabList, InspectorTabPanel, InspectorTabs, InspectorTitle, PROPERTY_ROW_GRID, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX, entityTabGlyph, parsePersistedPaneWidth, resolveDragWidth, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, resolveKeyboardWidth, shouldForceSidebarCollapsed, useInspectorPaneWidth };
|
package/dist/inspector/tabs.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { Tabs } from "@base-ui/react/tabs";
|
|
|
8
8
|
*/
|
|
9
9
|
declare const InspectorTabs: ({ className, ...props }: Omit<Tabs.Root.Props, "orientation">) => React$1.JSX.Element;
|
|
10
10
|
declare const INSPECTOR_RAIL_MEDIA_QUERY: "(min-width: 48rem)";
|
|
11
|
-
declare const resolveInspectorTabOrientation: (isRailLayout: boolean) => "
|
|
11
|
+
declare const resolveInspectorTabOrientation: (isRailLayout: boolean) => "vertical" | "horizontal";
|
|
12
12
|
declare const InspectorTabList: ({ className, ...props }: Tabs.List.Props) => React$1.JSX.Element;
|
|
13
13
|
declare const InspectorTab: ({ className, ...props }: Tabs.Tab.Props) => React$1.JSX.Element;
|
|
14
14
|
declare const InspectorTabPanel: ({ className, ...props }: Tabs.Panel.Props) => React$1.JSX.Element;
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
//#region src/kanban/band-peek.d.ts
|
|
2
2
|
/**
|
|
3
|
-
* The peek a collapsed band opens while a
|
|
4
|
-
* as a state machine with an injectable scheduler so its timing rules
|
|
5
|
-
* tested without a DOM.
|
|
3
|
+
* The peek a collapsed band opens while a dragged card rests on its folded
|
|
4
|
+
* slot, as a state machine with an injectable scheduler so its timing rules
|
|
5
|
+
* can be tested without a DOM. A plain hover never peeks: the peek exists so
|
|
6
|
+
* a drag can still land on a specific column inside a folded band, and the
|
|
7
|
+
* board only feeds the controller drag events.
|
|
6
8
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* band for a short linger. The band renders as separate elements (its
|
|
15
|
-
* caption, then its columns in each lane), so moving from the caption
|
|
16
|
-
* down into a column crosses an element boundary; without the linger the
|
|
17
|
-
* band would fold under the pointer and the slot would peek it straight
|
|
18
|
-
* back open.
|
|
9
|
+
* One rule keeps a peek from fighting the drag: it ends only after the drag
|
|
10
|
+
* has left every part of the open band for a short linger. The band renders
|
|
11
|
+
* as separate elements (its caption, then its columns in each lane), so
|
|
12
|
+
* moving from the caption down into a column crosses an element boundary;
|
|
13
|
+
* without the linger the band would fold under the card and the slot would
|
|
14
|
+
* peek it straight back open. The end of the drag, wherever it lands, ends
|
|
15
|
+
* the peek at once.
|
|
19
16
|
*/
|
|
20
|
-
/** How long a
|
|
17
|
+
/** How long a dragged card rests on a folded slot before the band peeks open. */
|
|
21
18
|
declare const KANBAN_BAND_PEEK_DELAY_MS = 400;
|
|
22
|
-
/** How long the
|
|
19
|
+
/** How long the drag may be outside an open band before the peek ends. */
|
|
23
20
|
declare const KANBAN_BAND_PEEK_LINGER_MS = 150;
|
|
24
21
|
/** Runs `callback` after `ms`; the returned function cancels it. */
|
|
25
22
|
type BandPeekScheduler = (callback: () => void, ms: number) => () => void;
|
|
@@ -32,21 +29,18 @@ type BandPeekControllerOptions = {
|
|
|
32
29
|
schedule?: BandPeekScheduler;
|
|
33
30
|
};
|
|
34
31
|
type BandPeekController = {
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
/** The
|
|
38
|
-
|
|
39
|
-
/** The
|
|
40
|
-
|
|
41
|
-
/** The
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
* under the pointer; it must not peek until the pointer leaves the slot.
|
|
46
|
-
*/
|
|
47
|
-
foldedUnderPointer: (bandId: string) => void;
|
|
32
|
+
/** A dragged card moved over a band's folded slot. */
|
|
33
|
+
slotDragOver: (bandId: string) => void;
|
|
34
|
+
/** The drag left a band's folded slot. */
|
|
35
|
+
slotDragLeave: (bandId: string) => void;
|
|
36
|
+
/** The drag entered a part of a band that is rendered open. */
|
|
37
|
+
openDragEnter: (bandId: string) => void;
|
|
38
|
+
/** The drag left a part of a band that is rendered open. */
|
|
39
|
+
openDragLeave: (bandId: string) => void;
|
|
40
|
+
/** The drag ended (dropped or cancelled), wherever it was. */
|
|
41
|
+
dragEnded: () => void;
|
|
48
42
|
/**
|
|
49
|
-
* The band was folded
|
|
43
|
+
* The band was folded (from its caption, the keyboard, or a controlled
|
|
50
44
|
* caller): any peek ends, and the slot may peek on the next hover.
|
|
51
45
|
*/
|
|
52
46
|
bandFolded: (bandId: string) => void;
|
|
@@ -54,8 +48,7 @@ type BandPeekController = {
|
|
|
54
48
|
bandExpanded: (bandId: string) => void;
|
|
55
49
|
/**
|
|
56
50
|
* A band's folded slot left the DOM (the band expanded or disappeared) so
|
|
57
|
-
* a peek it was about to open
|
|
58
|
-
* applies.
|
|
51
|
+
* a peek it was about to open no longer applies.
|
|
59
52
|
*/
|
|
60
53
|
slotUnmounted: (bandId: string) => void;
|
|
61
54
|
/** Ends any pending timer, for unmount. */
|