@lotics/ui 7.10.0 → 7.12.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/AGENTS.md CHANGED
@@ -866,8 +866,8 @@ radio_picker · counter · range_slider · date_picker · date_range_filter_fiel
866
866
  inline_text_input · inline_number_input · inline_select · inline_member_select · inline_date_picker ·
867
867
  inline_time_picker (the Inline* family — per-field editors on `inline_edit`'s `useInlineEdit` +
868
868
  `InlineEditView`; `InlineSelect`/`InlineMemberSelect` render the resting value like its option — `renderOptionContent` by default, `renderSelected` to override — a chip/badge at rest, not just text) ·
869
- inline_static (InlineStatic — a READ-ONLY value matching the Inline* box metrics EXACTLY (height, padding, 1px transparent border) so a non-editable field — a computed total, a system ID, a synced/locked value — aligns pixel-for-pixel in the same column; non-interactive, NOT a disabled input; `muted`/`tabular`/`align="right"` for a number column) ·
870
- list · list_item · menu_button · menu_list_item · detail_row (DetailRow — label+value row for drawer/peek detail; optional `trailing` slot renders a right-side action/badge/unit after the value) · danger_zone (DangerZone — the destructive section: a soft danger-tinted frame (`tint`/`solid`, never raw hex) + a danger heading + a description + a destructive action slot (children, e.g. a `danger` Button); sits APART at the bottom of a record/settings surface) · pressable_row ·
869
+ inline_static (InlineStatic — a READ-ONLY value matching the Inline* box metrics EXACTLY (height, padding, 1px transparent border) so a non-editable field — a computed total, a system ID, a synced/locked value — aligns pixel-for-pixel in the same column; non-interactive, NOT a disabled input; `muted`/`tabular`/`align="right"` for a number column, `weight="medium"` to emphasise a total among plain rows) ·
870
+ list · list_item · menu_button · menu_list_item · detail_row (DetailRow — label+value row for drawer/peek detail; in FORM mode (`labelWidth` set) the value column FILLS the row so a stack of inline editors all span the same width + none jumps wider on edit; optional `trailing` slot renders a right-side action/badge/unit after the value) · danger_zone (DangerZone — the destructive section: a soft danger-tinted frame (`tint`/`solid`, never raw hex) + a danger heading + a description + a destructive action slot (children, e.g. a `danger` Button); sits APART at the bottom of a record/settings surface) · pressable_row ·
871
871
  check_circle (CheckCircle — the completion ring: an empty ring that springs to a filled check when done, distinct from the square checkbox; the task/to-do/checklist toggle. Compose task rows directly, no Task component) · action_menu ·
