@x-tech-laboratories/x-libs 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,7 +47,7 @@ Code the X-apps share, published as one npm package, `@x-tech-laboratories/x-lib
47
47
  | `web/react/menu_button` | `MenuButton`: a ⋮ button that opens a menu under itself, even inside a link, saying through `aria-expanded` whether it is open. |
48
48
  | `web/react/test_stubs` | Stands in for `showPopover`, `hidePopover`, `showModal`, `close`, `matchMedia` and `scrollTo`, which jsdom lacks; an app's widget tests import it for its side effects, and a node test's shared setup may import it too. |
49
49
  | `web/react/list_picker` | `pick_list_dialog`: adds one thing to any of the app's lists, such as a video to playlists, ticking the lists that hold it and making a new one. |
50
- | `web/react/controls`, `table` | Segmented choices, button groups, switches, search boxes that hand on typing as it pauses, submit on Enter or a button, clear on Escape, can take a paste whole, take focus and follow a value cleared from outside, selects, `CommitInput`, `DateTimeField`, `PathInput` with a Browse button on the app's picker, `ColorPicker`, `TreeSelect` opening an indented card that shows the picked path; `DataTable` on TanStack Table, sorting by several columns, with columns to move, resize, hide and filter from buttons pinned over the heading band, tree rows, groups under headings, rows the app drags and styles, rows that come sorted from a database, rows worked by keyboard, rows that draw again only when they change, `virtual` rows drawn only where they show, a last column that fills the room left, group headings that hide when the filter empties them, a `wrap_class` for Halon's `scroll` and `sticky-head`, and cards on phones; a pager. |
50
+ | `web/react/controls`, `table` | Segmented choices, button groups, switches, search boxes that hand on typing as it pauses, submit on Enter or a button, clear on Escape, can take a paste whole, take focus and follow a value cleared from outside, selects, `CommitInput`, `DateTimeField`, `PathInput` with a Browse button on the app's picker, `ColorPicker`, `TreeSelect` opening an indented card that shows the picked path; `DataTable` on TanStack Table, sorting by several columns, with columns to move, resize, hide and filter from buttons pinned over the heading band, a `header` such as an icon shown in place of a column's label, tree rows, groups under headings, rows the app drags and styles, rows that come sorted from a database, rows worked by keyboard, rows that draw again only when they change, `virtual` rows drawn only where they show, a last column that fills the room left, group headings that hide when the filter empties them, a `wrap_class` for Halon's `scroll` and `sticky-head`, and cards on phones; a pager. |
51
51
  | `web/react/shortcuts` | `use_shortcuts` for keys such as `"mod+z"`, quiet while typing or while a dialog or popover is open; `describe_keys` for help text. |
52
52
  | `web/react/tree` | `Tree`: nested rows that fold, walk with the arrow keys, reorder by dragging or with Alt and the arrow keys, show several picked, and take drops from outside. |
53
53
  | `web/react/toasts`, `dialog`, `menus`, `split` | Toasts with a link or Undo that say how they closed, and `undoable` actions that commit or undo exactly once; modal dialogs with a subtitle, header actions, a full-window size and a `form` whose submit button sends it, and confirm and text dialogs, openable from anywhere; context menus and popovers worked by keyboard, openable from anywhere, their keep-open items ticking at once; resize handles and splits. |
@@ -20,6 +20,7 @@ export interface SidebarWidth {
20
20
  }
21
21
  interface ShellProps {
22
22
  brand: string;
23
+ brand_icon?: string;
23
24
  sections?: ShellSection[];
24
25
  active?: string;
25
26
  sidebar?: ReactNode;
@@ -41,5 +42,5 @@ interface ShellProps {
41
42
  brand_in_sidebar?: boolean;
42
43
  children: ReactNode;
43
44
  }
44
- export declare function Shell({ brand, sections, active, sidebar: sidebar_content, sidebar_width, theme, user, logged_out: held_logged_out, user_href, on_logout, brand_href, tools, sidebar_foot, status_bar, view_key, menu_open: held_menu_open, on_menu_open_change, main_class, contained, brand_in_sidebar, children, }: ShellProps): import("react").JSX.Element;
45
+ export declare function Shell({ brand, brand_icon, sections, active, sidebar: sidebar_content, sidebar_width, theme, user, logged_out: held_logged_out, user_href, on_logout, brand_href, tools, sidebar_foot, status_bar, view_key, menu_open: held_menu_open, on_menu_open_change, main_class, contained, brand_in_sidebar, children, }: ShellProps): import("react").JSX.Element;
45
46
  export {};
@@ -5,6 +5,7 @@ export type { Sorting } from "../tables.ts";
5
5
  export type { DropPlacement } from "../tree.ts";
6
6
  export interface TableColumn<Row> {
7
7
  label: string;
8
+ header?: ReactNode;
8
9
  id?: string;
9
10
  cell: (row: Row) => ReactNode;
10
11
  sort?: (row: Row) => string | number | null;
@@ -10,6 +10,12 @@ import { placement_at } from "../tree.js";
10
10
  import { virtual_window } from "../virtual.js";
11
11
  import { ContextMenu, Popover } from "./menus.js";
12
12
  import { ResizeHandle } from "./split.js";
13
+ // A column's heading: its header with the label kept for screen readers and hover, or the label alone.
14
+ function heading(column) {
15
+ if (column.header === undefined)
16
+ return column.label;
17
+ return (_jsxs("span", { className: "column-header", title: column.label, children: [_jsx("span", { "aria-hidden": "true", children: column.header }), _jsx("span", { className: "visually-hidden", children: column.label })] }));
18
+ }
13
19
  const is_empty = (value) => value === null || value === undefined || value === "";
14
20
  // The rows that pass and, in a tree, those above a row that passes, with each row's passing children.
15
21
  function filtered_tree(rows, passes, get_children, kept_children) {
@@ -293,7 +299,7 @@ export function DataTable(props) {
293
299
  return event.preventDefault();
294
300
  event.dataTransfer.setData("text/plain", id);
295
301
  set_dragged(id);
296
- }, onDragEnd: () => { set_dragged(null); set_drop_target(null); }, onDragOver: (event) => drag_over(column, event), onDragLeave: () => set_drop_target(null), onDrop: drop, children: [sortable ? (_jsxs("button", { type: "button", className: "sort", title: props.on_sorting_change ? "Click to sort by this column, Shift+click to add it to the sort" : undefined, onClick: (event) => sort_by(id, event.shiftKey), onKeyDown: (event) => move_by_key(column, event), children: [column.label, marker] })) : movable ? (_jsx("span", { tabIndex: 0, onKeyDown: (event) => move_by_key(column, event), children: column.label })) : column.label, column.filter !== undefined && (_jsx("button", { type: "button", className: column.filter_active ? "icon filter-button active" : "icon filter-button", "aria-label": `Filter by ${column.label}`, "aria-expanded": open_filter?.id === id, title: column.filter_active ? `Filtered by ${column.label}` : `Filter by ${column.label}`, onClick: (event) => toggle_filter(id, event.currentTarget), children: _jsx("svg", { viewBox: "0 0 16 16", width: "12", height: "12", "aria-hidden": "true", children: _jsx("path", { d: "M2 3h12l-4.5 5.5V13l-3 1.5v-6z", fill: "currentColor" }) }) })), props.on_column_widths_change && !filling && (_jsx(ResizeHandle, { side: "left", size: width ?? measured[id] ?? column.min_width ?? 48, min: column.min_width ?? 48, max: column.max_width ?? 960, default_size: clamp_width(column.width ?? measured[id] ?? 120, column.min_width, column.max_width), on_resize: (size) => resize(column, size, false), on_resize_end: (size) => resize(column, size, true), label: `Resize the ${column.label} column`, class_name: "column-resize" }))] }, id));
302
+ }, onDragEnd: () => { set_dragged(null); set_drop_target(null); }, onDragOver: (event) => drag_over(column, event), onDragLeave: () => set_drop_target(null), onDrop: drop, children: [sortable ? (_jsxs("button", { type: "button", className: "sort", title: props.on_sorting_change ? "Click to sort by this column, Shift+click to add it to the sort" : undefined, onClick: (event) => sort_by(id, event.shiftKey), onKeyDown: (event) => move_by_key(column, event), children: [heading(column), marker] })) : movable ? (_jsx("span", { tabIndex: 0, onKeyDown: (event) => move_by_key(column, event), children: heading(column) })) : heading(column), column.filter !== undefined && (_jsx("button", { type: "button", className: column.filter_active ? "icon filter-button active" : "icon filter-button", "aria-label": `Filter by ${column.label}`, "aria-expanded": open_filter?.id === id, title: column.filter_active ? `Filtered by ${column.label}` : `Filter by ${column.label}`, onClick: (event) => toggle_filter(id, event.currentTarget), children: _jsx("svg", { viewBox: "0 0 16 16", width: "12", height: "12", "aria-hidden": "true", children: _jsx("path", { d: "M2 3h12l-4.5 5.5V13l-3 1.5v-6z", fill: "currentColor" }) }) })), props.on_column_widths_change && !filling && (_jsx(ResizeHandle, { side: "left", size: width ?? measured[id] ?? column.min_width ?? 48, min: column.min_width ?? 48, max: column.max_width ?? 960, default_size: clamp_width(column.width ?? measured[id] ?? 120, column.min_width, column.max_width), on_resize: (size) => resize(column, size, false), on_resize_end: (size) => resize(column, size, true), label: `Resize the ${column.label} column`, class_name: "column-resize" }))] }, id));
297
303
  }
298
304
  function body_row(row, key) {
299
305
  const original = row.original;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-tech-laboratories/x-libs",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Code the X-apps share: agents and their long runs, passwords and sign-in, the Fastify server pieces, durations, and the web page's helpers and widgets",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {