@lotics/ui 7.12.1 → 7.12.2

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/AGENTS.md CHANGED
@@ -302,7 +302,9 @@ marked + disabled), not a generic text menu. A composition (Badge + Button + Pop
302
302
  **Default: `<FilesEditor files onAdd onRemove>`** — it bundles the grid + a toolbar (Upload · Select ·
303
303
  Download all) + a batch SELECT mode + the gallery + confirmed-remove; the host only owns `files` and
304
304
  wires `onAdd` (picked → its upload) / `onRemove`. The destructive per-tile ✕ shows only in SELECT mode
305
- (the default view is a clean preview — no stray-tap deletes); the built-in gallery is Download +
305
+ (the default view is a clean preview — no stray-tap deletes); pass `selectTileRemove={false}` to drop
306
+ that ✕ entirely so select mode deletes ONLY via Select → Menu → Delete (the batch gating flow — how the
307
+ cell popover's `cell_files_editor` does it). The built-in gallery is Download +
306
308
  inline preview (no "open in new tab" — it's redundant once everything previews inline). In a
307
309
  height-bounded container (a popover/drawer) pass `gridMaxHeight` so the grid SCROLLS and the toolbar
308
310
  pins below it; omit it in free-flow layouts (a form field) where the grid grows. Reach for the
@@ -669,7 +671,9 @@ was only ever about the review surfaces' sparkles/severity glyphs, not functiona
669
671
  + cells align. The Divider BETWEEN rows is the resting separation. The COLUMNAR register is
670
672
  `Table`/`TableRow`/`TableCell` (columns once → header + widths can't drift; ONE hairline under the
671
673
  header; the rows below it Divider-separated; a sortable column shows its `SortHeader` sort glyph
672
- ALWAYS so it reads as sortable). Make it SELECTABLE with the `Table` `leading` gutter + `selectAll`
674
+ ALWAYS so it reads as sortable). A `TableRow` with no `onPress` renders a STATIC read-only row (no
675
+ hover wash / pointer cursor) so `Table` also serves read-only tabular data (a fee breakdown, a spec
676
+ sheet), not just interactive registers. Make it SELECTABLE with the `Table` `leading` gutter + `selectAll`
673
677
  slot — a `CheckboxInput` per `TableRow` (`leading`) + a select-all in the header band, the ticked
674
678
  rows `marked`, paired with a `FloatingActionBar`; the selection state is the **`useSelection()`**
675
679
  hook (`selected`/`count`/`toggle`/`setAll`/`allSelected`/`indeterminate`/`clear` — gating which
@@ -878,7 +882,7 @@ CalloutText · CalloutActions) · kpi_card · kpi_strip · summary_line (Summary
878
882
  bar_chart · line_chart · pie_chart · ring_gauge · progress_bar · stacked_progress_bar · breakdown ·
879
883
  funnel (Funnel — conversion funnel: narrowing bars + the step rate as a bold aligned headline row, count below; `orientation` vertical|horizontal, `onSelect`/`selectedKey` press-to-drill; the subset/drop-off sibling of stacked_progress_bar's whole-split) ·
880
884
  status_grid (StatusGrid + StatusLegend) · heatmap (density cross-tab — colour-only, no numbers) · matrix (Matrix — the PIVOT cross-tab: band-compound `Matrix` root + `Matrix.Header` (corner + axis labels) + `Matrix.Grid` (`display` number|heat|both — the cells, pressable, the value IN the cell) + `Matrix.Totals` (row + column + grand) + `Matrix.Legend`; the data layer `matrixTotals`/`MatrixAxisItem`/`MatrixCellRef` import from `@lotics/ui/matrix_totals`. Pick over `Heatmap` when the NUMBER and totals matter, not just where it clusters) · legend_item · remainder_meter · allocation_row ·
881
- scan_field · file_dropzone · files_editor (FilesEditor — THE all-in-one attachment field: FileGrid + a toolbar (Upload primary · Select · Download all) that swaps into a batch SELECT mode (Select all · a Menu of Download/Share/Delete · Done; the per-tile ✕ is select-mode-only, never in the default view) + built-in gallery (Download + inline preview; no "open in new tab") + Alert-confirmed remove; host wires `files` + `onAdd`/`onRemove` (+ optional `uploads`, `onShareSelected`, `readOnly`, `labels`, `galleryLabels`, `gridMaxHeight` — cap the grid height so it scrolls and the toolbar pins, for a popover/drawer); mirrors the frontend cell_files_editor. Use FileGrid/FileRows bare only when you own the chrome) · file_grid (FileGrid — the upload-aware grid: completed files + a live upload queue in one surface; FileUpload/PendingUpload types; the add-files default) · uploading_thumbnail (UploadingThumbnail — the single in-flight upload tile FileGrid renders; reach for it only when hand-rolling a non-grid upload layout) · file_thumbnail · file_thumbnail_grid · file_row · file_rows (FileRows — batteries-included file list: row press → built-in gallery + a ⋯ Download/Open-external/Remove menu; composes FileRow + ActionMenu + FileGalleryModal) · file_preview (FilePreview — the universal inline preview: image/PDF/video/audio + Word via `@lotics/docx` + Excel/CSV via `@lotics/xlsx`; the heavy engines are LAZY (dynamic-imported, ~free until a doc of that type is opened) so custom-code APPS can use it too — they just add `@lotics/docx` + `@lotics/xlsx` to their deps. Renders to canvas/DOM, never a nested iframe — works in the sandboxed app iframe) ·
885
+ scan_field · file_dropzone · files_editor (FilesEditor — THE all-in-one attachment field: FileGrid + a toolbar (Upload primary · Select · Download all) that swaps into a batch SELECT mode (Select all · a Menu of Download/Share/Delete · Done; the per-tile ✕ is select-mode-only, never in the default view; `selectTileRemove={false}` drops even that so delete is menu-only) + built-in gallery (Download + inline preview; no "open in new tab") + Alert-confirmed remove; host wires `files` + `onAdd`/`onRemove` (+ optional `uploads`, `onShareSelected`, `readOnly`, `selectTileRemove`, `labels`, `galleryLabels`, `gridMaxHeight` — cap the grid height so it scrolls and the toolbar pins, for a popover/drawer); mirrors the frontend cell_files_editor. Use FileGrid/FileRows bare only when you own the chrome) · file_grid (FileGrid — the upload-aware grid: completed files + a live upload queue in one surface; FileUpload/PendingUpload types; the add-files default) · uploading_thumbnail (UploadingThumbnail — the single in-flight upload tile FileGrid renders; reach for it only when hand-rolling a non-grid upload layout) · file_thumbnail · file_thumbnail_grid · file_row · file_rows (FileRows — batteries-included file list: row press → built-in gallery + a ⋯ Download/Open-external/Remove menu; composes FileRow + ActionMenu + FileGalleryModal) · file_preview (FilePreview — the universal inline preview: image/PDF/video/audio + Word via `@lotics/docx` + Excel/CSV via `@lotics/xlsx`; the heavy engines are LAZY (dynamic-imported, ~free until a doc of that type is opened) so custom-code APPS can use it too — they just add `@lotics/docx` + `@lotics/xlsx` to their deps. Renders to canvas/DOM, never a nested iframe — works in the sandboxed app iframe) ·
882
886
  file_gallery_modal · image_gallery · use_selection (useSelection — always-on multi-select state for a register/list: the `selected` Set + `toggle`/`setAll`/`allSelected`/`indeterminate`/`count`/`clear`; selectability gating stays with the caller. The checkbox-always-visible counterpart to `use_selection_mode`) · use_selection_mode · share_or_download · rotate_image · avatar · skeleton · activity_indicator · loading · divider ·
883
887
  spacer · stack · section_card · page_header · page_content · calendar (calendar/index.ts) · gantt ·
884
888
  comments_thread · agent_run (live streaming work feed) · agent_progress (its compact floating
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "7.12.1",
3
+ "version": "7.12.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./tokens": "./src/tokens.ts",
@@ -64,6 +64,12 @@ export interface FilesEditorProps {
64
64
  onDownloadZipSelected?: (files: DisplayFile[]) => void;
65
65
  /** Remove a completed file. Omit (or `readOnly`) for a view-only surface. */
66
66
  onRemove?: (id: string) => void;
67
+ /** Show the per-tile ✕ during SELECT mode (default `true`). The default
68
+ * (non-select) view NEVER shows a ✕ regardless — this governs select mode
69
+ * only. Set `false` when batch Select → Menu → Delete is the sole delete
70
+ * path (e.g. a cell popover), so a tile in select mode toggles selection and
71
+ * nothing else. */
72
+ selectTileRemove?: boolean;
67
73
  /** Cancel an in-flight upload / retry a failed one (when using `uploads`). */
68
74
  onUploadRemove?: (id: string) => void;
69
75
  onRetry?: (id: string) => void;
@@ -112,6 +118,7 @@ export function FilesEditor(props: FilesEditorProps) {
112
118
  onUpload,
113
119
  onDownloadZipSelected,
114
120
  onRemove,
121
+ selectTileRemove = true,
115
122
  onUploadRemove,
116
123
  onRetry,
117
124
  onRetryAll,
@@ -163,9 +170,12 @@ export function FilesEditor(props: FilesEditorProps) {
163
170
  selectedIds={selectMode ? selected : undefined}
164
171
  onToggleSelect={selectMode ? toggle : undefined}
165
172
  onFilePress={selectMode ? undefined : (f) => setGalleryIndex(files.findIndex((x) => x.id === f.id))}
166
- // The per-tile ✕ is a SELECT-mode affordance the default view stays a clean
167
- // preview surface so a stray tap can't drop a file.
168
- onDisplayRemove={!readOnly && selectMode && onRemove ? (id) => confirmRemove(() => onRemove(id)) : undefined}
173
+ // The per-tile ✕ is a SELECT-mode affordance (opt-out via `selectTileRemove`)
174
+ // — the default view stays a clean preview surface so a stray tap can't drop a
175
+ // file, and a `selectTileRemove={false}` surface deletes only via the menu.
176
+ onDisplayRemove={
177
+ !readOnly && selectMode && selectTileRemove && onRemove ? (id) => confirmRemove(() => onRemove(id)) : undefined
178
+ }
169
179
  onUploadRemove={onUploadRemove}
170
180
  onRetry={onRetry}
171
181
  onRetryAll={onRetryAll}
package/src/table.tsx CHANGED
@@ -144,11 +144,25 @@ export function TableRow(props: TableRowProps) {
144
144
  );
145
145
  const { focusVisible, focusProps } = useFocusRing();
146
146
 
147
+ // Read-only row (no `onPress`): a static, non-interactive row — no hover wash,
148
+ // no pointer cursor, no focusable door — so a `Table` can present read-only
149
+ // tabular data (a fee breakdown, a spec sheet) without implying the rows open
150
+ // something. An interactive row (with `onPress`) keeps the full register surface.
151
+ if (!onPress) {
152
+ return (
153
+ <View style={styles.staticRow}>
154
+ {ctx.leading > 0 ? <View style={{ width: ctx.leading }}>{leading}</View> : null}
155
+ <View style={[styles.door, { minHeight }]}>{cells}</View>
156
+ {ctx.trailing > 0 ? <View style={{ width: ctx.trailing, alignItems: "flex-end" }}>{trailing}</View> : null}
157
+ </View>
158
+ );
159
+ }
160
+
147
161
  return (
148
- <PressableRow onPress={onPress ?? noop} selected={selected} marked={marked} style={styles.row}>
162
+ <PressableRow onPress={onPress} selected={selected} marked={marked} style={styles.row}>
149
163
  {ctx.leading > 0 ? <View style={{ width: ctx.leading }}>{leading}</View> : null}
150
164
  <Pressable
151
- accessibilityRole={onPress ? "button" : undefined}
165
+ accessibilityRole="button"
152
166
  accessibilityLabel={accessibilityLabel}
153
167
  onPress={onPress}
154
168
  {...focusProps}
@@ -173,8 +187,6 @@ export function TableCell(props: TableCellProps) {
173
187
  return <View style={_column ? colStyle(_column) : undefined}>{children}</View>;
174
188
  }
175
189
 
176
- const noop = () => {};
177
-
178
190
  const styles = StyleSheet.create({
179
191
  // A hairline under the column header anchors the columns; the rows below it are
180
192
  // Divider-separated.
@@ -194,6 +206,14 @@ const styles = StyleSheet.create({
194
206
  row: {
195
207
  gap: 14,
196
208
  },
209
+ // Read-only row (a `TableRow` with no `onPress`): the register row's gutter +
210
+ // layout so cells still align with the header, minus the hover/press surface.
211
+ staticRow: {
212
+ flexDirection: "row",
213
+ alignItems: "center",
214
+ gap: 14,
215
+ paddingHorizontal: 20,
216
+ },
197
217
  door: {
198
218
  flex: 1,
199
219
  flexDirection: "row",