@stll/ui 0.2.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.
@@ -3,9 +3,13 @@ import { ReactNode, RefObject } from "react";
3
3
  type OutlineItem = {
4
4
  id: string;
5
5
  label: string;
6
+ /** What the entry contains, after the label that names it. The label
7
+ * stays whole; the title is what truncates when the row is narrow. */
8
+ title?: string;
6
9
  /** Nesting depth among included items; drives indent + tick taper. */
7
10
  level: number;
8
- /** Optional trailing annotation in the panel (e.g. a page number). */
11
+ /** Optional trailing annotation in the panel (e.g. a page number or a
12
+ * provision range); never truncated. */
9
13
  meta?: string;
10
14
  /** Optional CSS custom-property name colouring this entry's tick + chip
11
15
  * (e.g. "--option-blue"). Defaults to the neutral foreground. */
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
3
  import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "./tooltip.js";
4
- import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
6
6
  //#region src/components/outline-rail.tsx
7
7
  /**
@@ -23,6 +23,8 @@ const TICK_BASE_WIDTH = 6;
23
23
  const TICK_LEVEL_STEP = 2;
24
24
  const TICK_MAX_LEVEL = 5;
25
25
  const RAIL_MAX_TICKS = 40;
26
+ /** The entry as one line of text: label, then its title when it has one. */
27
+ const entryText = (item) => item.title === void 0 ? item.label : `${item.label} ${item.title}`;
26
28
  const tickWidth = (level) => {
27
29
  return TICK_BASE_WIDTH + (TICK_MAX_LEVEL - Math.min(Math.max(level, 0), TICK_MAX_LEVEL)) * TICK_LEVEL_STEP;
28
30
  };
@@ -199,7 +201,8 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
199
201
  }
200
202
  onJumpRef.current(id, container);
201
203
  }, [activeId, scrollContainerRef]);
