@uniweb/kit 0.10.2 → 0.10.3
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": "@uniweb/kit",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Standard component library for Uniweb foundations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"fuse.js": "^7.0.0",
|
|
42
42
|
"shiki": "^3.0.0",
|
|
43
43
|
"tailwind-merge": "^3.6.0",
|
|
44
|
-
"@uniweb/
|
|
44
|
+
"@uniweb/semantic-parser": "1.2.0",
|
|
45
45
|
"@uniweb/scene": "0.1.2",
|
|
46
|
-
"@uniweb/
|
|
46
|
+
"@uniweb/core": "0.8.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": "^19.0.0",
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Table Renderer
|
|
2
|
+
* Table Renderer — the styled table, opt-in.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The engine emits a plain semantic `<table>` and leaves the look to the
|
|
5
|
+
* typography layer, because what a table LOOKS like is a foundation's call.
|
|
6
|
+
* This is the other option: visible structure — header band, row separators,
|
|
7
|
+
* zebra striping — that a foundation opts into.
|
|
8
|
+
*
|
|
9
|
+
* <Prose content={content} block={block} components={{ table: Table }} />
|
|
10
|
+
*
|
|
11
|
+
* ── What was wrong with it ──
|
|
12
|
+
*
|
|
13
|
+
* Until 2026-07-31 this read raw ProseMirror and got two things wrong, both
|
|
14
|
+
* silently. It tested `cell.type === 'tableHeader'`, a node type the reader
|
|
15
|
+
* never emits — cells are `tableCell` carrying `attrs.header` — so **no cell
|
|
16
|
+
* was ever a `<th>`** and every table rendered as an undifferentiated grid.
|
|
17
|
+
* And it read `cell.content[0].content[0].text`, the first text node of the
|
|
18
|
+
* first paragraph, so a cell was truncated to its first run with every mark
|
|
19
|
+
* discarded: `` `name` `` came out as bare `name`, and a cell holding a link
|
|
20
|
+
* lost it entirely.
|
|
21
|
+
*
|
|
22
|
+
* It now takes the sequence's table element, where a cell is a nested sequence
|
|
23
|
+
* — so a cell keeps its marks and may hold any block.
|
|
5
24
|
*
|
|
6
25
|
* @module @uniweb/kit/Section/renderers/Table
|
|
7
26
|
*/
|
|
@@ -9,43 +28,75 @@
|
|
|
9
28
|
import React from 'react'
|
|
10
29
|
import { cn } from '../../../utils/index.js'
|
|
11
30
|
import { SafeHtml } from '../../../components/SafeHtml/index.js'
|
|
31
|
+
import { SequenceElement } from '../../Render/index.jsx'
|
|
12
32
|
|
|
13
33
|
/**
|
|
14
|
-
*
|
|
34
|
+
* A cell's contents, with a lone paragraph unwrapped.
|
|
35
|
+
*
|
|
36
|
+
* `tableCell` is `paragraph+`, so a markdown cell is always exactly one
|
|
37
|
+
* paragraph — and emitting that `<p>` is what a typography layer then gives
|
|
38
|
+
* margins to, measured at 17.5px top and bottom, turning one-line rows into
|
|
39
|
+
* 75px. The engine makes the same allowance for the same reason.
|
|
40
|
+
*/
|
|
41
|
+
function cellContent(cell, block, components) {
|
|
42
|
+
const children = cell?.children || []
|
|
43
|
+
const [only] = children
|
|
44
|
+
|
|
45
|
+
if (children.length === 1 && only?.type === 'paragraph') {
|
|
46
|
+
return <SafeHtml value={only.text || ''} as="span" />
|
|
47
|
+
}
|
|
48
|
+
return children.map((el, i) => (
|
|
49
|
+
<SequenceElement key={i} element={el} block={block} components={components} />
|
|
50
|
+
))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Table - styled table renderer
|
|
15
55
|
*
|
|
16
56
|
* @param {Object} props
|
|
17
|
-
* @param {Array} props.
|
|
57
|
+
* @param {Array} [props.rows] - Rows from a sequence table element
|
|
58
|
+
* @param {Object} [props.element] - The table element itself, so this drops
|
|
59
|
+
* straight into `components={{ table: Table }}` with no adapter
|
|
60
|
+
* @param {Object} [props.block] - Block instance, for insets inside a cell
|
|
61
|
+
* @param {Object} [props.components] - Renderer overrides, for nested content
|
|
18
62
|
* @param {string} [props.className] - Additional CSS classes
|
|
19
63
|
*/
|
|
20
|
-
export function Table({
|
|
21
|
-
|
|
64
|
+
export function Table({ rows, element, block, components, className, ...props }) {
|
|
65
|
+
const data = rows ?? element?.rows
|
|
66
|
+
if (!Array.isArray(data) || data.length === 0) return null
|
|
67
|
+
|
|
68
|
+
const [first, ...rest] = data
|
|
69
|
+
const hasHeader = first.cells?.some((cell) => cell.header)
|
|
70
|
+
const bodyRows = hasHeader ? rest : data
|
|
71
|
+
|
|
72
|
+
const renderRow = (row, key, zebra) => (
|
|
73
|
+
<tr key={key} className={zebra ? 'bg-muted' : undefined}>
|
|
74
|
+
{row.cells?.map((cell, i) => {
|
|
75
|
+
const CellTag = cell.header ? 'th' : 'td'
|
|
76
|
+
return (
|
|
77
|
+
<CellTag
|
|
78
|
+
key={i}
|
|
79
|
+
style={cell.align ? { textAlign: cell.align } : undefined}
|
|
80
|
+
colSpan={cell.colspan > 1 ? cell.colspan : undefined}
|
|
81
|
+
rowSpan={cell.rowspan > 1 ? cell.rowspan : undefined}
|
|
82
|
+
className={cn(
|
|
83
|
+
'px-4 py-2 text-sm',
|
|
84
|
+
cell.header ? 'font-medium text-heading' : 'text-body'
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
{cellContent(cell, block, components)}
|
|
88
|
+
</CellTag>
|
|
89
|
+
)
|
|
90
|
+
})}
|
|
91
|
+
</tr>
|
|
92
|
+
)
|
|
22
93
|
|
|
23
94
|
return (
|
|
24
95
|
<div className={cn('overflow-x-auto', className)} {...props}>
|
|
25
|
-
<table className="min-w-full
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{row.content?.map((cell, cellIndex) => {
|
|
30
|
-
const CellTag = cell.type === 'tableHeader' ? 'th' : 'td'
|
|
31
|
-
const cellContent = cell.content?.[0]?.content?.[0]?.text || ''
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<CellTag
|
|
35
|
-
key={cellIndex}
|
|
36
|
-
className={cn(
|
|
37
|
-
'px-4 py-2 text-sm',
|
|
38
|
-
cell.type === 'tableHeader'
|
|
39
|
-
? 'font-medium text-heading bg-muted'
|
|
40
|
-
: 'text-body'
|
|
41
|
-
)}
|
|
42
|
-
>
|
|
43
|
-
<SafeHtml value={cellContent} as="span" />
|
|
44
|
-
</CellTag>
|
|
45
|
-
)
|
|
46
|
-
})}
|
|
47
|
-
</tr>
|
|
48
|
-
))}
|
|
96
|
+
<table className="min-w-full border border-border rounded-lg">
|
|
97
|
+
{hasHeader && <thead className="bg-muted">{renderRow(first, 'h', false)}</thead>}
|
|
98
|
+
<tbody className="divide-y divide-border">
|
|
99
|
+
{bodyRows.map((row, i) => renderRow(row, i, i % 2 === 1))}
|
|
49
100
|
</tbody>
|
|
50
101
|
</table>
|
|
51
102
|
</div>
|