@parallelworks/ui 0.84.1 → 0.85.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.
@@ -1,5 +1,6 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { ErrorIcon } from "../icons.js";
3
+ import { Tooltip } from "../components/Tooltip.js";
3
4
  import { useStrings } from "../components/Provider.js";
4
5
  import { AppLink } from "../components/AppLink.js";
5
6
  import { positionKeys } from "../components/keys.js";
@@ -95,32 +96,18 @@ function NameCell({ icon, iconTile = false, name, subtitle, href, badge, title,
95
96
  }
96
97
  /** A row's column text: muted 13px, or monospace for an ID, a path or a
97
98
  * hash. The style sits on the text rather than the cell, so it carries over
98
- * when a narrow list stacks the column under the name. */
99
+ * when a narrow list stacks the column under the name. Text wider than its
100
+ * column is cut off with an ellipsis, and shows whole in a tooltip. */
99
101
  function ListCellText({ mono = false, className, children }) {
100
- return /* @__PURE__ */ jsx("span", {
101
- className: cx("text-(--theme-muted-text-color)", mono ? "font-mono text-xs" : "text-[13px]", className),
102
+ const text = /* @__PURE__ */ jsx("span", {
103
+ className: cx("block min-w-0 truncate text-(--theme-muted-text-color)", mono ? "font-mono text-xs" : "text-[13px]", className),
102
104
  children
103
105
  });
104
- }
105
- function GroupRow({ label, count, colSpan }) {
106
- return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", {
107
- colSpan,
108
- className: "bg-(--theme-muted-panel-bg) py-1.5 text-xs font-medium",
109
- children: [
110
- /* @__PURE__ */ jsx("span", {
111
- className: "text-(--theme-app)",
112
- children: label
113
- }),
114
- /* @__PURE__ */ jsx("span", {
115
- className: "mx-1.5 opacity-50",
116
- children: "·"
117
- }),
118
- /* @__PURE__ */ jsx("span", {
119
- className: "text-(--theme-muted-text-color)",
120
- children: count
121
- })
122
- ]
123
- }) });
106
+ return typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsx(Tooltip, {
107
+ content: children,
108
+ overflowOnly: true,
109
+ children: text
110
+ }) : text;
124
111
  }
125
112
  /** Says a list couldn't load. It never shows the error's own text, which is
126
113
  * the server's words, in its language and not written for the reader. */
@@ -228,7 +215,7 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
228
215
  const { common } = useStrings();
229
216
  if (view && !view.showActions) return null;
230
217
  const width = view ? view.actionsWidth : actionsColumnWidth(actionCount);