872
872
  floating_action_bar · filter_chip · column_filter (ColumnFilter — the typed per-column filter pill +
873
873
  columnFilterToConditions; for a register filtering on several columns) · chip_group · search_input ·
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "7.10.0",
3
+ "version": "7.12.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./tokens": "./src/tokens.ts",
@@ -9,9 +9,8 @@ export interface DetailRowProps {
9
9
  children: ReactNode;
10
10
  /** An optional right-side slot, rendered as the LAST child of the row (after
11
11
  * `children`) — a small action `Button`, a status `Badge`, a unit label, an
12
- * `IconButton`. For it to sit at the right edge, wrap the value/editor in a
13
- * `flex:1` View so the value takes the slack and the trailing pins right (the
14
- * inline-record templates do this). */
12
+ * `IconButton`. In form mode (`labelWidth` set) the value column already fills
13
+ * the row, so the trailing pins to the right edge automatically. */
15
14
  trailing?: ReactNode;
16
15
  /** Fixed label-column width (px) to align labels in a left column — for a peek
17
16
  * or form-like stack. Omit for a spread row: the label takes the slack and the
@@ -26,17 +25,19 @@ export interface DetailRowProps {
26
25
  /**
27
26
  * One label + value line for a drawer / peek detail view — NOT a list row (that's
28
27
  * `PressableRow` / `TableRow`). Spread by default (muted label left, value pushed
29
- * to the right edge); pass `labelWidth` to align labels in a fixed left column
30
- * with the values flowing immediately after instead.
28
+ * to the right edge); pass `labelWidth` to align labels in a fixed left column
29
+ * the value column then FILLS the rest of the row, so a stack of inline editors
30
+ * all span the same width (none jumps wider on edit).
31
31
  */
32
32
  export function DetailRow(props: DetailRowProps) {
33
33
  const { label, children, trailing, labelWidth, labelSize = "sm", minHeight = 28 } = props;
34
+ const form = labelWidth != null;
34
35
  return (
35
36
  <View style={[styles.row, { minHeight }]}>
36
- <Text size={labelSize} color="muted" style={labelWidth != null ? { width: labelWidth } : styles.flexLabel}>
37
+ <Text size={labelSize} color="muted" style={form ? { width: labelWidth } : styles.flexLabel}>
37
38
  {label}
38
39
  </Text>
39
- {children}
40
+ {form ? <View style={styles.value}>{children}</View> : children}
40
41
  {trailing != null ? trailing : null}
41
42
  </View>
42
43
  );
@@ -49,4 +50,10 @@ const styles = StyleSheet.create({
49
50
  gap: 12,
50
51
  },
51
52
  flexLabel: { flex: 1 },
53
+ // Form mode (labelWidth set): the value column FILLS the row so every inline
54
+ // editor spans the SAME width — and none jumps wider when it swaps to the
55
+ // (flex:1) edit control. A stretch column, so an InlineEditView / InlineStatic
56
+ // child fills it edge-to-edge. A status Badge or action is NOT a value — pass
57
+ // it as `trailing` (pins right); one placed here would stretch full-width.
58
+ value: { flex: 1 },
52
59
  });
@@ -50,7 +50,7 @@ export interface DisplayFile {
50
50
  url: string;
51
51
  /** Optional thumbnail URL for images */
52
52
  thumbnailUrl?: string;
53
- /** Preview URL for legacy Excel files (.xls converted to .xlsx server-side) */
53
+ /** Optional local preview URL (e.g. an object URL for a file still uploading). */
54
54
  previewUrl?: string;
55
55
  }
56
56
 
@@ -16,6 +16,9 @@ export interface InlineStaticProps {
16
16
  tabular?: boolean;
17
17
  /** Horizontal alignment — `right` for a number column. Default `left`. */
18
18
  align?: "left" | "right";
19
+ /** Value weight — `medium` to emphasise a total or headline figure among plain
20
+ * rows. Default `regular` (reads as an ordinary value, not a heading). */
21
+ weight?: "regular" | "medium";
19
22
  }
20
23
 
21
24
  /**
@@ -29,7 +32,7 @@ export interface InlineStaticProps {
29
32
  * slot, so the value takes the slack and the trailing pins right.
30
33
  */
31
34
  export function InlineStatic(props: InlineStaticProps) {
32
- const { value, placeholder, muted, tabular, align = "left" } = props;
35
+ const { value, placeholder, muted, tabular, align = "left", weight } = props;
33
36
  const isEmpty = value.length === 0;
34
37
  const display = isEmpty ? (placeholder ?? "—") : value;
35
38
  return (
@@ -38,6 +41,7 @@ export function InlineStatic(props: InlineStaticProps) {
38
41
  numberOfLines={1}
39
42
  tabular={tabular}
40
43
  align={align}
44
+ weight={weight}
41
45
  color={muted ? "muted" : "default"}
42
46
  style={[inlineValueTextStyle, isEmpty ? styles.placeholder : null]}
43
47
  >
package/src/mime.ts CHANGED
@@ -21,8 +21,6 @@ const EXCEL_MIME_TYPES = [
21
21
  "application/vnd.ms-excel",
22
22
  ];
23
23
 
24
- const LEGACY_EXCEL_MIME_TYPES = ["application/vnd.ms-excel", "application/wps-office.xls"];
25
-
26
24
  const DOCX_MIME_TYPES = [
27
25
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
28
26
  "application/wps-office.docx",
@@ -36,11 +34,6 @@ export function isExcelMimeType(mimeType: string): boolean {
36
34
  return EXCEL_MIME_TYPES.includes(mimeType.toLowerCase());
37
35
  }
38
36
 
39
- /** Legacy .xls (BIFF) — not parseable in-app; excluded from previewable. */
40
- export function isLegacyExcelMimeType(mimeType: string): boolean {
41
- return LEGACY_EXCEL_MIME_TYPES.includes(mimeType.toLowerCase());
42
- }
43
-
44
37
  export function isCsvMimeType(mimeType: string): boolean {
45
38
  const mt = mimeType.toLowerCase();
46
39
  return mt === "text/csv" || mt === "text/tab-separated-values";
@@ -50,10 +43,10 @@ export function isDocxMimeType(mimeType: string): boolean {
50
43
  return DOCX_MIME_TYPES.includes(mimeType.toLowerCase());
51
44
  }
52
45
 
53
- /** PDF, modern Excel (.xlsx), Word (.docx), or CSV — types FilePreview renders inline. */
46
+ /** PDF, Excel (.xlsx + legacy .xls), Word (.docx), or CSV — types FilePreview renders inline. */
54
47
  export function isPreviewableMimeType(mimeType: string): boolean {
55
48
  return (
56
- (isExcelMimeType(mimeType) && !isLegacyExcelMimeType(mimeType)) ||
49
+ isExcelMimeType(mimeType) ||
57
50
  isCsvMimeType(mimeType) ||
58
51
  isPdfMimeType(mimeType) ||
59
52
  isDocxMimeType(mimeType)