@stll/ui 0.0.1-placeholder.0 → 0.3.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/README.md +84 -0
- package/dist/components/accordion.d.ts +8 -0
- package/dist/components/accordion.js +38 -0
- package/dist/components/alert-dialog.d.ts +32 -0
- package/dist/components/alert-dialog.js +75 -0
- package/dist/components/avatar.d.ts +7 -0
- package/dist/components/avatar.js +22 -0
- package/dist/components/bidi-text.d.ts +24 -0
- package/dist/components/bidi-text.js +38 -0
- package/dist/components/brand-icons.d.ts +7 -0
- package/dist/components/brand-icons.js +18 -0
- package/dist/components/breadcrumb.d.ts +12 -0
- package/dist/components/breadcrumb.js +61 -0
- package/dist/components/button-variants.d.ts +17 -0
- package/dist/components/button-variants.js +43 -0
- package/dist/components/button.d.ts +20 -0
- package/dist/components/button.js +53 -0
- package/dist/components/checkbox.d.ts +5 -0
- package/dist/components/checkbox.js +44 -0
- package/dist/components/color-picker.d.ts +47 -0
- package/dist/components/color-picker.js +249 -0
- package/dist/components/combobox.d.ts +40 -0
- package/dist/components/combobox.js +208 -0
- package/dist/components/command.d.ts +34 -0
- package/dist/components/command.js +100 -0
- package/dist/components/date-picker-popover.d.ts +29 -0
- package/dist/components/date-picker-popover.js +548 -0
- package/dist/components/date-picker-popover.logic.d.ts +14 -0
- package/dist/components/date-picker-popover.logic.js +26 -0
- package/dist/components/destructive-action-confirmation.d.ts +21 -0
- package/dist/components/destructive-action-confirmation.js +60 -0
- package/dist/components/destructive-confirm-dialog.d.ts +17 -0
- package/dist/components/destructive-confirm-dialog.js +49 -0
- package/dist/components/dialog.d.ts +37 -0
- package/dist/components/dialog.js +92 -0
- package/dist/components/directional-icon.d.ts +30 -0
- package/dist/components/directional-icon.js +12 -0
- package/dist/components/field.d.ts +11 -0
- package/dist/components/field.js +34 -0
- package/dist/components/form.d.ts +5 -0
- package/dist/components/form.js +12 -0
- package/dist/components/frame.d.ts +10 -0
- package/dist/components/frame.js +35 -0
- package/dist/components/hex-color-picker.d.ts +11 -0
- package/dist/components/hex-color-picker.js +366 -0
- package/dist/components/input-group.d.ts +15 -0
- package/dist/components/input-group.js +53 -0
- package/dist/components/input-otp.d.ts +13 -0
- package/dist/components/input-otp.js +39 -0
- package/dist/components/input.d.ts +12 -0
- package/dist/components/input.js +46 -0
- package/dist/components/label.d.ts +5 -0
- package/dist/components/label.js +18 -0
- package/dist/components/menu.d.ts +44 -0
- package/dist/components/menu.js +152 -0
- package/dist/components/outline-rail.d.ts +43 -0
- package/dist/components/outline-rail.js +416 -0
- package/dist/components/pagination.d.ts +17 -0
- package/dist/components/pagination.js +78 -0
- package/dist/components/popover.d.ts +34 -0
- package/dist/components/popover.js +67 -0
- package/dist/components/preview-card.d.ts +12 -0
- package/dist/components/preview-card.js +25 -0
- package/dist/components/preview-pane.d.ts +23 -0
- package/dist/components/preview-pane.js +33 -0
- package/dist/components/scroll-area.d.ts +19 -0
- package/dist/components/scroll-area.js +38 -0
- package/dist/components/scroll-to-top.d.ts +12 -0
- package/dist/components/scroll-to-top.js +43 -0
- package/dist/components/secret-input.d.ts +9 -0
- package/dist/components/secret-input.js +38 -0
- package/dist/components/segmented-icon-toggle.d.ts +22 -0
- package/dist/components/segmented-icon-toggle.js +30 -0
- package/dist/components/select.d.ts +25 -0
- package/dist/components/select.js +196 -0
- package/dist/components/separator.d.ts +16 -0
- package/dist/components/separator.js +26 -0
- package/dist/components/sheet.d.ts +29 -0
- package/dist/components/sheet.js +87 -0
- package/dist/components/skeleton.d.ts +4 -0
- package/dist/components/skeleton.js +10 -0
- package/dist/components/stella-mark.d.ts +5 -0
- package/dist/components/stella-mark.js +12 -0
- package/dist/components/stella-wordmark.d.ts +5 -0
- package/dist/components/stella-wordmark.js +21 -0
- package/dist/components/table.d.ts +20 -0
- package/dist/components/table.js +80 -0
- package/dist/components/tabs.d.ts +11 -0
- package/dist/components/tabs.js +46 -0
- package/dist/components/textarea.d.ts +9 -0
- package/dist/components/textarea.js +31 -0
- package/dist/components/toast.d.ts +60 -0
- package/dist/components/toast.js +261 -0
- package/dist/components/tooltip-trigger-helper.d.ts +11 -0
- package/dist/components/tooltip-trigger-helper.js +14 -0
- package/dist/components/tooltip.d.ts +15 -0
- package/dist/components/tooltip.js +32 -0
- package/dist/data-table/index.d.ts +2 -0
- package/dist/data-table/index.js +2 -0
- package/dist/data-table/schema.d.ts +70 -0
- package/dist/data-table/schema.js +40 -0
- package/dist/hooks/use-contained-handler.d.ts +55 -0
- package/dist/hooks/use-contained-handler.js +54 -0
- package/dist/hooks/use-content-dir.d.ts +48 -0
- package/dist/hooks/use-content-dir.js +45 -0
- package/dist/hooks/use-mobile.d.ts +4 -0
- package/dist/hooks/use-mobile.js +16 -0
- package/dist/hooks/use-viewport-width.d.ts +5 -0
- package/dist/hooks/use-viewport-width.js +14 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +65 -0
- package/dist/inspector/chrome.d.ts +72 -0
- package/dist/inspector/chrome.js +153 -0
- package/dist/inspector/dock.d.ts +48 -0
- package/dist/inspector/dock.js +36 -0
- package/dist/inspector/index.d.ts +6 -0
- package/dist/inspector/index.js +6 -0
- package/dist/inspector/layout-tokens.d.ts +28 -0
- package/dist/inspector/layout-tokens.js +28 -0
- package/dist/inspector/pane-width.d.ts +64 -0
- package/dist/inspector/pane-width.js +53 -0
- package/dist/inspector/use-pane-width.d.ts +99 -0
- package/dist/inspector/use-pane-width.js +149 -0
- package/dist/kanban/card-properties.d.ts +27 -0
- package/dist/kanban/card-properties.js +19 -0
- package/dist/kanban/card-shell.d.ts +32 -0
- package/dist/kanban/card-shell.js +46 -0
- package/dist/kanban/column-header.d.ts +23 -0
- package/dist/kanban/column-header.js +24 -0
- package/dist/kanban/grouping.d.ts +84 -0
- package/dist/kanban/grouping.js +76 -0
- package/dist/kanban/index.d.ts +5 -0
- package/dist/kanban/index.js +5 -0
- package/dist/lib/button-disposition.d.ts +63 -0
- package/dist/lib/button-disposition.js +52 -0
- package/dist/lib/option-color.d.ts +28 -0
- package/dist/lib/option-color.js +59 -0
- package/dist/lib/overlay-layer.d.ts +22 -0
- package/dist/lib/overlay-layer.js +21 -0
- package/dist/lib/tab-order.d.ts +15 -0
- package/dist/lib/tab-order.js +15 -0
- package/dist/lib/tooltip-content.d.ts +13 -0
- package/dist/lib/tooltip-content.js +12 -0
- package/dist/lib/utils.d.ts +20 -0
- package/dist/lib/utils.js +41 -0
- package/dist/lib/week.d.ts +25 -0
- package/dist/lib/week.js +40 -0
- package/dist/styles/theme.css +1209 -0
- package/package.json +518 -2
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/inspector/dock.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Docked inspector pane: an in-flow spacer plus a fixed overlay.
|
|
5
|
+
*
|
|
6
|
+
* The spacer is what makes the content column reflow: the pane itself is
|
|
7
|
+
* `position: fixed`, so without a same-width sibling in the flow it would
|
|
8
|
+
* cover the content instead of displacing it. Keeping the pane fixed
|
|
9
|
+
* (rather than in the flow) is what lets it span the full viewport height
|
|
10
|
+
* regardless of the topbar above the content.
|
|
11
|
+
*
|
|
12
|
+
* The root carries the reserved inline size itself rather than leaving it to
|
|
13
|
+
* the spacer alone: a block-level root in a plain block host would otherwise
|
|
14
|
+
* take the full containing width and push the content column off-screen,
|
|
15
|
+
* which is exactly the failure the spacer exists to prevent. With the width
|
|
16
|
+
* on the root, the dock reserves the same footprint in a flex row, a grid
|
|
17
|
+
* track, or a plain block.
|
|
18
|
+
*/
|
|
19
|
+
type InspectorResizeHandleProps = {
|
|
20
|
+
"aria-orientation": "vertical";
|
|
21
|
+
"aria-valuemax": number;
|
|
22
|
+
"aria-valuemin": number;
|
|
23
|
+
"aria-valuenow": number;
|
|
24
|
+
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
|
25
|
+
onLostPointerCapture: (event: React.PointerEvent<HTMLElement>) => void;
|
|
26
|
+
onPointerCancel: (event: React.PointerEvent<HTMLElement>) => void;
|
|
27
|
+
onPointerDown: (event: React.PointerEvent<HTMLElement>) => void;
|
|
28
|
+
onPointerMove: (event: React.PointerEvent<HTMLElement>) => void;
|
|
29
|
+
onPointerUp: (event: React.PointerEvent<HTMLElement>) => void;
|
|
30
|
+
tabIndex: number;
|
|
31
|
+
};
|
|
32
|
+
type InspectorDockProps = {
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
className?: string | undefined;
|
|
35
|
+
/** Accessible name for the drag handle. */
|
|
36
|
+
resizeHandleLabel: string;
|
|
37
|
+
/** Handlers and ARIA state from `useInspectorPaneWidth`. */
|
|
38
|
+
resizeHandleProps: InspectorResizeHandleProps;
|
|
39
|
+
/** Double-click affordance restoring the default width. */
|
|
40
|
+
onResetWidth?: (() => void) | undefined;
|
|
41
|
+
/** Whether the full pane is shown, as opposed to the bare rail. */
|
|
42
|
+
showPaneContent: boolean;
|
|
43
|
+
/** Inline size the dock reserves, in CSS pixels. */
|
|
44
|
+
width: number;
|
|
45
|
+
};
|
|
46
|
+
declare const InspectorDock: ({ children, className, onResetWidth, resizeHandleLabel, resizeHandleProps, showPaneContent, width }: InspectorDockProps) => import("react").JSX.Element;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { InspectorDock };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/inspector/dock.tsx
|
|
4
|
+
const InspectorDock = ({ children, className, onResetWidth, resizeHandleLabel, resizeHandleProps, showPaneContent, width }) => {
|
|
5
|
+
const widthPx = `${width}px`;
|
|
6
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
7
|
+
className: cn("text-sidebar-foreground hidden shrink-0 md:block", className),
|
|
8
|
+
"data-side": "inline-end",
|
|
9
|
+
"data-slot": "inspector-dock",
|
|
10
|
+
"data-state": showPaneContent ? "expanded" : "collapsed",
|
|
11
|
+
style: { width: widthPx },
|
|
12
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
13
|
+
"aria-hidden": "true",
|
|
14
|
+
className: "bg-sidebar relative w-full",
|
|
15
|
+
"data-slot": "inspector-dock-spacer"
|
|
16
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
17
|
+
className: "fixed inset-y-0 end-0 z-10 hidden h-svh md:flex",
|
|
18
|
+
"data-slot": "inspector-dock-pane",
|
|
19
|
+
style: { width: widthPx },
|
|
20
|
+
children: [showPaneContent && /* @__PURE__ */ jsx("div", {
|
|
21
|
+
"aria-label": resizeHandleLabel,
|
|
22
|
+
className: "hover:bg-border active:bg-border focus-visible:bg-primary focus-visible:outline-primary absolute inset-y-0 -start-px z-20 flex w-1 cursor-col-resize items-center justify-center border-s focus-visible:outline-2",
|
|
23
|
+
"data-slot": "inspector-resize-handle",
|
|
24
|
+
role: "separator",
|
|
25
|
+
...resizeHandleProps,
|
|
26
|
+
tabIndex: 0,
|
|
27
|
+
onDoubleClick: onResetWidth
|
|
28
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
29
|
+
className: "bg-sidebar flex h-full w-full flex-col",
|
|
30
|
+
children
|
|
31
|
+
})]
|
|
32
|
+
})]
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { InspectorDock };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTitle } from "./chrome.js";
|
|
2
|
+
import { InspectorDock } from "./dock.js";
|
|
3
|
+
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";
|
|
4
|
+
import { INSPECTOR_CONTENT_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";
|
|
5
|
+
import { INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, parsePersistedPaneWidth, resolveDragWidth, resolveKeyboardWidth, useInspectorPaneWidth } from "./use-pane-width.js";
|
|
6
|
+
export { INSPECTOR_CONTENT_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, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, 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 };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
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";
|
|
2
|
+
import { Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTitle } from "./chrome.js";
|
|
3
|
+
import { InspectorDock } from "./dock.js";
|
|
4
|
+
import { INSPECTOR_CONTENT_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";
|
|
5
|
+
import { INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, parsePersistedPaneWidth, resolveDragWidth, resolveKeyboardWidth, useInspectorPaneWidth } from "./use-pane-width.js";
|
|
6
|
+
export { INSPECTOR_CONTENT_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, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, 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 };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/inspector/layout-tokens.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Layout constants the inspector chrome is built from.
|
|
4
|
+
*
|
|
5
|
+
* The rail's cells, the header strip, and the property rows all share one
|
|
6
|
+
* height, so a pane assembled from these parts keeps a single vertical
|
|
7
|
+
* rhythm no matter which of them a section happens to use.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Every inspector row, header strip, and rail cell is exactly this tall.
|
|
11
|
+
* A *fixed* height, not a minimum: rows that grow with their content break
|
|
12
|
+
* the vertical rhythm the rail's 48px cells establish, and a list of
|
|
13
|
+
* key/value rows stops scanning as a list once the rows differ in height.
|
|
14
|
+
*/
|
|
15
|
+
declare const TOOLBAR_ROW_HEIGHT: "h-12";
|
|
16
|
+
declare const TOOLBAR_ROW_HEIGHT_PX: 48;
|
|
17
|
+
declare const SIDE_RAIL_WIDTH: "w-12";
|
|
18
|
+
/**
|
|
19
|
+
* Borders sit on both inline edges of the rail box so the total footprint
|
|
20
|
+
* stays exactly 48px under `border-box`.
|
|
21
|
+
*/
|
|
22
|
+
declare const SIDE_RAIL_CONTAINER_CLASS: "bg-sidebar flex shrink-0 flex-col border-s border-e w-12";
|
|
23
|
+
declare const SIDE_RAIL_ICON_BUTTON_SIZE: "size-8";
|
|
24
|
+
declare const SIDE_RAIL_TAB_ICON_SIZE: "size-3.5";
|
|
25
|
+
/** Two-column grid the key/value rows share. */
|
|
26
|
+
declare const PROPERTY_ROW_GRID: "grid-cols-[8rem_minmax(0,1fr)]";
|
|
27
|
+
//#endregion
|
|
28
|
+
export { 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 };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/inspector/layout-tokens.ts
|
|
2
|
+
/**
|
|
3
|
+
* Layout constants the inspector chrome is built from.
|
|
4
|
+
*
|
|
5
|
+
* The rail's cells, the header strip, and the property rows all share one
|
|
6
|
+
* height, so a pane assembled from these parts keeps a single vertical
|
|
7
|
+
* rhythm no matter which of them a section happens to use.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Every inspector row, header strip, and rail cell is exactly this tall.
|
|
11
|
+
* A *fixed* height, not a minimum: rows that grow with their content break
|
|
12
|
+
* the vertical rhythm the rail's 48px cells establish, and a list of
|
|
13
|
+
* key/value rows stops scanning as a list once the rows differ in height.
|
|
14
|
+
*/
|
|
15
|
+
const TOOLBAR_ROW_HEIGHT = "h-12";
|
|
16
|
+
const TOOLBAR_ROW_HEIGHT_PX = 48;
|
|
17
|
+
const SIDE_RAIL_WIDTH = "w-12";
|
|
18
|
+
/**
|
|
19
|
+
* Borders sit on both inline edges of the rail box so the total footprint
|
|
20
|
+
* stays exactly 48px under `border-box`.
|
|
21
|
+
*/
|
|
22
|
+
const SIDE_RAIL_CONTAINER_CLASS = `bg-sidebar flex shrink-0 flex-col border-s border-e ${SIDE_RAIL_WIDTH}`;
|
|
23
|
+
const SIDE_RAIL_ICON_BUTTON_SIZE = "size-8";
|
|
24
|
+
const SIDE_RAIL_TAB_ICON_SIZE = "size-3.5";
|
|
25
|
+
/** Two-column grid the key/value rows share. */
|
|
26
|
+
const PROPERTY_ROW_GRID = "grid-cols-[8rem_minmax(0,1fr)]";
|
|
27
|
+
//#endregion
|
|
28
|
+
export { 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 };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//#region src/inspector/pane-width.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Width arithmetic for a docked inspector pane.
|
|
4
|
+
*
|
|
5
|
+
* The pane is a fixed overlay backed by an in-flow spacer, so nothing in
|
|
6
|
+
* the layout pushes back when it grows: whatever the pane takes comes
|
|
7
|
+
* straight out of the content column. Without a clamp against the space
|
|
8
|
+
* actually available, shrinking the window (or expanding the sidebar)
|
|
9
|
+
* leaves the content column a few dozen pixels wide instead of folding.
|
|
10
|
+
*
|
|
11
|
+
* The host application's sidebar width is a parameter rather than an
|
|
12
|
+
* import, so the policy carries no coupling to any particular shell.
|
|
13
|
+
*/
|
|
14
|
+
declare const INSPECTOR_PANE_DEFAULT_WIDTH = 512;
|
|
15
|
+
declare const INSPECTOR_PANE_MIN_WIDTH = 320;
|
|
16
|
+
declare const INSPECTOR_PANE_MAX_WIDTH = 800;
|
|
17
|
+
/**
|
|
18
|
+
* Floor for the content column beside the pane. Below this the content
|
|
19
|
+
* surface stops being usable, so the pane yields space first.
|
|
20
|
+
*/
|
|
21
|
+
declare const INSPECTOR_CONTENT_MIN_WIDTH = 400;
|
|
22
|
+
/** Inline size of the collapsed icon rail, in CSS pixels. */
|
|
23
|
+
declare const INSPECTOR_RAIL_WIDTH = 48;
|
|
24
|
+
type ForceSidebarCollapsedInput = {
|
|
25
|
+
/** Inline size the expanded sidebar takes out of the layout row. */
|
|
26
|
+
expandedSidebarWidth: number;
|
|
27
|
+
inspectorPaneOpen: boolean;
|
|
28
|
+
/** Viewport width in CSS pixels; 0 before the viewport is known. */
|
|
29
|
+
viewportWidth: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The expanded sidebar yields its optional width before either docked pane
|
|
33
|
+
* can violate its minimum. The collapsed rail plus both minimums fit at the
|
|
34
|
+
* desktop breakpoint, so this policy keeps every desktop width usable.
|
|
35
|
+
*/
|
|
36
|
+
declare const shouldForceSidebarCollapsed: ({ expandedSidebarWidth, inspectorPaneOpen, viewportWidth }: ForceSidebarCollapsedInput) => boolean;
|
|
37
|
+
type InspectorPaneWidthInput = {
|
|
38
|
+
/** Width the user dragged the pane to. */
|
|
39
|
+
desiredWidth: number;
|
|
40
|
+
/** Inline size the sidebar takes out of the same layout row. */
|
|
41
|
+
sidebarWidth: number;
|
|
42
|
+
/** Viewport width in CSS pixels; 0 before the viewport is known. */
|
|
43
|
+
viewportWidth: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Largest width the pane may take without starving the content column.
|
|
47
|
+
* Never returns less than {@link INSPECTOR_PANE_MIN_WIDTH}: a pane too
|
|
48
|
+
* narrow to read is no better than a content column too narrow to read,
|
|
49
|
+
* and collapsing the sidebar is the escape hatch for a genuinely small
|
|
50
|
+
* viewport.
|
|
51
|
+
*/
|
|
52
|
+
declare const resolveInspectorPaneMaxWidth: ({ sidebarWidth, viewportWidth }: Omit<InspectorPaneWidthInput, "desiredWidth">) => number;
|
|
53
|
+
/** Width the pane renders at, given the width the user asked for. */
|
|
54
|
+
declare const resolveInspectorPaneWidth: ({ desiredWidth, sidebarWidth, viewportWidth }: InspectorPaneWidthInput) => number;
|
|
55
|
+
/**
|
|
56
|
+
* Width the dock reserves in the layout: the full pane while content is
|
|
57
|
+
* shown, the bare rail while it is collapsed or minimized.
|
|
58
|
+
*/
|
|
59
|
+
declare const resolveInspectorDockWidth: ({ paneWidth, showPaneContent }: {
|
|
60
|
+
paneWidth: number;
|
|
61
|
+
showPaneContent: boolean;
|
|
62
|
+
}) => number;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { INSPECTOR_CONTENT_MIN_WIDTH, INSPECTOR_PANE_DEFAULT_WIDTH, INSPECTOR_PANE_MAX_WIDTH, INSPECTOR_PANE_MIN_WIDTH, INSPECTOR_RAIL_WIDTH, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, shouldForceSidebarCollapsed };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/inspector/pane-width.ts
|
|
2
|
+
/**
|
|
3
|
+
* Width arithmetic for a docked inspector pane.
|
|
4
|
+
*
|
|
5
|
+
* The pane is a fixed overlay backed by an in-flow spacer, so nothing in
|
|
6
|
+
* the layout pushes back when it grows: whatever the pane takes comes
|
|
7
|
+
* straight out of the content column. Without a clamp against the space
|
|
8
|
+
* actually available, shrinking the window (or expanding the sidebar)
|
|
9
|
+
* leaves the content column a few dozen pixels wide instead of folding.
|
|
10
|
+
*
|
|
11
|
+
* The host application's sidebar width is a parameter rather than an
|
|
12
|
+
* import, so the policy carries no coupling to any particular shell.
|
|
13
|
+
*/
|
|
14
|
+
const INSPECTOR_PANE_DEFAULT_WIDTH = 512;
|
|
15
|
+
const INSPECTOR_PANE_MIN_WIDTH = 320;
|
|
16
|
+
const INSPECTOR_PANE_MAX_WIDTH = 800;
|
|
17
|
+
/**
|
|
18
|
+
* Floor for the content column beside the pane. Below this the content
|
|
19
|
+
* surface stops being usable, so the pane yields space first.
|
|
20
|
+
*/
|
|
21
|
+
const INSPECTOR_CONTENT_MIN_WIDTH = 400;
|
|
22
|
+
/** Inline size of the collapsed icon rail, in CSS pixels. */
|
|
23
|
+
const INSPECTOR_RAIL_WIDTH = 48;
|
|
24
|
+
/**
|
|
25
|
+
* The expanded sidebar yields its optional width before either docked pane
|
|
26
|
+
* can violate its minimum. The collapsed rail plus both minimums fit at the
|
|
27
|
+
* desktop breakpoint, so this policy keeps every desktop width usable.
|
|
28
|
+
*/
|
|
29
|
+
const shouldForceSidebarCollapsed = ({ expandedSidebarWidth, inspectorPaneOpen, viewportWidth }) => inspectorPaneOpen && viewportWidth > 0 && viewportWidth < expandedSidebarWidth + 400 + 320;
|
|
30
|
+
/**
|
|
31
|
+
* Largest width the pane may take without starving the content column.
|
|
32
|
+
* Never returns less than {@link INSPECTOR_PANE_MIN_WIDTH}: a pane too
|
|
33
|
+
* narrow to read is no better than a content column too narrow to read,
|
|
34
|
+
* and collapsing the sidebar is the escape hatch for a genuinely small
|
|
35
|
+
* viewport.
|
|
36
|
+
*/
|
|
37
|
+
const resolveInspectorPaneMaxWidth = ({ sidebarWidth, viewportWidth }) => {
|
|
38
|
+
if (viewportWidth <= 0) return 320;
|
|
39
|
+
const available = viewportWidth - sidebarWidth - 400;
|
|
40
|
+
return Math.max(320, Math.min(800, available));
|
|
41
|
+
};
|
|
42
|
+
/** Width the pane renders at, given the width the user asked for. */
|
|
43
|
+
const resolveInspectorPaneWidth = ({ desiredWidth, sidebarWidth, viewportWidth }) => Math.min(Math.max(desiredWidth, 320), resolveInspectorPaneMaxWidth({
|
|
44
|
+
sidebarWidth,
|
|
45
|
+
viewportWidth
|
|
46
|
+
}));
|
|
47
|
+
/**
|
|
48
|
+
* Width the dock reserves in the layout: the full pane while content is
|
|
49
|
+
* shown, the bare rail while it is collapsed or minimized.
|
|
50
|
+
*/
|
|
51
|
+
const resolveInspectorDockWidth = ({ paneWidth, showPaneContent }) => showPaneContent ? paneWidth : 48;
|
|
52
|
+
//#endregion
|
|
53
|
+
export { INSPECTOR_CONTENT_MIN_WIDTH, INSPECTOR_PANE_DEFAULT_WIDTH, INSPECTOR_PANE_MAX_WIDTH, INSPECTOR_PANE_MIN_WIDTH, INSPECTOR_RAIL_WIDTH, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, shouldForceSidebarCollapsed };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { KeyboardEvent, PointerEvent } from "react";
|
|
2
|
+
//#region src/inspector/use-pane-width.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reads a persisted pane width, rejecting anything that is not a finite
|
|
5
|
+
* number inside the pane's own bounds. A corrupt or stale entry must not
|
|
6
|
+
* be able to render the pane at a width the drag handle cannot recover
|
|
7
|
+
* from, so an unusable value falls back to the default rather than being
|
|
8
|
+
* clamped into something the user never chose.
|
|
9
|
+
*/
|
|
10
|
+
declare const parsePersistedPaneWidth: (raw: string | null) => number;
|
|
11
|
+
/**
|
|
12
|
+
* Width the drag produces for a pane docked to the inline-end edge.
|
|
13
|
+
*
|
|
14
|
+
* That edge is the right in LTR (width = distance from the right) and the
|
|
15
|
+
* left in RTL (width = distance from the left). Without the RTL branch the
|
|
16
|
+
* delta is inverted and the drag oscillates.
|
|
17
|
+
*/
|
|
18
|
+
declare const resolveDragWidth: ({ clientX, isRtl, viewportWidth }: {
|
|
19
|
+
clientX: number;
|
|
20
|
+
isRtl: boolean;
|
|
21
|
+
viewportWidth: number;
|
|
22
|
+
}) => number;
|
|
23
|
+
/** Pixels one arrow press moves the edge; Page/Home/End move further. */
|
|
24
|
+
declare const INSPECTOR_PANE_KEYBOARD_STEP = 16;
|
|
25
|
+
declare const INSPECTOR_PANE_KEYBOARD_PAGE_STEP = 64;
|
|
26
|
+
/**
|
|
27
|
+
* Width a keyboard resize produces. Returns `null` for keys the handle does
|
|
28
|
+
* not own, so the caller can leave the event alone rather than swallowing
|
|
29
|
+
* every keystroke that reaches a focusable separator.
|
|
30
|
+
*
|
|
31
|
+
* Arrow semantics follow the *edge*, not the width: on an inline-end pane
|
|
32
|
+
* the inline-start arrow grows it. Under RTL the physical arrows swap, so
|
|
33
|
+
* the same key keeps meaning "make it bigger" on both sides.
|
|
34
|
+
*/
|
|
35
|
+
declare const resolveKeyboardWidth: ({ currentWidth, isRtl, key }: {
|
|
36
|
+
currentWidth: number;
|
|
37
|
+
isRtl: boolean;
|
|
38
|
+
key: string;
|
|
39
|
+
}) => number | null;
|
|
40
|
+
/**
|
|
41
|
+
* Reads and writes are wrapped because `window.localStorage` is a getter that
|
|
42
|
+
* *throws* where storage is blocked — a sandboxed iframe, a hardened privacy
|
|
43
|
+
* mode, a full quota. The `typeof window` check does not cover that, and a
|
|
44
|
+
* throw in the state initializer takes the whole pane down on mount. A pane
|
|
45
|
+
* that forgets its width is a smaller failure than a pane that does not
|
|
46
|
+
* render, so both directions degrade to in-memory.
|
|
47
|
+
*/
|
|
48
|
+
declare const readStoredWidth: (storageKey: string) => number;
|
|
49
|
+
declare const writeStoredWidth: (storageKey: string, width: number) => void;
|
|
50
|
+
type UseInspectorPaneWidthOptions = {
|
|
51
|
+
/** Inline size the sidebar takes out of the same layout row. */
|
|
52
|
+
sidebarWidth: number;
|
|
53
|
+
/**
|
|
54
|
+
* `localStorage` key the dragged width is remembered under. Omit to keep
|
|
55
|
+
* the width in memory only, which resets the pane on every reload.
|
|
56
|
+
*/
|
|
57
|
+
storageKey?: string | undefined;
|
|
58
|
+
/** Viewport width in CSS pixels; 0 before the viewport is known. */
|
|
59
|
+
viewportWidth: number;
|
|
60
|
+
};
|
|
61
|
+
type UseInspectorPaneWidthResult = {
|
|
62
|
+
/**
|
|
63
|
+
* Pointer + keyboard handlers and ARIA state to spread onto the drag
|
|
64
|
+
* handle. The handle is a focusable `separator`, so it is operable
|
|
65
|
+
* without a pointer.
|
|
66
|
+
*/
|
|
67
|
+
resizeHandleProps: {
|
|
68
|
+
"aria-orientation": "vertical";
|
|
69
|
+
"aria-valuemax": number;
|
|
70
|
+
"aria-valuemin": number;
|
|
71
|
+
"aria-valuenow": number;
|
|
72
|
+
onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
73
|
+
onLostPointerCapture: (event: PointerEvent<HTMLElement>) => void;
|
|
74
|
+
onPointerCancel: (event: PointerEvent<HTMLElement>) => void;
|
|
75
|
+
onPointerDown: (event: PointerEvent<HTMLElement>) => void;
|
|
76
|
+
onPointerMove: (event: PointerEvent<HTMLElement>) => void;
|
|
77
|
+
onPointerUp: (event: PointerEvent<HTMLElement>) => void;
|
|
78
|
+
tabIndex: number;
|
|
79
|
+
};
|
|
80
|
+
/** Reset the pane to its default width (double-click affordance). */
|
|
81
|
+
resetWidth: () => void;
|
|
82
|
+
/**
|
|
83
|
+
* Width the pane renders at: the dragged width clamped against the room
|
|
84
|
+
* left beside the sidebar.
|
|
85
|
+
*/
|
|
86
|
+
width: number;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Owns the docked pane's width: the width the user dragged to, its
|
|
90
|
+
* persistence, and the clamp against the room actually available.
|
|
91
|
+
*
|
|
92
|
+
* What renders is always the clamped value, so shrinking the window or
|
|
93
|
+
* expanding the sidebar takes space back from the pane instead of crushing
|
|
94
|
+
* the content column. The *desired* width keeps the user's choice, so the
|
|
95
|
+
* pane returns to it once the room comes back.
|
|
96
|
+
*/
|
|
97
|
+
declare const useInspectorPaneWidth: ({ sidebarWidth, storageKey, viewportWidth }: UseInspectorPaneWidthOptions) => UseInspectorPaneWidthResult;
|
|
98
|
+
//#endregion
|
|
99
|
+
export { INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, parsePersistedPaneWidth, readStoredWidth, resolveDragWidth, resolveKeyboardWidth, useInspectorPaneWidth, writeStoredWidth };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { resolveInspectorPaneWidth } from "./pane-width.js";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
//#region src/inspector/use-pane-width.ts
|
|
4
|
+
/**
|
|
5
|
+
* Reads a persisted pane width, rejecting anything that is not a finite
|
|
6
|
+
* number inside the pane's own bounds. A corrupt or stale entry must not
|
|
7
|
+
* be able to render the pane at a width the drag handle cannot recover
|
|
8
|
+
* from, so an unusable value falls back to the default rather than being
|
|
9
|
+
* clamped into something the user never chose.
|
|
10
|
+
*/
|
|
11
|
+
const parsePersistedPaneWidth = (raw) => {
|
|
12
|
+
if (raw === null) return 512;
|
|
13
|
+
const parsed = Number.parseInt(raw, 10);
|
|
14
|
+
if (!Number.isFinite(parsed) || parsed < 320 || parsed > 800) return 512;
|
|
15
|
+
return parsed;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Width the drag produces for a pane docked to the inline-end edge.
|
|
19
|
+
*
|
|
20
|
+
* That edge is the right in LTR (width = distance from the right) and the
|
|
21
|
+
* left in RTL (width = distance from the left). Without the RTL branch the
|
|
22
|
+
* delta is inverted and the drag oscillates.
|
|
23
|
+
*/
|
|
24
|
+
const resolveDragWidth = ({ clientX, isRtl, viewportWidth }) => isRtl ? clientX : viewportWidth - clientX;
|
|
25
|
+
/** Pixels one arrow press moves the edge; Page/Home/End move further. */
|
|
26
|
+
const INSPECTOR_PANE_KEYBOARD_STEP = 16;
|
|
27
|
+
const INSPECTOR_PANE_KEYBOARD_PAGE_STEP = 64;
|
|
28
|
+
/**
|
|
29
|
+
* Width a keyboard resize produces. Returns `null` for keys the handle does
|
|
30
|
+
* not own, so the caller can leave the event alone rather than swallowing
|
|
31
|
+
* every keystroke that reaches a focusable separator.
|
|
32
|
+
*
|
|
33
|
+
* Arrow semantics follow the *edge*, not the width: on an inline-end pane
|
|
34
|
+
* the inline-start arrow grows it. Under RTL the physical arrows swap, so
|
|
35
|
+
* the same key keeps meaning "make it bigger" on both sides.
|
|
36
|
+
*/
|
|
37
|
+
const resolveKeyboardWidth = ({ currentWidth, isRtl, key }) => {
|
|
38
|
+
const grow = isRtl ? "ArrowRight" : "ArrowLeft";
|
|
39
|
+
const shrink = isRtl ? "ArrowLeft" : "ArrowRight";
|
|
40
|
+
switch (key) {
|
|
41
|
+
case grow: return currentWidth + 16;
|
|
42
|
+
case shrink: return currentWidth - 16;
|
|
43
|
+
case "PageUp": return currentWidth + 64;
|
|
44
|
+
case "PageDown": return currentWidth - 64;
|
|
45
|
+
case "Home": return 320;
|
|
46
|
+
case "End": return 800;
|
|
47
|
+
case "Enter": return 512;
|
|
48
|
+
default: return null;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Reads and writes are wrapped because `window.localStorage` is a getter that
|
|
53
|
+
* *throws* where storage is blocked — a sandboxed iframe, a hardened privacy
|
|
54
|
+
* mode, a full quota. The `typeof window` check does not cover that, and a
|
|
55
|
+
* throw in the state initializer takes the whole pane down on mount. A pane
|
|
56
|
+
* that forgets its width is a smaller failure than a pane that does not
|
|
57
|
+
* render, so both directions degrade to in-memory.
|
|
58
|
+
*/
|
|
59
|
+
const readStoredWidth = (storageKey) => {
|
|
60
|
+
if (typeof window === "undefined") return 512;
|
|
61
|
+
try {
|
|
62
|
+
return parsePersistedPaneWidth(window.localStorage.getItem(storageKey));
|
|
63
|
+
} catch {
|
|
64
|
+
return 512;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const writeStoredWidth = (storageKey, width) => {
|
|
68
|
+
if (typeof window === "undefined") return;
|
|
69
|
+
try {
|
|
70
|
+
window.localStorage.setItem(storageKey, String(width));
|
|
71
|
+
} catch {}
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Owns the docked pane's width: the width the user dragged to, its
|
|
75
|
+
* persistence, and the clamp against the room actually available.
|
|
76
|
+
*
|
|
77
|
+
* What renders is always the clamped value, so shrinking the window or
|
|
78
|
+
* expanding the sidebar takes space back from the pane instead of crushing
|
|
79
|
+
* the content column. The *desired* width keeps the user's choice, so the
|
|
80
|
+
* pane returns to it once the room comes back.
|
|
81
|
+
*/
|
|
82
|
+
const useInspectorPaneWidth = ({ sidebarWidth, storageKey, viewportWidth }) => {
|
|
83
|
+
const [desiredWidth, setDesiredWidth] = useState(() => storageKey === void 0 ? 512 : readStoredWidth(storageKey));
|
|
84
|
+
const isDragging = useRef(false);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (storageKey === void 0) return;
|
|
87
|
+
writeStoredWidth(storageKey, desiredWidth);
|
|
88
|
+
}, [desiredWidth, storageKey]);
|
|
89
|
+
const handlePointerDown = (event) => {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
isDragging.current = true;
|
|
92
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
93
|
+
};
|
|
94
|
+
const handlePointerMove = (event) => {
|
|
95
|
+
if (!isDragging.current) return;
|
|
96
|
+
setDesiredWidth(resolveDragWidth({
|
|
97
|
+
clientX: event.clientX,
|
|
98
|
+
isRtl: document.documentElement.dir === "rtl",
|
|
99
|
+
viewportWidth: window.innerWidth
|
|
100
|
+
}));
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Ends the drag however the pointer stream ended. `pointerup` is only one of
|
|
104
|
+
* the ways: a touch taken over by scrolling, a context menu, or the handle
|
|
105
|
+
* losing capture mid-drag all end it without one, and a drag flag left set
|
|
106
|
+
* would resize the pane on the next pointer movement with no button held.
|
|
107
|
+
*/
|
|
108
|
+
const endDrag = (event) => {
|
|
109
|
+
isDragging.current = false;
|
|
110
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
111
|
+
};
|
|
112
|
+
const handleKeyDown = (event) => {
|
|
113
|
+
const next = resolveKeyboardWidth({
|
|
114
|
+
currentWidth: desiredWidth,
|
|
115
|
+
isRtl: document.documentElement.dir === "rtl",
|
|
116
|
+
key: event.key
|
|
117
|
+
});
|
|
118
|
+
if (next === null) return;
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
setDesiredWidth(Math.min(Math.max(next, 320), 800));
|
|
121
|
+
};
|
|
122
|
+
const resetWidth = () => {
|
|
123
|
+
setDesiredWidth(512);
|
|
124
|
+
};
|
|
125
|
+
const width = resolveInspectorPaneWidth({
|
|
126
|
+
desiredWidth,
|
|
127
|
+
sidebarWidth,
|
|
128
|
+
viewportWidth
|
|
129
|
+
});
|
|
130
|
+
return {
|
|
131
|
+
resetWidth,
|
|
132
|
+
resizeHandleProps: {
|
|
133
|
+
"aria-orientation": "vertical",
|
|
134
|
+
"aria-valuemax": 800,
|
|
135
|
+
"aria-valuemin": 320,
|
|
136
|
+
"aria-valuenow": width,
|
|
137
|
+
onKeyDown: handleKeyDown,
|
|
138
|
+
onLostPointerCapture: endDrag,
|
|
139
|
+
onPointerCancel: endDrag,
|
|
140
|
+
onPointerDown: handlePointerDown,
|
|
141
|
+
onPointerMove: handlePointerMove,
|
|
142
|
+
onPointerUp: endDrag,
|
|
143
|
+
tabIndex: 0
|
|
144
|
+
},
|
|
145
|
+
width
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
export { INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, parsePersistedPaneWidth, readStoredWidth, resolveDragWidth, resolveKeyboardWidth, useInspectorPaneWidth, writeStoredWidth };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/kanban/card-properties.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Which properties a kanban card renders as values.
|
|
4
|
+
*
|
|
5
|
+
* A card draws some fields itself — a title, a badge row, a footer — and the
|
|
6
|
+
* rest as generic property values. The split used to be a chain of literal id
|
|
7
|
+
* comparisons inside the card component, which meant the card knew every id the
|
|
8
|
+
* board could hold. Here the caller names its reserved ids once and vetoes the
|
|
9
|
+
* rest through a predicate.
|
|
10
|
+
*/
|
|
11
|
+
type KanbanCardFieldSelection = {
|
|
12
|
+
/** Field ids the card renders itself, so they are not repeated as values. */
|
|
13
|
+
reservedFieldIds: readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* Vetoes a field the board could render but this card should not (a
|
|
16
|
+
* system-computed property whose value the column already conveys, say).
|
|
17
|
+
* Omit to render every unreserved field.
|
|
18
|
+
*/
|
|
19
|
+
isRenderable?: ((fieldId: string) => boolean) | undefined;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The visible field ids a card renders as property values, in view order and
|
|
23
|
+
* without duplicates.
|
|
24
|
+
*/
|
|
25
|
+
declare const selectKanbanCardFieldIds: (visibleFieldIds: readonly string[], { reservedFieldIds, isRenderable }: KanbanCardFieldSelection) => string[];
|
|
26
|
+
//#endregion
|
|
27
|
+
export { KanbanCardFieldSelection, selectKanbanCardFieldIds };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/kanban/card-properties.ts
|
|
2
|
+
/**
|
|
3
|
+
* The visible field ids a card renders as property values, in view order and
|
|
4
|
+
* without duplicates.
|
|
5
|
+
*/
|
|
6
|
+
const selectKanbanCardFieldIds = (visibleFieldIds, { reservedFieldIds, isRenderable }) => {
|
|
7
|
+
const reserved = new Set(reservedFieldIds);
|
|
8
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9
|
+
const selected = [];
|
|
10
|
+
for (const fieldId of visibleFieldIds) {
|
|
11
|
+
if (reserved.has(fieldId) || seen.has(fieldId)) continue;
|
|
12
|
+
seen.add(fieldId);
|
|
13
|
+
if (isRenderable && !isRenderable(fieldId)) continue;
|
|
14
|
+
selected.push(fieldId);
|
|
15
|
+
}
|
|
16
|
+
return selected;
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { selectKanbanCardFieldIds };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ReactNode, RefObject } from "react";
|
|
2
|
+
//#region src/kanban/card-shell.d.ts
|
|
3
|
+
type KanbanCardShellProps = {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
/** Overlay slot pinned to the top-end corner (row actions). */
|
|
6
|
+
actions?: ReactNode;
|
|
7
|
+
/** Marks the card whose detail is currently open. */
|
|
8
|
+
active?: boolean | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Opens the card. Omit for a card with nothing to open: the shell then
|
|
11
|
+
* renders a plain region instead of a button, so a card that does nothing
|
|
12
|
+
* never lands in the tab order.
|
|
13
|
+
*/
|
|
14
|
+
onOpen?: (() => void) | undefined;
|
|
15
|
+
/** The card body, for callers that measure or flash it. */
|
|
16
|
+
bodyRef?: RefObject<HTMLDivElement | null> | undefined;
|
|
17
|
+
/** The drag source the shell wraps the card in. */
|
|
18
|
+
dragRef?: RefObject<HTMLDivElement | null> | undefined;
|
|
19
|
+
className?: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The card chrome: border, hover lift, active ring, drag wrapper, and the
|
|
23
|
+
* keyboard contract for a card that opens something.
|
|
24
|
+
*
|
|
25
|
+
* The shell carries no idea of what a card holds. Every board rendered one of
|
|
26
|
+
* three near-identical copies of this markup, differing only in whether the
|
|
27
|
+
* card opened anything, which is why opening is a prop rather than a branch at
|
|
28
|
+
* each call site.
|
|
29
|
+
*/
|
|
30
|
+
declare const KanbanCardShell: ({ children, actions, active, onOpen, bodyRef, dragRef, className }: KanbanCardShellProps) => import("react").JSX.Element;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { KanbanCardShell, KanbanCardShellProps };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { containedHandler } from "../hooks/use-contained-handler.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/kanban/card-shell.tsx
|
|
5
|
+
/**
|
|
6
|
+
* The card chrome: border, hover lift, active ring, drag wrapper, and the
|
|
7
|
+
* keyboard contract for a card that opens something.
|
|
8
|
+
*
|
|
9
|
+
* The shell carries no idea of what a card holds. Every board rendered one of
|
|
10
|
+
* three near-identical copies of this markup, differing only in whether the
|
|
11
|
+
* card opened anything, which is why opening is a prop rather than a branch at
|
|
12
|
+
* each call site.
|
|
13
|
+
*/
|
|
14
|
+
const KanbanCardShell = ({ children, actions, active, onOpen, bodyRef, dragRef, className }) => {
|
|
15
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [children, actions] });
|
|
16
|
+
if (!onOpen) return /* @__PURE__ */ jsx("div", {
|
|
17
|
+
className: "group/card",
|
|
18
|
+
ref: dragRef,
|
|
19
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
20
|
+
className: cn(CARD_CLASS, active && ACTIVE_CLASS, className),
|
|
21
|
+
ref: bodyRef,
|
|
22
|
+
children: body
|
|
23
|
+
})
|
|
24
|
+
});
|
|
25
|
+
return /* @__PURE__ */ jsx("div", {
|
|
26
|
+
className: "group/card",
|
|
27
|
+
ref: dragRef,
|
|
28
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
29
|
+
className: cn(CARD_CLASS, "cursor-pointer transition-shadow hover:shadow-md", active && ACTIVE_CLASS, className),
|
|
30
|
+
onClick: containedHandler(bodyRef, onOpen),
|
|
31
|
+
onKeyDown: (event) => {
|
|
32
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
onOpen();
|
|
35
|
+
},
|
|
36
|
+
ref: bodyRef,
|
|
37
|
+
role: "button",
|
|
38
|
+
tabIndex: 0,
|
|
39
|
+
children: body
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const CARD_CLASS = "bg-card relative block w-full rounded-lg border p-3 text-start shadow-xs";
|
|
44
|
+
const ACTIVE_CLASS = "ring-primary/30 ring-2";
|
|
45
|
+
//#endregion
|
|
46
|
+
export { KanbanCardShell };
|