@quario/xlsx 0.6.0 → 0.7.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/CHANGELOG.md +16 -0
- package/README.md +5 -5
- package/lib/cell.js +55 -6
- package/lib/index.js +8 -6
- package/lib/style.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] - 2026-09-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Styled runs become rich text.** A cell of more than one styled run is written
|
|
15
|
+
as rich text, each run's font merged over the cell's — so bold, italic,
|
|
16
|
+
underline, strikethrough, size, face and colour survive into the grid. A
|
|
17
|
+
run-level `background` is not read: a spreadsheet's rich-text runs carry fonts
|
|
18
|
+
only, and filling the cell would colour text nobody asked to colour. A cell of
|
|
19
|
+
exactly one run stays a typed cell, so a single styled amount is still a
|
|
20
|
+
number.
|
|
21
|
+
|
|
22
|
+
- **A run inside a sentence presents its value.** A run holding one
|
|
23
|
+
interpolation under a `format` shows the same decimals here as on the page,
|
|
24
|
+
where a mixed cell used to show the bare value.
|
|
25
|
+
|
|
10
26
|
## [0.6.0] - 2026-09-07
|
|
11
27
|
|
|
12
28
|
### Added
|
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ as that order alone. Group boundaries add no rows of their own, and `break: "pag
|
|
|
79
79
|
`reset: "page"` are ignored, as are `page.margin` and a report header's `height`.
|
|
80
80
|
|
|
81
81
|
Each item takes one row with its cell in the first column, unmerged; a split takes one row with its
|
|
82
|
-
slots as the cells across it (slot widths
|
|
82
|
+
slots as the cells across it (slot widths withdrawn, a slot rendering nothing an empty cell), and a
|
|
83
83
|
spanning header or total cell is a merged range. Two band roles carry an
|
|
84
84
|
omakase default that the author's own style always overrides: report-header items render bold at
|
|
85
85
|
size 14, group-header items render bold.
|
|
@@ -109,8 +109,8 @@ on the wrong type, or a code the engine cannot read, writes no format.
|
|
|
109
109
|
### Images
|
|
110
110
|
|
|
111
111
|
An image item is not a cell: it takes one worksheet row as its anchor and floats over the sheet as a
|
|
112
|
-
drawing at its natural pixel size. Placement here is best-effort. `fit`, `alt`, and `style`
|
|
113
|
-
|
|
112
|
+
drawing at its natural pixel size. Placement here is best-effort. `fit`, `alt`, and `style` are
|
|
113
|
+
unused, and all of it may change without a breaking change. Bytes too short to carry a size reject
|
|
114
114
|
the render rather than vanish.
|
|
115
115
|
|
|
116
116
|
### Styles
|
|
@@ -136,14 +136,14 @@ The first table's header row anchors the frozen view. Everything from that row u
|
|
|
136
136
|
while the body scrolls, the spreadsheet analogue of the PDF target's repeated headers. A report
|
|
137
137
|
without a table freezes nothing. This is normative.
|
|
138
138
|
|
|
139
|
-
### Widths are
|
|
139
|
+
### Widths are withdrawn
|
|
140
140
|
|
|
141
141
|
Column `width` percentages size nothing here, and every worksheet column keeps the application
|
|
142
142
|
default. A width is a share of the _table_ width, and a worksheet's columns are global to the sheet.
|
|
143
143
|
Column A carries the flattened band item rows and the table's first column alike, so there is no
|
|
144
144
|
table for a share to be a share of.
|
|
145
145
|
|
|
146
|
-
### Page columns are
|
|
146
|
+
### Page columns are withdrawn
|
|
147
147
|
|
|
148
148
|
A page column count sizes and splits nothing here, and will not change, as `break: "page"` is
|
|
149
149
|
ignored. Page columns are strips of a page, and this target has no page to strip.
|
package/lib/cell.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* From an event cell to a plain cell descriptor `{ value, format }`, built on
|
|
3
3
|
* the engine's `typed` stream rule. `value` is a native number, boolean, or
|
|
4
4
|
* Date when the cell is one bare interpolation (so numbers stay computable in
|
|
5
|
-
* the sheet),
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* the sheet), the worksheet's rich-text shape when the cell holds more than
|
|
6
|
+
* one styled run, and the display-text join otherwise. `format` is the
|
|
7
|
+
* resolved cell-level formatting from the style module. Display text that
|
|
8
|
+
* contains a newline gains `wrapText` so the break is visible in the grid.
|
|
8
9
|
*/
|
|
9
|
-
import { currencyOf, text, typed } from "quario";
|
|
10
|
-
import { format as look, merge } from "./style.js";
|
|
10
|
+
import { currencyOf, display, format as present, styledRuns, text, typed } from "quario";
|
|
11
|
+
import { font, format as look, merge } from "./style.js";
|
|
11
12
|
|
|
12
13
|
// The digit count is the engine's, never this target's: it rides on the
|
|
13
14
|
// resolved declaration the stream carries, the same one HTML and PDF
|
|
@@ -87,14 +88,62 @@ let paintWrap = (out, shown) => {
|
|
|
87
88
|
out.alignment = { ...out.alignment, wrapText: true };
|
|
88
89
|
};
|
|
89
90
|
|
|
91
|
+
// One run's presented text: literals verbatim, every value through the
|
|
92
|
+
// declaration that run resolved. A run holding a single value token presents
|
|
93
|
+
// it, so a formatted amount inside a sentence reads the same here as it does
|
|
94
|
+
// on the page -- SCHEMA.md pins that the fraction digits are the same in every
|
|
95
|
+
// target that presents, and this target presents (ADR 0061). A run the engine
|
|
96
|
+
// withdrew the declaration from has nothing to present with and falls to
|
|
97
|
+
// display text, which is what the whole cell used to do.
|
|
98
|
+
/** @type {(tokens: any[], style: any, intl: any) => string} */
|
|
99
|
+
let shownText = (tokens, style, intl) => {
|
|
100
|
+
let out = "";
|
|
101
|
+
for (let token of tokens)
|
|
102
|
+
out +=
|
|
103
|
+
"literal" in token
|
|
104
|
+
? token.literal
|
|
105
|
+
: (present(token.value, style, intl) ?? display(token.value));
|
|
106
|
+
return out;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// A cell of more than one styled run takes the worksheet's rich-text shape:
|
|
110
|
+
// each run's **font half** merged over the cell font, and nothing else. A
|
|
111
|
+
// run-level `background` is inert -- spreadsheet rich-text runs carry fonts
|
|
112
|
+
// only, and filling the cell would colour text the author did not name -- and
|
|
113
|
+
// so is a run-level number format, since a cell has one and this cell is text.
|
|
114
|
+
// No typed cell is lost: a cell of more than one run holds more than one token
|
|
115
|
+
// and was display text already.
|
|
116
|
+
/** @type {(cell: any, styled: any[], under: any, intl: any) => any} */
|
|
117
|
+
let rich = (cell, styled, under, intl) => {
|
|
118
|
+
let base = merge(under, cell.style);
|
|
119
|
+
let out = look(base);
|
|
120
|
+
let parts = styled.map((one) => {
|
|
121
|
+
let style = one.style ? merge(base, one.style) : base;
|
|
122
|
+
return { font: font(style), text: shownText(one.tokens, style, intl) };
|
|
123
|
+
});
|
|
124
|
+
paintWrap(out, parts.map((part) => part.text).join(""));
|
|
125
|
+
return { value: { richText: parts }, format: out };
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// The style a cell of one styled run wears: that run's resolved answer, which
|
|
129
|
+
// already carries the cell's own declarations, or the cell's where it has no
|
|
130
|
+
// run at all.
|
|
131
|
+
/** @type {(cell: any, styled: any[]) => any} */
|
|
132
|
+
let soleStyle = (cell, styled) => styled[0]?.style ?? cell.style;
|
|
133
|
+
|
|
90
134
|
/**
|
|
91
135
|
* Resolve one event cell against its enclosing style (a row's style, or an
|
|
92
136
|
* item's band-role default — the layer under the cell's own).
|
|
93
137
|
*
|
|
138
|
+
* A cell of exactly one styled run wears that run's answer whole, so a single
|
|
139
|
+
* styled amount is still a number in the grid.
|
|
140
|
+
*
|
|
94
141
|
* @type {(cell: { tokens: any[], style?: any }, under: any, intl?: any) => { value: any, format: any }}
|
|
95
142
|
*/
|
|
96
143
|
let field = (cell, under, intl) => {
|
|
97
|
-
let
|
|
144
|
+
let styled = styledRuns(cell.tokens);
|
|
145
|
+
if (styled.length > 1) return rich(cell, styled, under, intl);
|
|
146
|
+
let resolved = merge(under, soleStyle(cell, styled));
|
|
98
147
|
let shown = shownOf(cell, resolved?.format);
|
|
99
148
|
let out = look(resolved);
|
|
100
149
|
paintFmt(out, resolved, shown, intl);
|
package/lib/index.js
CHANGED
|
@@ -27,17 +27,19 @@ import { append, create, embed, freeze, mark, place, save, sheet, span } from ".
|
|
|
27
27
|
|
|
28
28
|
// Band-role omakase defaults, applied under the author's own style so a
|
|
29
29
|
// declared style always wins. Only the headline roles carry one; every other
|
|
30
|
-
// band writes plain cells.
|
|
31
|
-
//
|
|
32
|
-
// together, and a target imports only public engine helpers so there is nowhere
|
|
33
|
-
// to share them from.
|
|
30
|
+
// band writes plain cells. SCHEMA.md states the pair, and a target imports only
|
|
31
|
+
// public engine helpers so there is nowhere to share them from.
|
|
34
32
|
//
|
|
35
33
|
// `@quario/html` deliberately carries none of them: its consumer has a
|
|
36
34
|
// stylesheet and the `q-*` classes are the seam, where a worksheet has neither.
|
|
37
35
|
// The rule is docs/adr/0014-a-target-supplies-defaults-only-where-its-consumer-has-no-seam.md —
|
|
38
36
|
// a target supplies defaults only where its consumer has no seam to supply
|
|
39
|
-
// them.
|
|
40
|
-
//
|
|
37
|
+
// them.
|
|
38
|
+
//
|
|
39
|
+
// `test/omakase-defaults.test.js` is what holds the copies in agreement. This
|
|
40
|
+
// comment names the gate rather than listing the files, because the list is
|
|
41
|
+
// what rotted: it used to name `packages/viewer/lib/style.js` and a `style.js`
|
|
42
|
+
// of the PDF target's own, and neither file exists.
|
|
41
43
|
/** @type {Record<string, any>} */
|
|
42
44
|
let ROLES = {
|
|
43
45
|
"report-header": { bold: true, size: 14 },
|
package/lib/style.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quario/xlsx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "The spreadsheet render target for quario — in the makings, not yet released",
|
|
5
5
|
"homepage": "https://getquario.com",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"@arethetypeswrong/cli": "^0.18.3",
|
|
43
43
|
"@size-limit/preset-small-lib": "^13.0.3",
|
|
44
44
|
"@types/node": "^22.20.1",
|
|
45
|
-
"quario": "^0.
|
|
45
|
+
"quario": "^0.7.0",
|
|
46
46
|
"size-limit": "^13.0.3",
|
|
47
47
|
"typescript": "^7.0.2"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"quario": "^0.
|
|
50
|
+
"quario": "^0.7.0"
|
|
51
51
|
},
|
|
52
52
|
"size-limit": [
|
|
53
53
|
{
|