202
- const toggleCollapse = useCallback((id, level, rowEl) => {
204
+ const toggleCollapse = useCallback((id, rowEl) => {
205
+ const rowTop = rowEl?.getBoundingClientRect().top;
203
206
  setToggled((prev) => new Set(prev).add(id));
204
207
  setCollapsed((prev) => {
205
208
  const next = new Set(prev);
@@ -209,9 +212,8 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
209
212
  });
210
213
  requestAnimationFrame(() => {
211
214
  const panel = panelRef.current;
212
- if (!panel || !rowEl) return;
213
- const target = panel.getBoundingClientRect().top + level * ROW_H;
214
- panel.scrollTop += rowEl.getBoundingClientRect().top - target;
215
+ if (!panel || !rowEl || rowTop === void 0) return;
216
+ panel.scrollTop += rowEl.getBoundingClientRect().top - rowTop;
215
217
  });
216
218
  }, []);
217
219
  const openPanel = useCallback(() => {
@@ -261,7 +263,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
261
263
  "aria-expanded": !isCollapsed,
262
264
  "aria-label": isCollapsed ? "Expand" : "Collapse",
263
265
  className: "text-muted-foreground hover:text-foreground flex size-5 shrink-0 items-center justify-center",
264
- onClick: (event) => toggleCollapse(node.item.id, node.item.level, event.currentTarget.parentElement),
266
+ onClick: (event) => toggleCollapse(node.item.id, event.currentTarget.parentElement),
265
267
  style: { marginInlineStart: indent - 4 },
266
268
  type: "button",
267
269
  children: /* @__PURE__ */ jsx(Chevron, { open: !isCollapsed })
@@ -277,12 +279,21 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
277
279
  }),
278
280
  /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
279
281
  render: /* @__PURE__ */ jsx("button", {
280
- className: cn("min-w-0 flex-1 truncate py-1.5 text-start text-[13px] leading-snug", rowTextClass(isActive, hasChildren)),
282
+ className: cn("flex min-w-0 flex-1 items-baseline gap-1.5 py-1.5 text-start text-[13px] leading-snug", rowTextClass(isActive, hasChildren)),
281
283
  onClick: () => jumpTo(node.item.id),
282
284
  type: "button"
283
285
  }),
284
- children: node.item.label
285
- }), /* @__PURE__ */ jsx(TooltipPopup, { children: node.item.label })] }),
286
+ children: node.item.title === void 0 ? /* @__PURE__ */ jsx("span", {
287
+ className: "min-w-0 truncate",
288
+ children: node.item.label
289
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
290
+ className: "shrink-0 font-medium",
291
+ children: node.item.label
292
+ }), /* @__PURE__ */ jsx("span", {
293
+ className: "min-w-0 truncate font-normal",
294
+ children: node.item.title
295
+ })] })
296
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: entryText(node.item) })] }),
286
297
  node.item.meta !== void 0 && /* @__PURE__ */ jsx("span", {
287
298
  className: "text-foreground-placeholder shrink-0 ps-2 text-[11px] tabular-nums",
288
299
  children: node.item.meta
@@ -0,0 +1,2 @@
1
+ import { TableColumnCapabilities, TableColumnDescriptor, TableSchema, duplicateColumnIds, findTableColumn, hideableColumnIds, sortableColumnIds, tableColumnIds, tableColumnSizing, visibleColumnIds } from "./schema.js";
2
+ export { type TableColumnCapabilities, type TableColumnDescriptor, type TableSchema, duplicateColumnIds, findTableColumn, hideableColumnIds, sortableColumnIds, tableColumnIds, tableColumnSizing, visibleColumnIds };
@@ -0,0 +1,2 @@
1
+ import { duplicateColumnIds, findTableColumn, hideableColumnIds, sortableColumnIds, tableColumnIds, tableColumnSizing, visibleColumnIds } from "./schema.js";
2
+ export { duplicateColumnIds, findTableColumn, hideableColumnIds, sortableColumnIds, tableColumnIds, tableColumnSizing, visibleColumnIds };
@@ -0,0 +1,70 @@
1
+ //#region src/data-table/schema.d.ts
2
+ /**
3
+ * A table's schema: which columns exist, what each can do, and how wide it
4
+ * starts. Not how a cell draws — that is the caller's, and it is the reason
5
+ * this module holds no React.
6
+ *
7
+ * The split matters because "which columns does this view have, and which of
8
+ * them can be sorted, hidden, resized or pinned" is a question about data, and
9
+ * a question about data can be answered by a test. It used to be answerable
10
+ * only by rendering the table and asking TanStack.
11
+ */
12
+ /** What a reader may do to a column. */
13
+ type TableColumnCapabilities = {
14
+ sort: boolean;
15
+ hide: boolean;
16
+ resize: boolean;
17
+ pin: boolean;
18
+ };
19
+ /**
20
+ * One column.
21
+ *
22
+ * `render` is whatever the caller needs to draw the column: this module never
23
+ * looks inside it, which is what keeps the schema free of any idea about what
24
+ * a row holds.
25
+ */
26
+ type TableColumnDescriptor<TRender> = {
27
+ /** Unique within the schema, and stable across renders. */
28
+ id: string;
29
+ /** Header text. Empty for a column whose header draws no text. */
30
+ label: string;
31
+ render: TRender;
32
+ /** Starting width, in pixels. */
33
+ size: number;
34
+ /** Narrowest this column may be resized to; the schema's default if absent. */
35
+ minSize?: number | undefined;
36
+ capabilities: TableColumnCapabilities;
37
+ /**
38
+ * Metadata columns read quieter than content ones; a utility column
39
+ * (a selection checkbox, an add-column affordance) draws no value at all.
40
+ */
41
+ emphasis: "content" | "metadata" | "utility";
42
+ };
43
+ type TableSchema<TRender> = {
44
+ columns: readonly TableColumnDescriptor<TRender>[];
45
+ /** Narrowest any column may be resized to. */
46
+ defaultMinSize: number;
47
+ };
48
+ /** Every column a schema declares, in order. */
49
+ declare const tableColumnIds: <TRender>(schema: TableSchema<TRender>) => string[];
50
+ declare const sortableColumnIds: <TRender>(schema: TableSchema<TRender>) => string[];
51
+ declare const hideableColumnIds: <TRender>(schema: TableSchema<TRender>) => string[];
52
+ declare const findTableColumn: <TRender>(schema: TableSchema<TRender>, id: string) => TableColumnDescriptor<TRender> | undefined;
53
+ /**
54
+ * Which columns a view shows.
55
+ *
56
+ * A column that cannot be hidden stays visible whatever the stored hidden list
57
+ * says, so a stale list — a column that lost its hide capability while it was
58
+ * hidden — cannot strand the table without its select or name column.
59
+ */
60
+ declare const visibleColumnIds: <TRender>(schema: TableSchema<TRender>, hiddenColumnIds: readonly string[]) => string[];
61
+ /** The starting width of every column, keyed by id. */
62
+ declare const tableColumnSizing: <TRender>(schema: TableSchema<TRender>) => Record<string, number>;
63
+ /**
64
+ * A duplicate column id silently drops a column: the table keys by id, so the
65
+ * second declaration wins and the first disappears with no error anywhere.
66
+ * Callers that build a schema from user data (a property list) check here.
67
+ */
68
+ declare const duplicateColumnIds: <TRender>(schema: TableSchema<TRender>) => string[];
69
+ //#endregion
70
+ export { TableColumnCapabilities, TableColumnDescriptor, TableSchema, duplicateColumnIds, findTableColumn, hideableColumnIds, sortableColumnIds, tableColumnIds, tableColumnSizing, visibleColumnIds };
@@ -0,0 +1,40 @@
1
+ //#region src/data-table/schema.ts
2
+ /** Every column a schema declares, in order. */
3
+ const tableColumnIds = (schema) => schema.columns.map((column) => column.id);
4
+ const withCapability = (schema, capability) => schema.columns.filter((column) => column.capabilities[capability]).map((column) => column.id);
5
+ const sortableColumnIds = (schema) => withCapability(schema, "sort");
6
+ const hideableColumnIds = (schema) => withCapability(schema, "hide");
7
+ const findTableColumn = (schema, id) => schema.columns.find((column) => column.id === id);
8
+ /**
9
+ * Which columns a view shows.
10
+ *
11
+ * A column that cannot be hidden stays visible whatever the stored hidden list
12
+ * says, so a stale list — a column that lost its hide capability while it was
13
+ * hidden — cannot strand the table without its select or name column.
14
+ */
15
+ const visibleColumnIds = (schema, hiddenColumnIds) => {
16
+ const hidden = new Set(hiddenColumnIds);
17
+ return schema.columns.filter((column) => !column.capabilities.hide || !hidden.has(column.id)).map((column) => column.id);
18
+ };
19
+ /** The starting width of every column, keyed by id. */
20
+ const tableColumnSizing = (schema) => {
21
+ const sizing = {};
22
+ for (const column of schema.columns) sizing[column.id] = column.size;
23
+ return sizing;
24
+ };
25
+ /**
26
+ * A duplicate column id silently drops a column: the table keys by id, so the
27
+ * second declaration wins and the first disappears with no error anywhere.
28
+ * Callers that build a schema from user data (a property list) check here.
29
+ */
30
+ const duplicateColumnIds = (schema) => {
31
+ const seen = /* @__PURE__ */ new Set();
32
+ const duplicates = /* @__PURE__ */ new Set();
33
+ for (const column of schema.columns) {
34
+ if (seen.has(column.id)) duplicates.add(column.id);
35
+ seen.add(column.id);
36
+ }
37
+ return [...duplicates];
38
+ };
39
+ //#endregion
40
+ export { duplicateColumnIds, findTableColumn, hideableColumnIds, sortableColumnIds, tableColumnIds, tableColumnSizing, visibleColumnIds };
package/dist/index.d.ts CHANGED
@@ -45,6 +45,7 @@ import { SortDirection, SortableHead, Table, TableBody, TableCaption, TableCell,
45
45
  import { Tabs, TabsContent as TabsPanel, TabsList, TabsTab } from "./components/tabs.js";
46
46
  import { AnchoredToastProvider, TOAST_RIGHT_OFFSET_VAR, ToastPosition, ToastProvider, stellaToast } from "./components/toast.js";
47
47
  import { Tooltip, TooltipContent as TooltipPopup, TooltipCreateHandle, TooltipProvider, TooltipTrigger } from "./components/tooltip.js";
48
+ import { TableColumnCapabilities, TableColumnDescriptor, TableSchema, duplicateColumnIds, findTableColumn, hideableColumnIds, sortableColumnIds, tableColumnIds, tableColumnSizing, visibleColumnIds } from "./data-table/schema.js";
48
49
  import { containedEventHandler, containedHandler } from "./hooks/use-contained-handler.js";
49
50
  import { contentDir, isStructuredInputType, useContentDir } from "./hooks/use-content-dir.js";
50
51
  import { useIsMobile } from "./hooks/use-mobile.js";
@@ -55,6 +56,12 @@ import { PROPERTY_ROW_GRID, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZ
55
56
  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 "./inspector/pane-width.js";
56
57
  import { INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, parsePersistedPaneWidth, resolveDragWidth, resolveKeyboardWidth, useInspectorPaneWidth } from "./inspector/use-pane-width.js";
57
58
  import "./inspector/index.js";
59
+ import { KanbanCardFieldSelection, selectKanbanCardFieldIds } from "./kanban/card-properties.js";
60
+ import { KanbanCardShell, KanbanCardShellProps } from "./kanban/card-shell.js";
61
+ import { KanbanColumnHeader, KanbanColumnHeaderProps } from "./kanban/column-header.js";
62
+ import { ColorVariants, OptionColor, emptyColor, optionColors, resolveOptionColor } from "./lib/option-color.js";
63
+ import { KanbanBuiltInGroup, KanbanGroup, KanbanGroupOption, KanbanGrouping, KanbanSchema, ResolveKanbanGroupingParams, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows } from "./kanban/grouping.js";
64
+ import "./kanban/index.js";
58
65
  import { cn, composeRefs } from "./lib/utils.js";
59
66
  import { getFirstWeekday, getLocaleWeekInfo, getWeekendDays } from "./lib/week.js";
60
- export { Accordion, AccordionPanel as AccordionContent, AccordionPanel, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogBackdrop, AlertDialogBackdrop as AlertDialogOverlay, AlertDialogClose, AlertDialogPopup as AlertDialogContent, AlertDialogPopup, AlertDialogCreateHandle, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPanel, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertDialogViewport, AnchoredToastProvider, Avatar, AvatarFallback, AvatarImage, type BidiDirection, BidiText, type BidiTextProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Checkbox, ColorPicker, ColorPickerContent, type ColorPickerContentProps, type ColorPickerProps, type ColorPreset, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxClear, ComboboxCollection, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxList, ComboboxPopup, ComboboxRow, ComboboxSeparator, ComboboxStatus, ComboboxTrigger, ComboboxValue, Command, CommandCollection, CommandDialog, CommandDialogPopup, CommandDialogTrigger, CommandEmpty, CommandFooter, CommandGroup, CommandGroupLabel, CommandInput, CommandItem, CommandList, CommandPanel, CommandSeparator, CommandShortcut, DEFAULT_PRESETS, DatePickerPopover, type DatePickerPopoverProps, DestructiveActionConfirmation, DestructiveConfirmDialog, type DestructiveConfirmDialogProps, Dialog, DialogBackdrop, DialogBackdrop as DialogOverlay, DialogClose, DialogPopup as DialogContent, DialogPopup, DialogCreateHandle, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPortal, DialogTitle, DialogTrigger, DialogViewport, DirectionalIcon, DiscordLogoIcon, Menu as DropdownMenu, Menu, MenuCheckboxItem as DropdownMenuCheckboxItem, MenuCheckboxItem, MenuPopup as DropdownMenuContent, MenuPopup, MenuCreateHandle as DropdownMenuCreateHandle, MenuCreateHandle, MenuGroup as DropdownMenuGroup, MenuGroup, MenuItem as DropdownMenuItem, MenuItem, MenuGroupLabel as DropdownMenuLabel, MenuGroupLabel, MenuPortal as DropdownMenuPortal, MenuPortal, MenuRadioGroup as DropdownMenuRadioGroup, MenuRadioGroup, MenuRadioItem as DropdownMenuRadioItem, MenuRadioItem, MenuSeparator as DropdownMenuSeparator, MenuSeparator, MenuShortcut as DropdownMenuShortcut, MenuShortcut, MenuSub as DropdownMenuSub, MenuSub, MenuSubPopup as DropdownMenuSubContent, MenuSubPopup, MenuSubTrigger as DropdownMenuSubTrigger, MenuSubTrigger, MenuTrigger as DropdownMenuTrigger, MenuTrigger, Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldValidity, Form, Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle, GitHubLogoIcon, HexColorPicker, type HexColorPickerProps, PreviewCard as HoverCard, PreviewCard, PreviewCardPopup as HoverCardContent, PreviewCardPopup, PreviewCardTrigger as HoverCardTrigger, PreviewCardTrigger, 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, Input, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorDock, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTitle, Label, MenuPreviewLayout, OVERLAY_LAYER_CLASS_NAMES, OutlineItem, OutlineRail, OutlineRailProps, OverlayLayer, PROPERTY_ROW_GRID, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverClose, PopoverPopup as PopoverContent, PopoverPopup, PopoverCreateHandle, PopoverDescription, PopoverPanel, PopoverTitle, PopoverTrigger, PreviewCardPrimitive, PreviewPane, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, ScrollArea, ScrollBar, ScrollToTop, ScrollToTopProps, SecretInput, type SecretInputProps, SegmentedIconToggle, Select, SelectPopup as SelectContent, SelectPopup, SelectGroup, SelectGroupLabel, SelectItem, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetBackdrop, SheetBackdrop as SheetOverlay, SheetClose, SheetPopup as SheetContent, SheetPopup, SheetDescription, SheetFooter, SheetHeader, SheetPanel, SheetPortal, type SheetSide, SheetTitle, SheetTrigger, Skeleton, type SortDirection, SortableHead, StellaMark, StellaWordmarkLatin, TOAST_RIGHT_OFFSET_VAR, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsPanel as TabsContent, TabsPanel, TabsList, TabsTab, TabsTab as TabsTrigger, TextSeparator, Textarea, type TextareaProps, type ToastPosition, ToastProvider, Tooltip, TooltipPopup as TooltipContent, TooltipPopup, TooltipCreateHandle, TooltipProvider, TooltipTrigger, UserText, buttonAccessibleDisabledClass, buttonVariants, cn, composeRefs, containedEventHandler, containedHandler, contentDir, getFirstWeekday, getLocaleWeekInfo, getWeekendDays, isStructuredInputType, parsePersistedPaneWidth, resolveDragWidth, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, resolveKeyboardWidth, shouldForceSidebarCollapsed, stellaToast, useComboboxFilter, useContentDir, useDestructiveActionConfirmation, useInspectorPaneWidth, useIsMobile, useViewportWidth };
67
+ export { Accordion, AccordionPanel as AccordionContent, AccordionPanel, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogBackdrop, AlertDialogBackdrop as AlertDialogOverlay, AlertDialogClose, AlertDialogPopup as AlertDialogContent, AlertDialogPopup, AlertDialogCreateHandle, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPanel, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertDialogViewport, AnchoredToastProvider, Avatar, AvatarFallback, AvatarImage, type BidiDirection, BidiText, type BidiTextProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Checkbox, ColorPicker, ColorPickerContent, type ColorPickerContentProps, type ColorPickerProps, type ColorPreset, ColorVariants, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxClear, ComboboxCollection, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxList, ComboboxPopup, ComboboxRow, ComboboxSeparator, ComboboxStatus, ComboboxTrigger, ComboboxValue, Command, CommandCollection, CommandDialog, CommandDialogPopup, CommandDialogTrigger, CommandEmpty, CommandFooter, CommandGroup, CommandGroupLabel, CommandInput, CommandItem, CommandList, CommandPanel, CommandSeparator, CommandShortcut, DEFAULT_PRESETS, DatePickerPopover, type DatePickerPopoverProps, DestructiveActionConfirmation, DestructiveConfirmDialog, type DestructiveConfirmDialogProps, Dialog, DialogBackdrop, DialogBackdrop as DialogOverlay, DialogClose, DialogPopup as DialogContent, DialogPopup, DialogCreateHandle, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPortal, DialogTitle, DialogTrigger, DialogViewport, DirectionalIcon, DiscordLogoIcon, Menu as DropdownMenu, Menu, MenuCheckboxItem as DropdownMenuCheckboxItem, MenuCheckboxItem, MenuPopup as DropdownMenuContent, MenuPopup, MenuCreateHandle as DropdownMenuCreateHandle, MenuCreateHandle, MenuGroup as DropdownMenuGroup, MenuGroup, MenuItem as DropdownMenuItem, MenuItem, MenuGroupLabel as DropdownMenuLabel, MenuGroupLabel, MenuPortal as DropdownMenuPortal, MenuPortal, MenuRadioGroup as DropdownMenuRadioGroup, MenuRadioGroup, MenuRadioItem as DropdownMenuRadioItem, MenuRadioItem, MenuSeparator as DropdownMenuSeparator, MenuSeparator, MenuShortcut as DropdownMenuShortcut, MenuShortcut, MenuSub as DropdownMenuSub, MenuSub, MenuSubPopup as DropdownMenuSubContent, MenuSubPopup, MenuSubTrigger as DropdownMenuSubTrigger, MenuSubTrigger, MenuTrigger as DropdownMenuTrigger, MenuTrigger, Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldValidity, Form, Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle, GitHubLogoIcon, HexColorPicker, type HexColorPickerProps, PreviewCard as HoverCard, PreviewCard, PreviewCardPopup as HoverCardContent, PreviewCardPopup, PreviewCardTrigger as HoverCardTrigger, PreviewCardTrigger, 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, Input, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorDock, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTitle, type KanbanBuiltInGroup, type KanbanCardFieldSelection, KanbanCardShell, type KanbanCardShellProps, KanbanColumnHeader, type KanbanColumnHeaderProps, type KanbanGroup, type KanbanGroupOption, type KanbanGrouping, type KanbanSchema, Label, MenuPreviewLayout, OVERLAY_LAYER_CLASS_NAMES, OptionColor, OutlineItem, OutlineRail, OutlineRailProps, OverlayLayer, PROPERTY_ROW_GRID, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverClose, PopoverPopup as PopoverContent, PopoverPopup, PopoverCreateHandle, PopoverDescription, PopoverPanel, PopoverTitle, PopoverTrigger, PreviewCardPrimitive, PreviewPane, type ResolveKanbanGroupingParams, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, ScrollArea, ScrollBar, ScrollToTop, ScrollToTopProps, SecretInput, type SecretInputProps, SegmentedIconToggle, Select, SelectPopup as SelectContent, SelectPopup, SelectGroup, SelectGroupLabel, SelectItem, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetBackdrop, SheetBackdrop as SheetOverlay, SheetClose, SheetPopup as SheetContent, SheetPopup, SheetDescription, SheetFooter, SheetHeader, SheetPanel, SheetPortal, type SheetSide, SheetTitle, SheetTrigger, Skeleton, type SortDirection, SortableHead, StellaMark, StellaWordmarkLatin, TOAST_RIGHT_OFFSET_VAR, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX, Table, TableBody, TableCaption, TableCell, type TableColumnCapabilities, type TableColumnDescriptor, TableFooter, TableHead, TableHeader, TableRow, type TableSchema, Tabs, TabsPanel as TabsContent, TabsPanel, TabsList, TabsTab, TabsTab as TabsTrigger, TextSeparator, Textarea, type TextareaProps, type ToastPosition, ToastProvider, Tooltip, TooltipPopup as TooltipContent, TooltipPopup, TooltipCreateHandle, TooltipProvider, TooltipTrigger, UserText, buttonAccessibleDisabledClass, buttonVariants, cn, composeRefs, containedEventHandler, containedHandler, contentDir, duplicateColumnIds, emptyColor, findTableColumn, getFirstWeekday, getKanbanGroupingPropertyId, getKanbanGroups, getLocaleWeekInfo, getWeekendDays, hideableColumnIds, isKanbanGroupingRenderable, isStructuredInputType, optionColors, parsePersistedPaneWidth, resolveDragWidth, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, resolveKanbanGroupOptions, resolveKanbanGrouping, resolveKeyboardWidth, resolveOptionColor, selectKanbanCardFieldIds, selectKanbanRows, shouldForceSidebarCollapsed, sortableColumnIds, stellaToast, tableColumnIds, tableColumnSizing, useComboboxFilter, useContentDir, useDestructiveActionConfirmation, useInspectorPaneWidth, useIsMobile, useViewportWidth, visibleColumnIds };
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ import { StellaWordmarkLatin } from "./components/stella-wordmark.js";
49
49
  import { SortableHead, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "./components/table.js";
50
50
  import { Tabs, TabsContent as TabsPanel, TabsList, TabsTab } from "./components/tabs.js";
51
51
  import { AnchoredToastProvider, TOAST_RIGHT_OFFSET_VAR, ToastProvider, stellaToast } from "./components/toast.js";
52
+ import { duplicateColumnIds, findTableColumn, hideableColumnIds, sortableColumnIds, tableColumnIds, tableColumnSizing, visibleColumnIds } from "./data-table/schema.js";
52
53
  import { useIsMobile } from "./hooks/use-mobile.js";
53
54
  import { useViewportWidth } from "./hooks/use-viewport-width.js";
54
55
  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 "./inspector/layout-tokens.js";
@@ -56,4 +57,9 @@ import { Inspector, InspectorActions, InspectorContent, InspectorDescription, In
56
57
  import { InspectorDock } from "./inspector/dock.js";
57
58
  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 "./inspector/pane-width.js";
58
59
  import { INSPECTOR_PANE_KEYBOARD_PAGE_STEP, INSPECTOR_PANE_KEYBOARD_STEP, parsePersistedPaneWidth, resolveDragWidth, resolveKeyboardWidth, useInspectorPaneWidth } from "./inspector/use-pane-width.js";
59
- export { Accordion, AccordionPanel as AccordionContent, AccordionPanel, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogBackdrop, AlertDialogBackdrop as AlertDialogOverlay, AlertDialogClose, AlertDialogPopup as AlertDialogContent, AlertDialogPopup, AlertDialogCreateHandle, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPanel, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertDialogViewport, AnchoredToastProvider, Avatar, AvatarFallback, AvatarImage, BidiText, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Checkbox, ColorPicker, ColorPickerContent, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxClear, ComboboxCollection, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxList, ComboboxPopup, ComboboxRow, ComboboxSeparator, ComboboxStatus, ComboboxTrigger, ComboboxValue, Command, CommandCollection, CommandDialog, CommandDialogPopup, CommandDialogTrigger, CommandEmpty, CommandFooter, CommandGroup, CommandGroupLabel, CommandInput, CommandItem, CommandList, CommandPanel, CommandSeparator, CommandShortcut, DEFAULT_PRESETS, DatePickerPopover, DestructiveActionConfirmation, DestructiveConfirmDialog, Dialog, DialogBackdrop, DialogBackdrop as DialogOverlay, DialogClose, DialogPopup as DialogContent, DialogPopup, DialogCreateHandle, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPortal, DialogTitle, DialogTrigger, DialogViewport, DirectionalIcon, DiscordLogoIcon, Menu as DropdownMenu, Menu, MenuCheckboxItem as DropdownMenuCheckboxItem, MenuCheckboxItem, MenuPopup as DropdownMenuContent, MenuPopup, MenuCreateHandle as DropdownMenuCreateHandle, MenuCreateHandle, MenuGroup as DropdownMenuGroup, MenuGroup, MenuItem as DropdownMenuItem, MenuItem, MenuGroupLabel as DropdownMenuLabel, MenuGroupLabel, MenuPortal as DropdownMenuPortal, MenuPortal, MenuRadioGroup as DropdownMenuRadioGroup, MenuRadioGroup, MenuRadioItem as DropdownMenuRadioItem, MenuRadioItem, MenuSeparator as DropdownMenuSeparator, MenuSeparator, MenuShortcut as DropdownMenuShortcut, MenuShortcut, MenuSub as DropdownMenuSub, MenuSub, MenuSubPopup as DropdownMenuSubContent, MenuSubPopup, MenuSubTrigger as DropdownMenuSubTrigger, MenuSubTrigger, MenuTrigger as DropdownMenuTrigger, MenuTrigger, Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldValidity, Form, Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle, GitHubLogoIcon, HexColorPicker, PreviewCard as HoverCard, PreviewCard, PreviewCardPopup as HoverCardContent, PreviewCardPopup, PreviewCardTrigger as HoverCardTrigger, PreviewCardTrigger, 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, Input, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorDock, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTitle, Label, MenuPreviewLayout, OVERLAY_LAYER_CLASS_NAMES, OutlineRail, PROPERTY_ROW_GRID, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverClose, PopoverPopup as PopoverContent, PopoverPopup, PopoverCreateHandle, PopoverDescription, PopoverPanel, PopoverTitle, PopoverTrigger, PreviewCardPrimitive, PreviewPane, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, ScrollArea, ScrollBar, ScrollToTop, SecretInput, SegmentedIconToggle, Select, SelectPopup as SelectContent, SelectPopup, SelectGroup, SelectGroupLabel, SelectItem, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetBackdrop, SheetBackdrop as SheetOverlay, SheetClose, SheetPopup as SheetContent, SheetPopup, SheetDescription, SheetFooter, SheetHeader, SheetPanel, SheetPortal, SheetTitle, SheetTrigger, Skeleton, SortableHead, StellaMark, StellaWordmarkLatin, TOAST_RIGHT_OFFSET_VAR, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsPanel as TabsContent, TabsPanel, TabsList, TabsTab, TabsTab as TabsTrigger, TextSeparator, Textarea, ToastProvider, Tooltip, TooltipPopup as TooltipContent, TooltipPopup, TooltipCreateHandle, TooltipProvider, TooltipTrigger, UserText, buttonAccessibleDisabledClass, buttonVariants, cn, composeRefs, containedEventHandler, containedHandler, contentDir, getFirstWeekday, getLocaleWeekInfo, getWeekendDays, isStructuredInputType, parsePersistedPaneWidth, resolveDragWidth, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, resolveKeyboardWidth, shouldForceSidebarCollapsed, stellaToast, useComboboxFilter, useContentDir, useDestructiveActionConfirmation, useInspectorPaneWidth, useIsMobile, useViewportWidth };
60
+ import { selectKanbanCardFieldIds } from "./kanban/card-properties.js";
61
+ import { KanbanCardShell } from "./kanban/card-shell.js";
62
+ import { KanbanColumnHeader } from "./kanban/column-header.js";
63
+ import { getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows } from "./kanban/grouping.js";
64
+ import { emptyColor, optionColors, resolveOptionColor } from "./lib/option-color.js";
65
+ export { Accordion, AccordionPanel as AccordionContent, AccordionPanel, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogBackdrop, AlertDialogBackdrop as AlertDialogOverlay, AlertDialogClose, AlertDialogPopup as AlertDialogContent, AlertDialogPopup, AlertDialogCreateHandle, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogPanel, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertDialogViewport, AnchoredToastProvider, Avatar, AvatarFallback, AvatarImage, BidiText, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Checkbox, ColorPicker, ColorPickerContent, Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxClear, ComboboxCollection, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxList, ComboboxPopup, ComboboxRow, ComboboxSeparator, ComboboxStatus, ComboboxTrigger, ComboboxValue, Command, CommandCollection, CommandDialog, CommandDialogPopup, CommandDialogTrigger, CommandEmpty, CommandFooter, CommandGroup, CommandGroupLabel, CommandInput, CommandItem, CommandList, CommandPanel, CommandSeparator, CommandShortcut, DEFAULT_PRESETS, DatePickerPopover, DestructiveActionConfirmation, DestructiveConfirmDialog, Dialog, DialogBackdrop, DialogBackdrop as DialogOverlay, DialogClose, DialogPopup as DialogContent, DialogPopup, DialogCreateHandle, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPortal, DialogTitle, DialogTrigger, DialogViewport, DirectionalIcon, DiscordLogoIcon, Menu as DropdownMenu, Menu, MenuCheckboxItem as DropdownMenuCheckboxItem, MenuCheckboxItem, MenuPopup as DropdownMenuContent, MenuPopup, MenuCreateHandle as DropdownMenuCreateHandle, MenuCreateHandle, MenuGroup as DropdownMenuGroup, MenuGroup, MenuItem as DropdownMenuItem, MenuItem, MenuGroupLabel as DropdownMenuLabel, MenuGroupLabel, MenuPortal as DropdownMenuPortal, MenuPortal, MenuRadioGroup as DropdownMenuRadioGroup, MenuRadioGroup, MenuRadioItem as DropdownMenuRadioItem, MenuRadioItem, MenuSeparator as DropdownMenuSeparator, MenuSeparator, MenuShortcut as DropdownMenuShortcut, MenuShortcut, MenuSub as DropdownMenuSub, MenuSub, MenuSubPopup as DropdownMenuSubContent, MenuSubPopup, MenuSubTrigger as DropdownMenuSubTrigger, MenuSubTrigger, MenuTrigger as DropdownMenuTrigger, MenuTrigger, Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldValidity, Form, Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle, GitHubLogoIcon, HexColorPicker, PreviewCard as HoverCard, PreviewCard, PreviewCardPopup as HoverCardContent, PreviewCardPopup, PreviewCardTrigger as HoverCardTrigger, PreviewCardTrigger, 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, Input, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, InputGroupTextarea, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Inspector, InspectorActions, InspectorContent, InspectorDescription, InspectorDock, InspectorEmptyRow, InspectorHeader, InspectorHeaderText, InspectorProperty, InspectorPropertyLabel, InspectorPropertyList, InspectorPropertyValue, InspectorRail, InspectorRailCell, InspectorRailIconButton, InspectorRailTab, InspectorSection, InspectorSectionTitle, InspectorTitle, KanbanCardShell, KanbanColumnHeader, Label, MenuPreviewLayout, OVERLAY_LAYER_CLASS_NAMES, OutlineRail, PROPERTY_ROW_GRID, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, Popover, PopoverClose, PopoverPopup as PopoverContent, PopoverPopup, PopoverCreateHandle, PopoverDescription, PopoverPanel, PopoverTitle, PopoverTrigger, PreviewCardPrimitive, PreviewPane, SIDE_RAIL_CONTAINER_CLASS, SIDE_RAIL_ICON_BUTTON_SIZE, SIDE_RAIL_TAB_ICON_SIZE, SIDE_RAIL_WIDTH, ScrollArea, ScrollBar, ScrollToTop, SecretInput, SegmentedIconToggle, Select, SelectPopup as SelectContent, SelectPopup, SelectGroup, SelectGroupLabel, SelectItem, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetBackdrop, SheetBackdrop as SheetOverlay, SheetClose, SheetPopup as SheetContent, SheetPopup, SheetDescription, SheetFooter, SheetHeader, SheetPanel, SheetPortal, SheetTitle, SheetTrigger, Skeleton, SortableHead, StellaMark, StellaWordmarkLatin, TOAST_RIGHT_OFFSET_VAR, TOOLBAR_ROW_HEIGHT, TOOLBAR_ROW_HEIGHT_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsPanel as TabsContent, TabsPanel, TabsList, TabsTab, TabsTab as TabsTrigger, TextSeparator, Textarea, ToastProvider, Tooltip, TooltipPopup as TooltipContent, TooltipPopup, TooltipCreateHandle, TooltipProvider, TooltipTrigger, UserText, buttonAccessibleDisabledClass, buttonVariants, cn, composeRefs, containedEventHandler, containedHandler, contentDir, duplicateColumnIds, emptyColor, findTableColumn, getFirstWeekday, getKanbanGroupingPropertyId, getKanbanGroups, getLocaleWeekInfo, getWeekendDays, hideableColumnIds, isKanbanGroupingRenderable, isStructuredInputType, optionColors, parsePersistedPaneWidth, resolveDragWidth, resolveInspectorDockWidth, resolveInspectorPaneMaxWidth, resolveInspectorPaneWidth, resolveKanbanGroupOptions, resolveKanbanGrouping, resolveKeyboardWidth, resolveOptionColor, selectKanbanCardFieldIds, selectKanbanRows, shouldForceSidebarCollapsed, sortableColumnIds, stellaToast, tableColumnIds, tableColumnSizing, useComboboxFilter, useContentDir, useDestructiveActionConfirmation, useInspectorPaneWidth, useIsMobile, useViewportWidth, visibleColumnIds };
@@ -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 };
@@ -0,0 +1,23 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/kanban/column-header.d.ts
3
+ type KanbanColumnHeaderProps = {
4
+ /** Colour swatch, or the control that changes it. */
5
+ swatch?: ReactNode;
6
+ /** The column name, or the editor that has taken its place. */
7
+ title: ReactNode;
8
+ /** Short text after the name, such as the card count the caller formatted. */
9
+ meta?: ReactNode;
10
+ /** Column calculation, rendered after the count. */
11
+ calculation?: ReactNode;
12
+ /** Drag affordance, revealed on hover over the column. */
13
+ dragHandle?: ReactNode;
14
+ /** Column menu. */
15
+ actions?: ReactNode;
16
+ };
17
+ /**
18
+ * The column header row: one rhythm for the swatch, the name, the count, the
19
+ * calculation, and the column's controls, so every board's header lines up.
20
+ */
21
+ declare const KanbanColumnHeader: ({ swatch, title, meta, calculation, dragHandle, actions }: KanbanColumnHeaderProps) => import("react").JSX.Element;
22
+ //#endregion
23
+ export { KanbanColumnHeader, KanbanColumnHeaderProps };
@@ -0,0 +1,24 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ //#region src/kanban/column-header.tsx
3
+ /**
4
+ * The column header row: one rhythm for the swatch, the name, the count, the
5
+ * calculation, and the column's controls, so every board's header lines up.
6
+ */
7
+ const KanbanColumnHeader = ({ swatch, title, meta, calculation, dragHandle, actions }) => /* @__PURE__ */ jsxs("div", {
8
+ className: "flex items-center gap-2 px-3 py-2",
9
+ children: [
10
+ swatch,
11
+ /* @__PURE__ */ jsxs("span", {
12
+ className: "flex min-w-0 flex-1 items-center gap-1.5 truncate",
13
+ children: [title, meta !== void 0 && /* @__PURE__ */ jsx("span", {
14
+ className: "text-muted-foreground text-xs",
15
+ children: meta
16
+ })]
17
+ }),
18
+ calculation,
19
+ dragHandle,
20
+ actions
21
+ ]
22
+ });
23
+ //#endregion
24
+ export { KanbanColumnHeader };
@@ -0,0 +1,84 @@
1
+ import { OptionColor } from "../lib/option-color.js";
2
+ //#region src/kanban/grouping.d.ts
3
+ /** One column, before the uncategorized bucket is appended. */
4
+ type KanbanGroupOption = {
5
+ value: string;
6
+ label: string;
7
+ color?: string | undefined;
8
+ colorBg?: string | undefined;
9
+ optionColor?: OptionColor | undefined;
10
+ };
11
+ /** A column, including the uncategorized bucket (`value: null`). */
12
+ type KanbanGroup = {
13
+ value: string | null;
14
+ label: string;
15
+ color?: string | undefined;
16
+ colorBg?: string | undefined;
17
+ optionColor?: OptionColor | undefined;
18
+ };
19
+ /**
20
+ * A column source that is not a schema property, addressed by a reserved id.
21
+ *
22
+ * The board's columns are the caller's `options`, in the caller's order, so a
23
+ * built-in grouping is a data declaration rather than a branch this module has
24
+ * to know about.
25
+ */
26
+ type KanbanBuiltInGroup<TRow> = {
27
+ /** Reserved group id, distinct from any property id. */
28
+ id: string;
29
+ /** Ordered columns, without the uncategorized bucket. */
30
+ options: readonly KanbanGroupOption[];
31
+ /**
32
+ * Narrows the board to the rows this grouping can place. Omit when every row
33
+ * belongs on the board.
34
+ */
35
+ selectRows?: ((rows: readonly TRow[]) => TRow[]) | undefined;
36
+ };
37
+ /**
38
+ * Everything the grouping needs to know about a board.
39
+ *
40
+ * `getPropertyOptions` returns `null` for a property that cannot carry columns,
41
+ * which keeps the decision — and the property model it depends on — with the
42
+ * caller.
43
+ */
44
+ type KanbanSchema<TRow, TProperty> = {
45
+ builtInGroups: readonly KanbanBuiltInGroup<TRow>[];
46
+ properties: readonly TProperty[];
47
+ getPropertyId: (property: TProperty) => string;
48
+ getPropertyOptions: (property: TProperty) => readonly KanbanGroupOption[] | null;
49
+ };
50
+ type KanbanGrouping<TRow, TProperty> = {
51
+ type: "none";
52
+ } | {
53
+ type: "built-in";
54
+ propertyId: string;
55
+ group: KanbanBuiltInGroup<TRow>;
56
+ } | {
57
+ type: "property";
58
+ propertyId: string;
59
+ property: TProperty;
60
+ options: readonly KanbanGroupOption[];
61
+ };
62
+ type ResolveKanbanGroupingParams<TRow, TProperty> = {
63
+ /** The group-by id the view carries; empty means no grouping. */
64
+ groupBy: string;
65
+ schema: KanbanSchema<TRow, TProperty>;
66
+ };
67
+ /** Resolve a stored group-by id against a schema. */
68
+ declare const resolveKanbanGrouping: <TRow, TProperty>({ groupBy, schema }: ResolveKanbanGroupingParams<TRow, TProperty>) => KanbanGrouping<TRow, TProperty>;
69
+ declare const getKanbanGroupingPropertyId: <TRow, TProperty>(grouping: KanbanGrouping<TRow, TProperty>) => string | null;
70
+ /**
71
+ * A grouping with no columns is not a board. A built-in grouping declares its
72
+ * columns up front, so an empty declaration is the signal that the board cannot
73
+ * be drawn; a property grouping always draws, even when the property has no
74
+ * options yet, because rows still land in the uncategorized bucket.
75
+ */
76
+ declare const isKanbanGroupingRenderable: <TRow, TProperty>(grouping: KanbanGrouping<TRow, TProperty>) => boolean;
77
+ /** The rows a grouping can place on the board, in their incoming order. */
78
+ declare const selectKanbanRows: <TRow, TProperty>(rows: readonly TRow[], grouping: KanbanGrouping<TRow, TProperty>) => TRow[];
79
+ /** The static column options for a grouping, excluding uncategorized. */
80
+ declare const resolveKanbanGroupOptions: <TRow, TProperty>(grouping: KanbanGrouping<TRow, TProperty>) => readonly KanbanGroupOption[];
81
+ /** Append the uncategorized bucket (null value) after the options. */
82
+ declare const getKanbanGroups: (options: readonly KanbanGroupOption[], uncategorizedLabel: string) => KanbanGroup[];
83
+ //#endregion
84
+ export { KanbanBuiltInGroup, KanbanGroup, KanbanGroupOption, KanbanGrouping, KanbanSchema, ResolveKanbanGroupingParams, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows };
@@ -0,0 +1,76 @@
1
+ //#region src/kanban/grouping.ts
2
+ /** Resolve a stored group-by id against a schema. */
3
+ const resolveKanbanGrouping = ({ groupBy, schema }) => {
4
+ if (groupBy === "") return { type: "none" };
5
+ const builtIn = schema.builtInGroups.find((group) => group.id === groupBy);
6
+ if (builtIn !== void 0) return {
7
+ type: "built-in",
8
+ propertyId: groupBy,
9
+ group: builtIn
10
+ };
11
+ const property = schema.properties.find((candidate) => schema.getPropertyId(candidate) === groupBy);
12
+ if (property === void 0) return { type: "none" };
13
+ return {
14
+ type: "property",
15
+ propertyId: groupBy,
16
+ property,
17
+ options: schema.getPropertyOptions(property) ?? []
18
+ };
19
+ };
20
+ const getKanbanGroupingPropertyId = (grouping) => {
21
+ switch (grouping.type) {
22
+ case "none": return null;
23
+ case "built-in":
24
+ case "property": return grouping.propertyId;
25
+ default: return grouping;
26
+ }
27
+ };
28
+ /**
29
+ * A grouping with no columns is not a board. A built-in grouping declares its
30
+ * columns up front, so an empty declaration is the signal that the board cannot
31
+ * be drawn; a property grouping always draws, even when the property has no
32
+ * options yet, because rows still land in the uncategorized bucket.
33
+ */
34
+ const isKanbanGroupingRenderable = (grouping) => {
35
+ switch (grouping.type) {
36
+ case "none": return false;
37
+ case "built-in": return grouping.group.options.length > 0;
38
+ case "property": return true;
39
+ default: return grouping;
40
+ }
41
+ };
42
+ /** The rows a grouping can place on the board, in their incoming order. */
43
+ const selectKanbanRows = (rows, grouping) => {
44
+ switch (grouping.type) {
45
+ case "none": return [];
46
+ case "built-in": return grouping.group.selectRows ? grouping.group.selectRows(rows) : [...rows];
47
+ case "property": return [...rows];
48
+ default: return grouping;
49
+ }
50
+ };
51
+ /** The static column options for a grouping, excluding uncategorized. */
52
+ const resolveKanbanGroupOptions = (grouping) => {
53
+ switch (grouping.type) {
54
+ case "none": return [];
55
+ case "built-in": return grouping.group.options;
56
+ case "property": return grouping.options;
57
+ default: return grouping;
58
+ }
59
+ };
60
+ /** Append the uncategorized bucket (null value) after the options. */
61
+ const getKanbanGroups = (options, uncategorizedLabel) => {
62
+ const result = options.map((option) => ({
63
+ value: option.value,
64
+ label: option.label,
65
+ color: option.color,
66
+ colorBg: option.colorBg,
67
+ optionColor: option.optionColor
68
+ }));
69
+ result.push({
70
+ value: null,
71
+ label: uncategorizedLabel
72
+ });
73
+ return result;
74
+ };
75
+ //#endregion
76
+ export { getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows };
@@ -0,0 +1,5 @@
1
+ import { KanbanCardFieldSelection, selectKanbanCardFieldIds } from "./card-properties.js";
2
+ import { KanbanCardShell, KanbanCardShellProps } from "./card-shell.js";
3
+ import { KanbanColumnHeader, KanbanColumnHeaderProps } from "./column-header.js";
4
+ import { KanbanBuiltInGroup, KanbanGroup, KanbanGroupOption, KanbanGrouping, KanbanSchema, ResolveKanbanGroupingParams, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows } from "./grouping.js";
5
+ export { type KanbanBuiltInGroup, type KanbanCardFieldSelection, KanbanCardShell, type KanbanCardShellProps, KanbanColumnHeader, type KanbanColumnHeaderProps, type KanbanGroup, type KanbanGroupOption, type KanbanGrouping, type KanbanSchema, type ResolveKanbanGroupingParams, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanCardFieldIds, selectKanbanRows };
@@ -0,0 +1,5 @@
1
+ import { selectKanbanCardFieldIds } from "./card-properties.js";
2
+ import { KanbanCardShell } from "./card-shell.js";
3
+ import { KanbanColumnHeader } from "./column-header.js";
4
+ import { getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanRows } from "./grouping.js";
5
+ export { KanbanCardShell, KanbanColumnHeader, getKanbanGroupingPropertyId, getKanbanGroups, isKanbanGroupingRenderable, resolveKanbanGroupOptions, resolveKanbanGrouping, selectKanbanCardFieldIds, selectKanbanRows };
@@ -0,0 +1,28 @@
1
+ //#region src/lib/option-color.d.ts
2
+ /**
3
+ * The kit's colour token for user-chosen option colours.
4
+ *
5
+ * A token is either one of the sixteen named presets, which resolve to the
6
+ * theme's `--option-*` custom properties, or a six-character hex string, which
7
+ * resolves to a `color-mix` against the current background and foreground. The
8
+ * indirection is what keeps a stored colour theme-aware: nothing persists a
9
+ * literal colour the palette cannot re-tint.
10
+ *
11
+ * This lives in the kit rather than beside a data model because the resolver is
12
+ * pure presentation: it maps a token to CSS, and knows nothing about what the
13
+ * token was chosen for.
14
+ */
15
+ /** Named preset or arbitrary 6-character hex colour (e.g. "FF0000"). */
16
+ type OptionColor = "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "gray" | (string & Record<never, never>);
17
+ type ColorVariants = {
18
+ background: string;
19
+ foreground: string;
20
+ color: string;
21
+ };
22
+ declare const emptyColor: ColorVariants;
23
+ /** Resolve any OptionColor (named or hex) to CSS color variants. */
24
+ declare const resolveOptionColor: (color: OptionColor) => ColorVariants;
25
+ /** The 16 named preset color keys. */
26
+ declare const optionColors: readonly OptionColor[];
27
+ //#endregion
28
+ export { ColorVariants, OptionColor, emptyColor, optionColors, resolveOptionColor };
@@ -0,0 +1,59 @@
1
+ //#region src/lib/option-color.ts
2
+ /** Named preset colors with CSS variable references. */
3
+ const NAMED_COLORS = Object.freeze([
4
+ "red",
5
+ "orange",
6
+ "amber",
7
+ "yellow",
8
+ "lime",
9
+ "green",
10
+ "emerald",
11
+ "teal",
12
+ "cyan",
13
+ "sky",
14
+ "blue",
15
+ "indigo",
16
+ "violet",
17
+ "purple",
18
+ "fuchsia",
19
+ "gray"
20
+ ]);
21
+ const optionVar = (name) => ({
22
+ background: `var(--option-${name}-bg)`,
23
+ foreground: `var(--option-${name}-fg)`,
24
+ color: `var(--option-${name})`
25
+ });
26
+ const hexVar = (hex) => ({
27
+ background: `color-mix(in srgb, #${hex} 12%, var(--background))`,
28
+ foreground: `color-mix(in srgb, #${hex} 50%, var(--foreground))`,
29
+ color: `#${hex}`
30
+ });
31
+ const namedColorsMap = {
32
+ red: optionVar("red"),
33
+ orange: optionVar("orange"),
34
+ amber: optionVar("amber"),
35
+ yellow: optionVar("yellow"),
36
+ lime: optionVar("lime"),
37
+ green: optionVar("green"),
38
+ emerald: optionVar("emerald"),
39
+ teal: optionVar("teal"),
40
+ cyan: optionVar("cyan"),
41
+ sky: optionVar("sky"),
42
+ blue: optionVar("blue"),
43
+ indigo: optionVar("indigo"),
44
+ violet: optionVar("violet"),
45
+ purple: optionVar("purple"),
46
+ fuchsia: optionVar("fuchsia"),
47
+ gray: optionVar("gray")
48
+ };
49
+ const emptyColor = optionVar("empty");
50
+ /** Resolve any OptionColor (named or hex) to CSS color variants. */
51
+ const resolveOptionColor = (color) => {
52
+ const namedColor = NAMED_COLORS.find((candidate) => candidate === color);
53
+ if (namedColor !== void 0) return namedColorsMap[namedColor];
54
+ return hexVar(color);
55
+ };
56
+ /** The 16 named preset color keys. */
57
+ const optionColors = NAMED_COLORS;
58
+ //#endregion
59
+ export { emptyColor, optionColors, resolveOptionColor };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/ui",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Stella's design system: bidi-aware React primitives built on Base UI, the dockable inspector pane, and the Tailwind v4 theme they are styled with.",
5
5
  "keywords": [
6
6
  "base-ui",
@@ -81,6 +81,10 @@
81
81
  "types": "./dist/components/command.d.ts",
82
82
  "import": "./dist/components/command.js"
83
83
  },
84
+ "./data-table": {
85
+ "types": "./dist/data-table/index.d.ts",
86
+ "import": "./dist/data-table/index.js"
87
+ },
84
88
  "./date-picker-popover": {
85
89
  "types": "./dist/components/date-picker-popover.d.ts",
86
90
  "import": "./dist/components/date-picker-popover.js"
@@ -137,6 +141,10 @@
137
141
  "types": "./dist/components/menu.d.ts",
138
142
  "import": "./dist/components/menu.js"
139
143
  },
144
+ "./option-color": {
145
+ "types": "./dist/lib/option-color.d.ts",
146
+ "import": "./dist/lib/option-color.js"
147
+ },
140
148
  "./outline-rail": {
141
149
  "types": "./dist/components/outline-rail.d.ts",
142
150
  "import": "./dist/components/outline-rail.js"
@@ -249,6 +257,10 @@
249
257
  "types": "./dist/inspector/index.d.ts",
250
258
  "import": "./dist/inspector/index.js"
251
259
  },
260
+ "./kanban": {
261
+ "types": "./dist/kanban/index.d.ts",
262
+ "import": "./dist/kanban/index.js"
263
+ },
252
264
  "./theme.css": "./dist/styles/theme.css",
253
265
  "./components/accordion": {
254
266
  "types": "./dist/components/accordion.d.ts",