@structuralists/scaffolding 0.3.0 → 0.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@structuralists/scaffolding",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "main": "./index.ts",
5
5
  "types": "./index.ts",
6
6
  "exports": {
@@ -2,6 +2,11 @@ import { Text } from '../../Content/Text';
2
2
 
3
3
  export type JsonLeafNodeProps = {
4
4
  value: unknown;
5
+ /** When true, render `null` / `undefined` via Text with the monospace font.
6
+ * JsonTable sets this on recursive calls so leaves inside a table inherit
7
+ * the same font as the surrounding monospace cells (Text otherwise sets
8
+ * its own font-family and would break the inheritance). */
9
+ isMono?: boolean;
5
10
  };
6
11
 
7
12
  /**
@@ -13,14 +18,15 @@ export type JsonLeafNodeProps = {
13
18
  * placeholder until JsonTable grows specialized renderers for them.
14
19
  */
15
20
  export const JsonLeafNode = (props: JsonLeafNodeProps) => {
16
- const { value } = props;
21
+ const { value, isMono } = props;
22
+ const size = isMono ? 'small' : undefined;
17
23
 
18
24
  if (value === null) {
19
- return <Text isMuted>null</Text>;
25
+ return <Text isMuted isMono={isMono} size={size}>null</Text>;
20
26
  }
21
27
 
22
28
  if (value === undefined) {
23
- return <Text isMuted>undefined</Text>;
29
+ return <Text isMuted isMono={isMono} size={size}>undefined</Text>;
24
30
  }
25
31
 
26
32
  if (typeof value === 'boolean') {
@@ -1,33 +1,49 @@
1
- import { QuickTable, QuickTableRow, QuickTableCell } from '../../Tables/QuickTable';
1
+ import {
2
+ QuickTable,
3
+ QuickTableRow,
4
+ QuickTableCell,
5
+ QuickTableHeaderRow,
6
+ QuickTableHeaderCell,
7
+ } from '../../Tables/QuickTable';
2
8
  import { JsonLeafNode } from './JsonLeafNode';
3
9
  import { isPlainObject } from './utils';
4
10
  import type { JsonTableProps } from './types';
11
+ import styles from './styles.module.css';
5
12
 
6
13
  export const JsonTable = (props: JsonTableProps) => {
7
- const { value, title } = props;
14
+ const { value, title, isNested } = props;
8
15
 
9
16
  if (isPlainObject(value)) {
10
17
  const entries = Object.entries(value);
11
18
 
12
- return (
13
- <QuickTable title={title} hasColumnDividers>
19
+ const headerRow = title ? (
20
+ <QuickTableHeaderRow>
21
+ <QuickTableHeaderCell colSpan={2}>{title}</QuickTableHeaderCell>
22
+ </QuickTableHeaderRow>
23
+ ) : undefined;
24
+
25
+ const table = (
26
+ <QuickTable headerRow={headerRow} hasColumnDividers hasOuterBorder={!isNested}>
14
27
  {entries.map((entry) => {
15
28
  const [key, child] = entry;
29
+ const childIsObject = isPlainObject(child);
16
30
 
17
31
  return (
18
32
  <QuickTableRow key={key}>
19
33
  <QuickTableCell width={1}>{key}</QuickTableCell>
20
- <QuickTableCell>
21
- <JsonTable value={child} title={key} />
34
+ <QuickTableCell hasPadding={!childIsObject}>
35
+ <JsonTable value={child} title={key} isNested />
22
36
  </QuickTableCell>
23
37
  </QuickTableRow>
24
38
  );
25
39
  })}
26
40
  </QuickTable>
27
41
  );
42
+
43
+ return isNested ? table : <div className={styles.root}>{table}</div>;
28
44
  }
29
45
 
30
- return <JsonLeafNode value={value} />;
46
+ return <JsonLeafNode value={value} isMono={isNested} />;
31
47
  };
32
48
 
33
49
  export type { JsonTableProps };
@@ -0,0 +1,7 @@
1
+ .root {
2
+ font-family: var(--ui-font-mono);
3
+ }
4
+
5
+ .root thead tr {
6
+ border-bottom-color: var(--ui-border-subtle);
7
+ }
@@ -6,8 +6,17 @@ export type JsonTableProps = {
6
6
  /** Any value. JsonTable inspects shape and dispatches to a specialized
7
7
  * renderer; primitives and unknown shapes fall back to JsonLeafNode. */
8
8
  value: JsonValue;
9
- /** Optional caption rendered above the QuickTable when value is a plain
10
- * object. JsonTable also passes the parent key down as `title` on each
11
- * recursive call, so nested object-valued cells render a labeled table. */
9
+ /** Optional title rendered as a header row inside the table border when
10
+ * value is a plain object. JsonTable also passes the parent key down as
11
+ * `title` on each recursive call, so nested object-valued cells render a
12
+ * labeled table. */
12
13
  title?: ReactNode;
14
+ /** @internal Set by JsonTable on every recursive call (i.e. when rendering
15
+ * a child entry of a parent table). Two effects: (1) on a nested object,
16
+ * suppresses the outer border so the inner table sits flush inside its
17
+ * parent cell — whose padding is also dropped at the parent level; (2) on
18
+ * a primitive leaf, signals JsonLeafNode to render `null` / `undefined`
19
+ * via Text with the monospace font so they inherit the surrounding font
20
+ * set by the JsonTable root. Not intended for external callers. */
21
+ isNested?: boolean;
13
22
  };
@@ -25,10 +25,13 @@ const widthStyle = (
25
25
  };
26
26
 
27
27
  export const QuickTableCell = (props: QuickTableCellProps) => {
28
- const { children, width, align } = props;
28
+ const { children, width, align, hasPadding = true } = props;
29
29
 
30
30
  return (
31
- <td className={cx(styles.cell, alignClass(align))} style={widthStyle(width)}>
31
+ <td
32
+ className={cx(styles.cell, alignClass(align), !hasPadding && styles.cellFlush)}
33
+ style={widthStyle(width)}
34
+ >
32
35
  {children}
33
36
  </td>
34
37
  );
@@ -43,10 +46,14 @@ export const QuickTableRow = (props: QuickTableRowProps) => {
43
46
  brandQuickTableType(QuickTableRow, 'QuickTableRow');
44
47
 
45
48
  export const QuickTableHeaderCell = (props: QuickTableHeaderCellProps) => {
46
- const { children, width, align } = props;
49
+ const { children, width, align, colSpan } = props;
47
50
 
48
51
  return (
49
- <th className={cx(styles.headerCell, alignClass(align))} style={widthStyle(width)}>
52
+ <th
53
+ className={cx(styles.headerCell, alignClass(align))}
54
+ style={widthStyle(width)}
55
+ colSpan={colSpan}
56
+ >
50
57
  {children}
51
58
  </th>
52
59
  );
@@ -61,14 +68,21 @@ export const QuickTableHeaderRow = (props: QuickTableHeaderRowProps) => {
61
68
  brandQuickTableType(QuickTableHeaderRow, 'QuickTableHeaderRow');
62
69
 
63
70
  export const QuickTable = (props: QuickTableProps) => {
64
- const { children, headerCells, headerRow, title, hasColumnDividers } = props;
71
+ const { children, headerCells, headerRow, title, hasColumnDividers, hasOuterBorder = true } =
72
+ props;
65
73
 
66
74
  const header =
67
75
  headerRow ??
68
76
  (headerCells ? <QuickTableHeaderRow>{headerCells}</QuickTableHeaderRow> : undefined);
69
77
 
70
78
  return (
71
- <table className={cx(styles.table, hasColumnDividers && styles.tableWithColumnDividers)}>
79
+ <table
80
+ className={cx(
81
+ styles.table,
82
+ hasColumnDividers && styles.tableWithColumnDividers,
83
+ !hasOuterBorder && styles.tableFlush,
84
+ )}
85
+ >
72
86
  {title ? <caption className={styles.caption}>{title}</caption> : null}
73
87
  {header ? <thead>{header}</thead> : null}
74
88
  <tbody>{wrapList(children, QuickTableRow)}</tbody>
@@ -63,3 +63,11 @@
63
63
  .tableWithColumnDividers .headerCell + .headerCell {
64
64
  border-left: 1px solid var(--ui-border-subtle);
65
65
  }
66
+
67
+ .cellFlush {
68
+ padding: 0;
69
+ }
70
+
71
+ .tableFlush {
72
+ border: 0;
73
+ }
@@ -17,6 +17,9 @@ export type QuickTableProps = {
17
17
  title?: ReactNode;
18
18
  /** Draw vertical divider lines between columns. Default: false. */
19
19
  hasColumnDividers?: boolean;
20
+ /** Draw the outer border around the table. Default: true. Set false when
21
+ * the table sits flush inside a parent cell that already provides a frame. */
22
+ hasOuterBorder?: boolean;
20
23
  };
21
24
 
22
25
  export type QuickTableRowProps = {
@@ -27,6 +30,10 @@ export type QuickTableCellProps = {
27
30
  children: ReactNode;
28
31
  width?: string | number;
29
32
  align?: QuickTableCellAlign;
33
+ /** Apply the cell's inner padding. Default: true. Set false when the cell's
34
+ * content is itself a bordered block (e.g. a nested table) that should sit
35
+ * flush with the cell edges. */
36
+ hasPadding?: boolean;
30
37
  };
31
38
 
32
39
  export type QuickTableHeaderRowProps = {
@@ -37,4 +44,5 @@ export type QuickTableHeaderCellProps = {
37
44
  children: ReactNode;
38
45
  width?: string | number;
39
46
  align?: QuickTableCellAlign;
47
+ colSpan?: number;
40
48
  };