231
- const floating = !!view && !view.alwaysShowActions && view.actionsWidth === actionsColumnWidth(0);
218
+ const floating = !!view && !view.alwaysShowActions && !view.lastColumnLocked && view.actionsWidth === actionsColumnWidth(0);
232
219
  return /* @__PURE__ */ jsx(Table.Header, {
233
220
  caps: false,
234
221
  style: {
@@ -244,4 +231,4 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
244
231
  }
245
232
  ListActionsHeader.isTableHeader = true;
246
233
  //#endregion
247
- export { FLOATING_ACTIONS_LANE, GroupRow, HEADERS_OFF, ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, STRETCHED_LINK, STRETCHED_LINK_ROW, StretchRowLinkContext, actionsColumnWidth, listTableFixedProps, listTableProps };
234
+ export { FLOATING_ACTIONS_LANE, HEADERS_OFF, ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, STRETCHED_LINK, STRETCHED_LINK_ROW, StretchRowLinkContext, actionsColumnWidth, listTableFixedProps, listTableProps };
@@ -1,4 +1,4 @@
1
- import type { ListView } from './listView.js';
1
+ import { type ListView } from './listView.js';
2
2
  import type { ListFilters } from './useListFilters.js';
3
3
  export declare function ListDisplayMenu<T>({ view, label }: {
4
4
  view: ListView<T>;
@@ -10,6 +10,7 @@ import { InfoTip } from "../components/InfoTip.js";
10
10
  import { Switch } from "../components/Switch.js";
11
11
  import { Select } from "../components/Select.js";
12
12
  import { AddFilterMenu } from "./ListFilterBar.js";
13
+ import { isLocked } from "./listView.js";
13
14
  import { Menu } from "@base-ui/react/menu";
14
15
  import { useState } from "react";
15
16
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -20,7 +21,7 @@ var pillClassName = "whitespace-nowrap rounded-full border border-(--theme-borde
20
21
  var SECTION_LABEL = "mb-2 text-[11px] font-medium uppercase tracking-wide text-(--theme-muted-text-color)";
21
22
  function ListDisplayMenu({ view, label }) {
22
23
  const { common, list: t } = useStrings();
23
- const toggleable = view.columns.filter((c) => !c.alwaysVisible);
24
+ const toggleable = view.columns.filter((c) => !isLocked(c));
24
25
  const hasGrouping = view.groupBys.length > 0;
25
26
  const orderDirLabel = view.orderDir === "asc" ? t.ascending : t.descending;
26
27
  return /* @__PURE__ */ jsxs(Popover, {
@@ -1,8 +1,8 @@
1
1
  export { ActionToolbar, type ToolbarAction, ToolbarButton, type ToolbarItem, } from './ActionToolbar.js';
2
2
  export { type ActiveFilter, applyFilterList, DATE_PRESETS, type DatePreset, dateFilterRange, decodeFilters, encodeFilters, type FilterField, type FilterKind, type FilterOperator, type FilterOption, type FilterRowValue, filterParamKeys, matchFilter, sameFilters, withFilterParams, } from './filterModel.js';
3
3
  export { AddFilterMenu, type AddFilterMenuProps, FilterChip, ListFilterBar, type ListFilterBarProps, } from './ListFilterBar.js';
4
- export { ListColumns, ListPage, type ListPageEmpty, type ListPageProps, ListRow, ListRowActionsProvider, useListNavigate, } from './ListPage.js';
5
- export { actionsColumnWidth, GroupRow, ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, listTableFixedProps, listTableProps, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, } from './ListTable.js';
4
+ export { GroupRow, ListColumns, ListPage, type ListPageEmpty, type ListPageProps, ListRow, ListRowActionsProvider, useListNavigate, } from './ListPage.js';
5
+ export { actionsColumnWidth, ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, listTableFixedProps, listTableProps, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, } from './ListTable.js';
6
6
  export { ListToolbar, type ListToolbarProps } from './ListToolbar.js';
7
7
  export { ListDisplayMenu, ListFilterMenu } from './ListViewControls.js';
8
8
  export { ListCount, type ListSearch, ListSearchControl, useListSearch, } from './listSearch.js';
@@ -1,13 +1,13 @@
1
1
  import { ActionToolbar, ToolbarButton } from "./ActionToolbar.js";
2
2
  import { DATE_PRESETS, applyFilterList, dateFilterRange, decodeFilters, encodeFilters, filterParamKeys, matchFilter, sameFilters, withFilterParams } from "./filterModel.js";
3
3
  import { AddFilterMenu, FilterChip, ListFilterBar } from "./ListFilterBar.js";
4
- import { GroupRow, ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps } from "./ListTable.js";
4
+ import { ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps } from "./ListTable.js";
5
5
  import { MoreButton, assembleRowMenu, rowContextMenuProps, useCopySubmenu, useRowMenuActive } from "./rowMenu.js";
6
- import { ListColumns, ListPage, ListRow, ListRowActionsProvider, useListNavigate } from "./ListPage.js";
6
+ import { GroupRow, ListColumns, ListPage, ListRow, ListRowActionsProvider, useListNavigate } from "./ListPage.js";
7
+ import { useListFilters } from "./useListFilters.js";
8
+ import { DEFAULT_ORDER, FAVORITE_ORDER, useListView } from "./listView.js";
7
9
  import { ListDisplayMenu, ListFilterMenu } from "./ListViewControls.js";
8
10
  import { ListCount, ListSearchControl, useListSearch } from "./listSearch.js";
9
11
  import { ListToolbar } from "./ListToolbar.js";
10
- import { useListFilters } from "./useListFilters.js";
11
- import { DEFAULT_ORDER, FAVORITE_ORDER, useListView } from "./listView.js";
12
12
  import { HoverCardRow, UserHoverCard } from "./UserHoverCard.js";
13
13
  export { ActionToolbar, AddFilterMenu, DATE_PRESETS, DEFAULT_ORDER, FAVORITE_ORDER, FilterChip, GroupRow, HoverCardRow, ListActionsHeader, ListCellText, ListColumns, ListCount, ListDisplayMenu, ListError, ListFilterBar, ListFilterMenu, ListPage, ListRow, ListRowActionsProvider, ListSearchControl, ListSelectAllHeader, ListSkeleton, ListToolbar, MoreButton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, ToolbarButton, UserHoverCard, actionsColumnWidth, applyFilterList, assembleRowMenu, dateFilterRange, decodeFilters, encodeFilters, filterParamKeys, listTableFixedProps, listTableProps, matchFilter, rowContextMenuProps, sameFilters, useCopySubmenu, useListFilters, useListNavigate, useListSearch, useListView, useRowMenuActive, withFilterParams };
@@ -7,8 +7,13 @@ export interface ColumnDef {
7
7
  key: string;
8
8
  label: string;
9
9
  headerClassName?: string;
10
- /** Pinned on — never offered in the column picker (e.g. the Name column). */
10
+ /** Pinned on — never offered in the column picker — and the list's name
11
+ * column, which keeps a minimum width so it can't be crushed. */
11
12
  alwaysVisible?: boolean;
13
+ /** Always shown and never offered in the column picker, at its own width:
14
+ * for a column such as a delete button's, which `alwaysVisible` would widen
15
+ * to a name's minimum. */
16
+ locked?: boolean;
12
17
  /** Hidden until the user turns it on in the Display menu. */
13
18
  defaultHidden?: boolean;
14
19
  /** NOT a table column (no header) — appears in picker and isVisible, but excluded from visibleColumns. */
@@ -25,12 +30,21 @@ export interface ColumnDef {
25
30
  * then sorts the list: it shows, and sets, the same order as Display
26
31
  * options' Ordering. */
27
32
  orderBy?: string;
33
+ /** The column's width, such as `'12%'` or `128`, set on its header. In a
34
+ * fixed-layout list (`listTableFixedProps`), where every column without one
35
+ * takes an equal share, give the other columns a width each and leave the
36
+ * name without one, so it takes what they leave. A percentage is a share of
37
+ * the list: when narrower widths drop columns, the shares they held go to
38
+ * the columns left, each in proportion to its own, the name's among them. */
39
+ width?: string | number;
28
40
  /** `end` sets the header and the cells against the column's far edge, for
29
41
  * figures, sizes and times that compare best lined up on their last digit. */
30
42
  align?: 'start' | 'end';
31
43
  }
32
44
  /** Column responsive importance; `low` drops before `medium`. See {@link ColumnDef.priority}. */
33
45
  type ColumnPriority = 'low' | 'medium';
46
+ /** Whether a column is always shown and kept out of the column picker. */
47
+ export declare function isLocked(col: ColumnDef): boolean;
34
48
  interface GroupByOption {
35
49
  value: string;
36
50
  label: string;
@@ -113,6 +127,10 @@ export interface ListViewChrome {
113
127
  /** Per visible column, whether it stacks under the first on a narrow
114
128
  * container ({@link ColumnDef.stack}); `ListRow` reads entry `i`. */
115
129
  columnStacks: boolean[];
130
+ /** Whether the last column is a locked one, such as a delete button's: the
131
+ * row's `⋯` then keeps a lane of its own after it rather than floating over
132
+ * it on hover. */
133
+ lastColumnLocked?: boolean | undefined;
116
134
  /** The list's filters, which `ListPage` shows as a row of chips under its toolbar. */
117
135
  filterState?: ListFilters | undefined;
118
136
  }
@@ -11,6 +11,10 @@ var PRIORITY_SHOW_CLASS = {
11
11
  function stacks(col, i) {
12
12
  return i > 0 && !col.priority && col.stack !== false;
13
13
  }
14
+ /** Whether a column is always shown and kept out of the column picker. */
15
+ function isLocked(col) {
16
+ return Boolean(col.alwaysVisible || col.locked);
17
+ }
14
18
  function columnResponsiveClass(col, i) {
15
19
  if (stacks(col, i)) return PRIORITY_SHOW_CLASS.medium;
16
20
  return col.priority ? PRIORITY_SHOW_CLASS[col.priority] : "";
@@ -55,7 +59,7 @@ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], def
55
59
  ]);
56
60
  const defaultGroupByValue = defaultGroupBy ?? groupBys[0]?.value ?? "";
57
61
  const defaultOrderByValue = isFavorite ? DEFAULT_ORDER : defaultOrderBy ?? orderBys[0]?.value ?? "";
58
- const defaultHiddenKeys = useMemo(() => columns.filter((c) => c.defaultHidden && !c.alwaysVisible).map((c) => c.key), [columns]);
62
+ const defaultHiddenKeys = useMemo(() => columns.filter((c) => c.defaultHidden && !isLocked(c)).map((c) => c.key), [columns]);
59
63
  const defaultPinnedKeys = useMemo(() => actions.filter((a) => a.defaultPinned).map((a) => a.key), [actions]);
60
64
  const [hiddenKeys, setHiddenKeys] = usePersistedState(`${storageKey}:hiddenColumns`, defaultHiddenKeys);
61
65
  const [pinnedKeys, setPinnedKeys] = usePersistedState(`${storageKey}:pinnedActions`, defaultPinnedKeys);
@@ -84,11 +88,13 @@ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], def
84
88
  const actionsWidth = effectiveShowActions ? actionsColumnWidth(pinnedCount) : 0;
85
89
  const isVisible = useCallback((key) => {
86
90
  const col = columns.find((c) => c.key === key);
87
- return Boolean(col?.alwaysVisible) || !hiddenKeys.includes(key);
91
+ return col !== void 0 && isLocked(col) || !hiddenKeys.includes(key);
88
92
  }, [columns, hiddenKeys]);
89
- const visibleColumns = useMemo(() => columns.filter((c) => !c.inline && (c.alwaysVisible || !hiddenKeys.includes(c.key))), [columns, hiddenKeys]);
93
+ const visibleColumns = useMemo(() => columns.filter((c) => !c.inline && (isLocked(c) || !hiddenKeys.includes(c.key))), [columns, hiddenKeys]);
90
94
  const columnClasses = useMemo(() => visibleColumns.map(columnClass), [visibleColumns]);
91
95
  const columnStacks = useMemo(() => visibleColumns.map(stacks), [visibleColumns]);
96
+ const last = visibleColumns.at(-1);
97
+ const lastColumnLocked = visibleColumns.length > 1 && last !== void 0 && !last.alwaysVisible && isLocked(last);
92
98
  const toggleColumn = useCallback((key) => setHiddenKeys((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]), [setHiddenKeys]);
93
99
  const { add, update, remove } = filterState;
94
100
  const setFacetValues = useCallback((key, values) => {
@@ -183,6 +189,7 @@ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], def
183
189
  visibleColumns,
184
190
  columnClasses,
185
191
  columnStacks,
192
+ lastColumnLocked,
186
193
  isVisible,
187
194
  pinnedCount,
188
195
  actionsWidth,
@@ -230,4 +237,4 @@ function facetField(facet) {
230
237
  };
231
238
  }
232
239
  //#endregion
233
- export { DEFAULT_ORDER, FAVORITE_ORDER, useListView };
240
+ export { DEFAULT_ORDER, FAVORITE_ORDER, isLocked, useListView };