@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.
Files changed (149) hide show
  1. package/README.md +84 -0
  2. package/dist/components/accordion.d.ts +8 -0
  3. package/dist/components/accordion.js +38 -0
  4. package/dist/components/alert-dialog.d.ts +32 -0
  5. package/dist/components/alert-dialog.js +75 -0
  6. package/dist/components/avatar.d.ts +7 -0
  7. package/dist/components/avatar.js +22 -0
  8. package/dist/components/bidi-text.d.ts +24 -0
  9. package/dist/components/bidi-text.js +38 -0
  10. package/dist/components/brand-icons.d.ts +7 -0
  11. package/dist/components/brand-icons.js +18 -0
  12. package/dist/components/breadcrumb.d.ts +12 -0
  13. package/dist/components/breadcrumb.js +61 -0
  14. package/dist/components/button-variants.d.ts +17 -0
  15. package/dist/components/button-variants.js +43 -0
  16. package/dist/components/button.d.ts +20 -0
  17. package/dist/components/button.js +53 -0
  18. package/dist/components/checkbox.d.ts +5 -0
  19. package/dist/components/checkbox.js +44 -0
  20. package/dist/components/color-picker.d.ts +47 -0
  21. package/dist/components/color-picker.js +249 -0
  22. package/dist/components/combobox.d.ts +40 -0
  23. package/dist/components/combobox.js +208 -0
  24. package/dist/components/command.d.ts +34 -0
  25. package/dist/components/command.js +100 -0
  26. package/dist/components/date-picker-popover.d.ts +29 -0
  27. package/dist/components/date-picker-popover.js +548 -0
  28. package/dist/components/date-picker-popover.logic.d.ts +14 -0
  29. package/dist/components/date-picker-popover.logic.js +26 -0
  30. package/dist/components/destructive-action-confirmation.d.ts +21 -0
  31. package/dist/components/destructive-action-confirmation.js +60 -0
  32. package/dist/components/destructive-confirm-dialog.d.ts +17 -0
  33. package/dist/components/destructive-confirm-dialog.js +49 -0
  34. package/dist/components/dialog.d.ts +37 -0
  35. package/dist/components/dialog.js +92 -0
  36. package/dist/components/directional-icon.d.ts +30 -0
  37. package/dist/components/directional-icon.js +12 -0
  38. package/dist/components/field.d.ts +11 -0
  39. package/dist/components/field.js +34 -0
  40. package/dist/components/form.d.ts +5 -0
  41. package/dist/components/form.js +12 -0
  42. package/dist/components/frame.d.ts +10 -0
  43. package/dist/components/frame.js +35 -0
  44. package/dist/components/hex-color-picker.d.ts +11 -0
  45. package/dist/components/hex-color-picker.js +366 -0
  46. package/dist/components/input-group.d.ts +15 -0
  47. package/dist/components/input-group.js +53 -0
  48. package/dist/components/input-otp.d.ts +13 -0
  49. package/dist/components/input-otp.js +39 -0
  50. package/dist/components/input.d.ts +12 -0
  51. package/dist/components/input.js +46 -0
  52. package/dist/components/label.d.ts +5 -0
  53. package/dist/components/label.js +18 -0
  54. package/dist/components/menu.d.ts +44 -0
  55. package/dist/components/menu.js +152 -0
  56. package/dist/components/outline-rail.d.ts +43 -0
  57. package/dist/components/outline-rail.js +416 -0
  58. package/dist/components/pagination.d.ts +17 -0
  59. package/dist/components/pagination.js +78 -0
  60. package/dist/components/popover.d.ts +34 -0
  61. package/dist/components/popover.js +67 -0
  62. package/dist/components/preview-card.d.ts +12 -0
  63. package/dist/components/preview-card.js +25 -0
  64. package/dist/components/preview-pane.d.ts +23 -0
  65. package/dist/components/preview-pane.js +33 -0
  66. package/dist/components/scroll-area.d.ts +19 -0
  67. package/dist/components/scroll-area.js +38 -0
  68. package/dist/components/scroll-to-top.d.ts +12 -0
  69. package/dist/components/scroll-to-top.js +43 -0
  70. package/dist/components/secret-input.d.ts +9 -0
  71. package/dist/components/secret-input.js +38 -0
  72. package/dist/components/segmented-icon-toggle.d.ts +22 -0
  73. package/dist/components/segmented-icon-toggle.js +30 -0
  74. package/dist/components/select.d.ts +25 -0
  75. package/dist/components/select.js +196 -0
  76. package/dist/components/separator.d.ts +16 -0
  77. package/dist/components/separator.js +26 -0
  78. package/dist/components/sheet.d.ts +29 -0
  79. package/dist/components/sheet.js +87 -0
  80. package/dist/components/skeleton.d.ts +4 -0
  81. package/dist/components/skeleton.js +10 -0
  82. package/dist/components/stella-mark.d.ts +5 -0
  83. package/dist/components/stella-mark.js +12 -0
  84. package/dist/components/stella-wordmark.d.ts +5 -0
  85. package/dist/components/stella-wordmark.js +21 -0
  86. package/dist/components/table.d.ts +20 -0
  87. package/dist/components/table.js +80 -0
  88. package/dist/components/tabs.d.ts +11 -0
  89. package/dist/components/tabs.js +46 -0
  90. package/dist/components/textarea.d.ts +9 -0
  91. package/dist/components/textarea.js +31 -0
  92. package/dist/components/toast.d.ts +60 -0
  93. package/dist/components/toast.js +261 -0
  94. package/dist/components/tooltip-trigger-helper.d.ts +11 -0
  95. package/dist/components/tooltip-trigger-helper.js +14 -0
  96. package/dist/components/tooltip.d.ts +15 -0
  97. package/dist/components/tooltip.js +32 -0
  98. package/dist/data-table/index.d.ts +2 -0
  99. package/dist/data-table/index.js +2 -0
  100. package/dist/data-table/schema.d.ts +70 -0
  101. package/dist/data-table/schema.js +40 -0
  102. package/dist/hooks/use-contained-handler.d.ts +55 -0
  103. package/dist/hooks/use-contained-handler.js +54 -0
  104. package/dist/hooks/use-content-dir.d.ts +48 -0
  105. package/dist/hooks/use-content-dir.js +45 -0
  106. package/dist/hooks/use-mobile.d.ts +4 -0
  107. package/dist/hooks/use-mobile.js +16 -0
  108. package/dist/hooks/use-viewport-width.d.ts +5 -0
  109. package/dist/hooks/use-viewport-width.js +14 -0
  110. package/dist/index.d.ts +67 -0
  111. package/dist/index.js +65 -0
  112. package/dist/inspector/chrome.d.ts +72 -0
  113. package/dist/inspector/chrome.js +153 -0
  114. package/dist/inspector/dock.d.ts +48 -0
  115. package/dist/inspector/dock.js +36 -0
  116. package/dist/inspector/index.d.ts +6 -0
  117. package/dist/inspector/index.js +6 -0
  118. package/dist/inspector/layout-tokens.d.ts +28 -0
  119. package/dist/inspector/layout-tokens.js +28 -0
  120. package/dist/inspector/pane-width.d.ts +64 -0
  121. package/dist/inspector/pane-width.js +53 -0
  122. package/dist/inspector/use-pane-width.d.ts +99 -0
  123. package/dist/inspector/use-pane-width.js +149 -0
  124. package/dist/kanban/card-properties.d.ts +27 -0
  125. package/dist/kanban/card-properties.js +19 -0
  126. package/dist/kanban/card-shell.d.ts +32 -0
  127. package/dist/kanban/card-shell.js +46 -0
  128. package/dist/kanban/column-header.d.ts +23 -0
  129. package/dist/kanban/column-header.js +24 -0
  130. package/dist/kanban/grouping.d.ts +84 -0
  131. package/dist/kanban/grouping.js +76 -0
  132. package/dist/kanban/index.d.ts +5 -0
  133. package/dist/kanban/index.js +5 -0
  134. package/dist/lib/button-disposition.d.ts +63 -0
  135. package/dist/lib/button-disposition.js +52 -0
  136. package/dist/lib/option-color.d.ts +28 -0
  137. package/dist/lib/option-color.js +59 -0
  138. package/dist/lib/overlay-layer.d.ts +22 -0
  139. package/dist/lib/overlay-layer.js +21 -0
  140. package/dist/lib/tab-order.d.ts +15 -0
  141. package/dist/lib/tab-order.js +15 -0
  142. package/dist/lib/tooltip-content.d.ts +13 -0
  143. package/dist/lib/tooltip-content.js +12 -0
  144. package/dist/lib/utils.d.ts +20 -0
  145. package/dist/lib/utils.js +41 -0
  146. package/dist/lib/week.d.ts +25 -0
  147. package/dist/lib/week.js +40 -0
  148. package/dist/styles/theme.css +1209 -0
  149. 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 };