@sheetpane/images 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +79 -2
- package/dist/anchors.d.ts +82 -0
- package/dist/anchors.d.ts.map +1 -0
- package/dist/anchors.js +92 -0
- package/dist/anchors.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +191 -0
- package/dist/index.js.map +1 -0
- package/dist/media.d.ts +61 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +100 -0
- package/dist/media.js.map +1 -0
- package/package.json +41 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arpit Arya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,80 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sheetpane/images
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Images for sheetpane — pictures anchored in the sheet drawn in sheet space through the `overlay` hook; view only, kept on export.**
|
|
4
|
+
|
|
5
|
+
A plugin: it registers against the one in-memory workbook of `@sheetpane/grid`
|
|
6
|
+
(`createWorkbook({ plugins: [...] })`, or `plugins="images"` on the element), so
|
|
7
|
+
`<sheetpane-excel>` and `<sheetpane-errors>` both see its effect. Versioned in
|
|
8
|
+
lockstep with `@sheetpane/grid`.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { createWorkbook } from '@sheetpane/grid';
|
|
12
|
+
import { images } from '@sheetpane/images';
|
|
13
|
+
|
|
14
|
+
el.workbook = createWorkbook({ plugins: [images()] });
|
|
15
|
+
// or: import '@sheetpane/images' and <sheetpane-excel plugins="images">
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
No runtime dependencies. Importing is safe in Node (no DOM until a picture renders).
|
|
19
|
+
|
|
20
|
+
## What it draws
|
|
21
|
+
|
|
22
|
+
Every `picture` in a sheet's drawing part, at its anchor, in the overlay layer: it scrolls,
|
|
23
|
+
zooms and clips with its cells, under the pins and above the cells. `alt` is the picture's
|
|
24
|
+
description (`descr`), else its name.
|
|
25
|
+
|
|
26
|
+
| format | shown as |
|
|
27
|
+
|---|---|
|
|
28
|
+
| PNG, JPEG, GIF, BMP, WebP | an `<img>` over an object URL |
|
|
29
|
+
| SVG | an `<img>` too — never inline markup, so no script in the file can run |
|
|
30
|
+
| EMF, WMF, TIFF, anything else | a dashed frame the size of the anchor: `logo.emf — preview unavailable` |
|
|
31
|
+
|
|
32
|
+
The format is told from the bytes' header first and the file name second.
|
|
33
|
+
|
|
34
|
+
Charts and shapes are not pictures and are skipped (the charts plugin draws charts). The status
|
|
35
|
+
bar adds `2 pictures · 1 preview unavailable` for the sheet in view.
|
|
36
|
+
|
|
37
|
+
**View only.** In Edit a click on a picture selects the cell under the click, so that cell can
|
|
38
|
+
still be flagged; pictures cannot be moved, resized, added or deleted.
|
|
39
|
+
|
|
40
|
+
## Anchors (`src/anchors.ts`, exported)
|
|
41
|
+
|
|
42
|
+
EMU become pixels at 9,525 EMU per pixel (914,400 per inch, 96 px per inch), then are placed in
|
|
43
|
+
the grid's own geometry (`Geometry` — pixels at 100 % zoom from A1's top-left):
|
|
44
|
+
|
|
45
|
+
| anchor | rectangle |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `twoCell` | `from` to `to`, each corner plus its offset |
|
|
48
|
+
| `oneCell` | `from` plus its offset, sized by `ext` |
|
|
49
|
+
| `absolute` | `pos` from the sheet origin, sized by `ext` |
|
|
50
|
+
|
|
51
|
+
An offset never reaches past its own cell, as in Excel. `anchorRect(anchor, geometry)`,
|
|
52
|
+
`pointToPx`, `cellAt`, `emuToPx` and `refOf` are plain functions with no DOM, so
|
|
53
|
+
`@sheetpane/charts` reuses them as they are.
|
|
54
|
+
|
|
55
|
+
**Excel's own row heights and hidden rows or columns are not used.** The grid draws every row at
|
|
56
|
+
its fixed height and shows what is stored, hidden or not; a picture anchored C4:F9 covers C4:F9
|
|
57
|
+
on screen, whatever size those cells are in Excel. A `oneCell` or `absolute` picture keeps its
|
|
58
|
+
`ext` in pixels, so beside tall Excel rows it can look smaller than it did there.
|
|
59
|
+
|
|
60
|
+
## Memory
|
|
61
|
+
|
|
62
|
+
Nothing is read from the file's media until a picture comes near the screen (a 20 MB photo costs
|
|
63
|
+
nothing until scrolled to). One object URL is made per media part however many pictures show it,
|
|
64
|
+
and all are revoked when the pane loads another workbook (or a load fails). A decode that
|
|
65
|
+
finishes after the workbook went makes no URL.
|
|
66
|
+
|
|
67
|
+
## Export
|
|
68
|
+
|
|
69
|
+
Patch-mode export leaves the drawing and media parts as they were (the fidelity ruling), so a
|
|
70
|
+
workbook's pictures survive a round trip. **A deleted row does not re-anchor pictures in v1**:
|
|
71
|
+
the file keeps the anchors it had.
|
|
72
|
+
|
|
73
|
+
## Tests
|
|
74
|
+
|
|
75
|
+
`pnpm exec vitest run --project images` — anchor arithmetic against geometry, rectangles against
|
|
76
|
+
the rendered cells (oneCell, twoCell, absolute, zoom), every format, laziness, object-URL
|
|
77
|
+
revocation. Fixtures are hand-written packages and the ExcelJS 2×2 PNG; files saved by Excel and
|
|
78
|
+
LibreOffice are owed by W-30.
|
|
79
|
+
|
|
80
|
+
**Using an AI coding agent?** `npx sheetpane-skills install` puts sheetpane's skills for Claude Code, Kiro and Codex into your repository.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Geometry } from '@sheetpane/grid';
|
|
2
|
+
/** 914,400 EMU per inch, 96 px per inch. */
|
|
3
|
+
export declare const EMU_PER_PX = 9525;
|
|
4
|
+
/** EMU → px (9,525 EMU per pixel), unrounded. */
|
|
5
|
+
export declare const emuToPx: (emu: number) => number;
|
|
6
|
+
/** px → EMU (9,525 EMU per pixel), rounded to a whole EMU. */
|
|
7
|
+
export declare const pxToEmu: (px: number) => number;
|
|
8
|
+
/** A position in a drawing: zero-based column and row, plus an offset in EMU inside that cell. */
|
|
9
|
+
export interface AnchorPoint {
|
|
10
|
+
col: number;
|
|
11
|
+
colOff: number;
|
|
12
|
+
row: number;
|
|
13
|
+
rowOff: number;
|
|
14
|
+
}
|
|
15
|
+
/** The shape of `DrawingAnchor` (@sheetpane/import) that placing needs; any drawing kind fits. */
|
|
16
|
+
export interface Anchor {
|
|
17
|
+
anchor: 'twoCell' | 'oneCell' | 'absolute';
|
|
18
|
+
from?: AnchorPoint;
|
|
19
|
+
to?: AnchorPoint;
|
|
20
|
+
/** Size in EMU (oneCell, absolute). */
|
|
21
|
+
ext?: {
|
|
22
|
+
cx: number;
|
|
23
|
+
cy: number;
|
|
24
|
+
};
|
|
25
|
+
/** Position in EMU from the sheet origin (absolute). */
|
|
26
|
+
pos?: {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** A rectangle in sheet space: px at 100 % zoom from the top-left of A1. */
|
|
32
|
+
export interface Rect {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* An anchor point in sheet space, placed through the grid's geometry. An offset never reaches past its own cell
|
|
40
|
+
* (Excel clamps it the same way).
|
|
41
|
+
*
|
|
42
|
+
* @param g - The grid's geometry (its column and row positions and sizes).
|
|
43
|
+
* @param p - The point: column, row and their EMU offsets.
|
|
44
|
+
* @returns The point's `{ x, y }` in px.
|
|
45
|
+
*/
|
|
46
|
+
export declare function pointToPx(g: Geometry, p: AnchorPoint): {
|
|
47
|
+
x: number;
|
|
48
|
+
y: number;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* The rectangle an anchor covers, or `undefined` when the anchor lacks what its kind needs.
|
|
52
|
+
* - twoCell: `from` to `to`, each with its offsets;
|
|
53
|
+
* - oneCell: `from` plus `ext`;
|
|
54
|
+
* - absolute: `pos` plus `ext`, from the sheet origin.
|
|
55
|
+
*
|
|
56
|
+
* @param a - The anchor (any drawing kind fits).
|
|
57
|
+
* @param g - The grid's geometry.
|
|
58
|
+
* @returns The rectangle in sheet space, or `undefined`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function anchorRect(a: Anchor, g: Geometry): Rect | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* The cell under a point in sheet space (px at 100 %); a negative coordinate counts as 0.
|
|
63
|
+
*
|
|
64
|
+
* @param g - The grid's geometry.
|
|
65
|
+
* @param x - Horizontal position, px.
|
|
66
|
+
* @param y - Vertical position, px.
|
|
67
|
+
* @returns The zero-based `{ row, col }`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function cellAt(g: Geometry, x: number, y: number): {
|
|
70
|
+
row: number;
|
|
71
|
+
col: number;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* A column's letters: `0` → `A`, `25` → `Z`, `26` → `AA`.
|
|
75
|
+
*
|
|
76
|
+
* @param col - The column, 0-based.
|
|
77
|
+
* @returns Its letters.
|
|
78
|
+
*/
|
|
79
|
+
export declare function colLabel(col: number): string;
|
|
80
|
+
/** Zero-based row and column → `A1`. */
|
|
81
|
+
export declare const refOf: (row: number, col: number) => string;
|
|
82
|
+
//# sourceMappingURL=anchors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchors.d.ts","sourceRoot":"","sources":["../src/anchors.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,4CAA4C;AAC5C,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,iDAAiD;AACjD,eAAO,MAAM,OAAO,QAAS,MAAM,KAAG,MAA0B,CAAC;AACjE,8DAA8D;AAC9D,eAAO,MAAM,OAAO,OAAQ,MAAM,KAAG,MAAqC,CAAC;AAE3E,kGAAkG;AAClG,MAAM,WAAW,WAAW;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAEzF,kGAAkG;AAClG,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IAC3C,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,EAAE,CAAC,EAAE,WAAW,CAAC;IACjB,uCAAuC;IACvC,GAAG,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,wDAAwD;IACxD,GAAG,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAChC;AAED,4EAA4E;AAC5E,MAAM,WAAW,IAAI;IAAG,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAK7E;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,CAenE;AAeD;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAEtF;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI5C;AAED,wCAAwC;AACxC,eAAO,MAAM,KAAK,QAAS,MAAM,OAAO,MAAM,KAAG,MAAsC,CAAC"}
|
package/dist/anchors.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** 914,400 EMU per inch, 96 px per inch. */
|
|
2
|
+
export const EMU_PER_PX = 9525;
|
|
3
|
+
/** EMU → px (9,525 EMU per pixel), unrounded. */
|
|
4
|
+
export const emuToPx = (emu) => emu / EMU_PER_PX;
|
|
5
|
+
/** px → EMU (9,525 EMU per pixel), rounded to a whole EMU. */
|
|
6
|
+
export const pxToEmu = (px) => Math.round(px * EMU_PER_PX);
|
|
7
|
+
const clamp = (v, lo, hi) => Math.min(Math.max(v, lo), hi);
|
|
8
|
+
const finite = (n) => (Number.isFinite(n) ? n : 0);
|
|
9
|
+
/**
|
|
10
|
+
* An anchor point in sheet space, placed through the grid's geometry. An offset never reaches past its own cell
|
|
11
|
+
* (Excel clamps it the same way).
|
|
12
|
+
*
|
|
13
|
+
* @param g - The grid's geometry (its column and row positions and sizes).
|
|
14
|
+
* @param p - The point: column, row and their EMU offsets.
|
|
15
|
+
* @returns The point's `{ x, y }` in px.
|
|
16
|
+
*/
|
|
17
|
+
export function pointToPx(g, p) {
|
|
18
|
+
return {
|
|
19
|
+
x: g.colX(p.col) + clamp(emuToPx(finite(p.colOff)), 0, g.colWidth(p.col)),
|
|
20
|
+
y: g.rowY(p.row) + clamp(emuToPx(finite(p.rowOff)), 0, g.rowHeight(p.row)),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The rectangle an anchor covers, or `undefined` when the anchor lacks what its kind needs.
|
|
25
|
+
* - twoCell: `from` to `to`, each with its offsets;
|
|
26
|
+
* - oneCell: `from` plus `ext`;
|
|
27
|
+
* - absolute: `pos` plus `ext`, from the sheet origin.
|
|
28
|
+
*
|
|
29
|
+
* @param a - The anchor (any drawing kind fits).
|
|
30
|
+
* @param g - The grid's geometry.
|
|
31
|
+
* @returns The rectangle in sheet space, or `undefined`.
|
|
32
|
+
*/
|
|
33
|
+
export function anchorRect(a, g) {
|
|
34
|
+
if (a.anchor === 'twoCell') {
|
|
35
|
+
if (!a.from || !a.to)
|
|
36
|
+
return undefined;
|
|
37
|
+
const p0 = pointToPx(g, a.from), p1 = pointToPx(g, a.to);
|
|
38
|
+
return { x: Math.min(p0.x, p1.x), y: Math.min(p0.y, p1.y), width: Math.abs(p1.x - p0.x), height: Math.abs(p1.y - p0.y) };
|
|
39
|
+
}
|
|
40
|
+
if (!a.ext)
|
|
41
|
+
return undefined;
|
|
42
|
+
const width = emuToPx(finite(a.ext.cx)), height = emuToPx(finite(a.ext.cy));
|
|
43
|
+
if (a.anchor === 'oneCell') {
|
|
44
|
+
if (!a.from)
|
|
45
|
+
return undefined;
|
|
46
|
+
const p = pointToPx(g, a.from);
|
|
47
|
+
return { x: p.x, y: p.y, width, height };
|
|
48
|
+
}
|
|
49
|
+
if (!a.pos)
|
|
50
|
+
return undefined;
|
|
51
|
+
return { x: emuToPx(finite(a.pos.x)), y: emuToPx(finite(a.pos.y)), width, height };
|
|
52
|
+
}
|
|
53
|
+
/** The last index in `[0, max)` whose start (`at(i)`) is at or before `v`. `at` must not decrease. */
|
|
54
|
+
function indexAt(at, v, max) {
|
|
55
|
+
let lo = 0, hi = max - 1;
|
|
56
|
+
while (lo < hi) {
|
|
57
|
+
const mid = (lo + hi + 1) >> 1;
|
|
58
|
+
if (at(mid) <= v)
|
|
59
|
+
lo = mid;
|
|
60
|
+
else
|
|
61
|
+
hi = mid - 1;
|
|
62
|
+
}
|
|
63
|
+
return lo;
|
|
64
|
+
}
|
|
65
|
+
const MAX_COLS = 16384;
|
|
66
|
+
const MAX_ROWS = 1048576;
|
|
67
|
+
/**
|
|
68
|
+
* The cell under a point in sheet space (px at 100 %); a negative coordinate counts as 0.
|
|
69
|
+
*
|
|
70
|
+
* @param g - The grid's geometry.
|
|
71
|
+
* @param x - Horizontal position, px.
|
|
72
|
+
* @param y - Vertical position, px.
|
|
73
|
+
* @returns The zero-based `{ row, col }`.
|
|
74
|
+
*/
|
|
75
|
+
export function cellAt(g, x, y) {
|
|
76
|
+
return { col: indexAt((c) => g.colX(c), Math.max(0, x), MAX_COLS), row: indexAt((r) => g.rowY(r), Math.max(0, y), MAX_ROWS) };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A column's letters: `0` → `A`, `25` → `Z`, `26` → `AA`.
|
|
80
|
+
*
|
|
81
|
+
* @param col - The column, 0-based.
|
|
82
|
+
* @returns Its letters.
|
|
83
|
+
*/
|
|
84
|
+
export function colLabel(col) {
|
|
85
|
+
let s = '';
|
|
86
|
+
for (let n = col + 1; n > 0; n = Math.floor((n - 1) / 26))
|
|
87
|
+
s = String.fromCharCode(65 + ((n - 1) % 26)) + s;
|
|
88
|
+
return s;
|
|
89
|
+
}
|
|
90
|
+
/** Zero-based row and column → `A1`. */
|
|
91
|
+
export const refOf = (row, col) => `${colLabel(col)}${row + 1}`;
|
|
92
|
+
//# sourceMappingURL=anchors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchors.js","sourceRoot":"","sources":["../src/anchors.ts"],"names":[],"mappings":"AAWA,4CAA4C;AAC5C,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAE/B,iDAAiD;AACjD,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,GAAG,GAAG,UAAU,CAAC;AACjE,8DAA8D;AAC9D,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,UAAU,CAAC,CAAC;AAmB3E,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAU,EAAE,EAAU,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACnF,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,CAAW,EAAE,CAAc;IACnD,OAAO;QACL,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACzE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;KAC3E,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,CAAW;IAC/C,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE;YAAE,OAAO,SAAS,CAAC;QACvC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACzD,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3H,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5E,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QAC9B,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3C,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACrF,CAAC;AAED,sGAAsG;AACtG,SAAS,OAAO,CAAC,EAAyB,EAAE,CAAS,EAAE,GAAW;IAChE,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;IACzB,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,EAAE,GAAG,GAAG,CAAC;;YAAM,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,QAAQ,GAAG,KAAK,CAAC;AACvB,MAAM,QAAQ,GAAG,OAAO,CAAC;AAEzB;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CAAC,CAAW,EAAE,CAAS,EAAE,CAAS;IACtD,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC;AAChI,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QAAE,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5G,OAAO,CAAC,CAAC;AACX,CAAC;AAED,wCAAwC;AACxC,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,GAAW,EAAU,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type OverlayItem, type Plugin, type PluginContext, type Geometry } from '@sheetpane/grid';
|
|
2
|
+
import { MediaCache } from './media.js';
|
|
3
|
+
export { EMU_PER_PX, emuToPx, pxToEmu, pointToPx, anchorRect, cellAt, colLabel, refOf } from './anchors.js';
|
|
4
|
+
export type { Anchor, AnchorPoint, Rect } from './anchors.js';
|
|
5
|
+
export { MediaCache, sniff, extensionOf, fileName, isUnsupportedPath } from './media.js';
|
|
6
|
+
export type { MediaResult } from './media.js';
|
|
7
|
+
/**
|
|
8
|
+
* The picture items of one sheet, with their rectangles in sheet space: one item per `picture` drawing with media
|
|
9
|
+
* and an area (charts and shapes are skipped). Exported for tests and for charts to model on.
|
|
10
|
+
*
|
|
11
|
+
* @param sheet - The sheet's name.
|
|
12
|
+
* @param ctx - The plugin context with the grid's geometry, as the `overlay` hook receives it.
|
|
13
|
+
* @param cache - Returns the media cache the items read their bytes through.
|
|
14
|
+
* @returns The overlay items; `alt` is the drawing's description, else its name.
|
|
15
|
+
*/
|
|
16
|
+
export declare function pictureItems(sheet: string, ctx: PluginContext & {
|
|
17
|
+
geometry: Geometry;
|
|
18
|
+
}, cache: () => MediaCache): OverlayItem[];
|
|
19
|
+
/**
|
|
20
|
+
* The pictures plugin: draws each picture anchored in the sheet through the `overlay` hook, view only. Bytes are
|
|
21
|
+
* read only when a picture nears the viewport; PNG, JPEG, GIF, BMP, WebP and SVG show through `<img>`, and EMF, WMF,
|
|
22
|
+
* TIFF or a missing part show a labelled "preview unavailable" frame. In Edit a click on a picture selects the cell
|
|
23
|
+
* under it. Export copies the drawings unchanged. One instance serves one pane; its object URLs are revoked when
|
|
24
|
+
* that pane loads another workbook. Importing this module registers it as `images`.
|
|
25
|
+
*
|
|
26
|
+
* @returns The plugin (`id: 'images'`), with a status count of the sheet's pictures.
|
|
27
|
+
* @see https://github.com/arpitarya/sheetpane/blob/main/records/0107_IMAGES.md
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import { createWorkbook } from '@sheetpane/grid';
|
|
31
|
+
* import { images } from '@sheetpane/images';
|
|
32
|
+
*
|
|
33
|
+
* const book = createWorkbook({ plugins: [images()] });
|
|
34
|
+
* await book.load(bytes, 'catalogue.xlsx');
|
|
35
|
+
* ```
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import '@sheetpane/images'; // registers `images`
|
|
39
|
+
* excel.setAttribute('plugins', 'images');
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function images(): Plugin;
|
|
43
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0C,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3I,OAAO,EAA+B,UAAU,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC5G,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzF,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAsB9C;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,EAAE,KAAK,EAAE,MAAM,UAAU,GAAG,WAAW,EAAE,CAa/H;AAuED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,IAAI,MAAM,CAiC/B"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// @sheetpane/images — pictures anchored in the sheet, drawn in sheet space through the `overlay`
|
|
2
|
+
// hook. View only: patch-mode export leaves the drawing and media parts as they were, so nothing
|
|
3
|
+
// is lost and nothing is edited (W-20). Importing is safe in Node: no DOM until an item renders.
|
|
4
|
+
import { registerPlugin } from '@sheetpane/grid';
|
|
5
|
+
import { anchorRect, cellAt, refOf } from './anchors.js';
|
|
6
|
+
import { fileName, isUnsupportedPath, MediaCache } from './media.js';
|
|
7
|
+
export { EMU_PER_PX, emuToPx, pxToEmu, pointToPx, anchorRect, cellAt, colLabel, refOf } from './anchors.js';
|
|
8
|
+
export { MediaCache, sniff, extensionOf, fileName, isUnsupportedPath } from './media.js';
|
|
9
|
+
/** Each workbook gets a number, so item ids never repeat across two loads (the grid caches rendered items by id). */
|
|
10
|
+
const generations = new WeakMap();
|
|
11
|
+
let nextGeneration = 1;
|
|
12
|
+
const generationOf = (book) => {
|
|
13
|
+
let g = generations.get(book);
|
|
14
|
+
if (!g) {
|
|
15
|
+
g = nextGeneration++;
|
|
16
|
+
generations.set(book, g);
|
|
17
|
+
}
|
|
18
|
+
return g;
|
|
19
|
+
};
|
|
20
|
+
const unavailable = (name) => `${name} — preview unavailable`;
|
|
21
|
+
const FRAME = 'box-sizing:border-box;width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:4px;overflow:hidden;text-align:center;'
|
|
22
|
+
+ 'border:1px dashed var(--sp-line-strong,#CBCBC5);background:var(--sp-sunken,#EFEFEB);color:var(--sp-ink-3,#63666E);font-size:12px;line-height:1.3;word-break:break-all';
|
|
23
|
+
/**
|
|
24
|
+
* The picture items of one sheet, with their rectangles in sheet space: one item per `picture` drawing with media
|
|
25
|
+
* and an area (charts and shapes are skipped). Exported for tests and for charts to model on.
|
|
26
|
+
*
|
|
27
|
+
* @param sheet - The sheet's name.
|
|
28
|
+
* @param ctx - The plugin context with the grid's geometry, as the `overlay` hook receives it.
|
|
29
|
+
* @param cache - Returns the media cache the items read their bytes through.
|
|
30
|
+
* @returns The overlay items; `alt` is the drawing's description, else its name.
|
|
31
|
+
*/
|
|
32
|
+
export function pictureItems(sheet, ctx, cache) {
|
|
33
|
+
const s = ctx.book.sheets.find((x) => x.name === sheet);
|
|
34
|
+
if (!s)
|
|
35
|
+
return [];
|
|
36
|
+
const gen = generationOf(ctx.book);
|
|
37
|
+
const items = [];
|
|
38
|
+
s.drawings.forEach((d, i) => {
|
|
39
|
+
if (d.kind !== 'picture' || !d.media)
|
|
40
|
+
return;
|
|
41
|
+
const rect = anchorRect(d, ctx.geometry);
|
|
42
|
+
if (!rect || rect.width <= 0 || rect.height <= 0)
|
|
43
|
+
return;
|
|
44
|
+
const name = d.name || fileName(d.media);
|
|
45
|
+
items.push({ id: `pic:${gen}:${i}:${d.media}`, sheet, rect, alt: d.descr || name, render: () => renderPicture(d, d.descr || name, rect, sheet, ctx, cache) });
|
|
46
|
+
});
|
|
47
|
+
return items;
|
|
48
|
+
}
|
|
49
|
+
function renderPicture(d, alt, rect, sheet, ctx, cache) {
|
|
50
|
+
const media = d.media, file = fileName(media);
|
|
51
|
+
const root = document.createElement('div');
|
|
52
|
+
root.className = 'sp-picture';
|
|
53
|
+
root.style.cssText = 'position:relative;width:100%;height:100%;overflow:hidden';
|
|
54
|
+
root.dataset.media = media;
|
|
55
|
+
const frame = (text, state) => {
|
|
56
|
+
const f = document.createElement('div');
|
|
57
|
+
f.className = 'sp-picture-frame';
|
|
58
|
+
f.dataset.state = state;
|
|
59
|
+
f.style.cssText = FRAME;
|
|
60
|
+
f.textContent = text;
|
|
61
|
+
return f;
|
|
62
|
+
};
|
|
63
|
+
const show = (el) => root.replaceChildren(el);
|
|
64
|
+
// Edit: a click on a picture selects the cell under it, so that cell can still be flagged.
|
|
65
|
+
root.addEventListener('click', (e) => {
|
|
66
|
+
if (ctx.store.getSnapshot().mode !== 'edit')
|
|
67
|
+
return;
|
|
68
|
+
const r = root.getBoundingClientRect();
|
|
69
|
+
if (!r.width || !r.height)
|
|
70
|
+
return;
|
|
71
|
+
const x = rect.x + ((e.clientX - r.left) / r.width) * rect.width, y = rect.y + ((e.clientY - r.top) / r.height) * rect.height;
|
|
72
|
+
const c = cellAt(ctx.geometry, x, y);
|
|
73
|
+
ctx.store.select({ sheet, ref: refOf(c.row, c.col) });
|
|
74
|
+
});
|
|
75
|
+
// EMF, WMF and the like are known by name: a labelled frame, nothing read.
|
|
76
|
+
if (isUnsupportedPath(media)) {
|
|
77
|
+
show(frame(unavailable(file), 'unavailable'));
|
|
78
|
+
return root;
|
|
79
|
+
}
|
|
80
|
+
// Everything else: a frame now, the bytes when the picture first scrolls near the screen.
|
|
81
|
+
show(frame(file, 'pending'));
|
|
82
|
+
let started = false;
|
|
83
|
+
const start = () => {
|
|
84
|
+
if (started)
|
|
85
|
+
return;
|
|
86
|
+
started = true;
|
|
87
|
+
void cache().get(media).then((m) => {
|
|
88
|
+
if (m.kind === 'unsupported')
|
|
89
|
+
return show(frame(unavailable(file), 'unavailable'));
|
|
90
|
+
if (m.kind === 'missing')
|
|
91
|
+
return show(frame(unavailable(file), 'missing'));
|
|
92
|
+
const img = document.createElement('img');
|
|
93
|
+
img.alt = alt;
|
|
94
|
+
img.decoding = 'async';
|
|
95
|
+
img.draggable = false;
|
|
96
|
+
img.style.cssText = 'display:block;width:100%;height:100%;object-fit:fill;user-select:none';
|
|
97
|
+
img.addEventListener('load', () => show(img), { once: true });
|
|
98
|
+
img.addEventListener('error', () => show(frame(unavailable(file), 'unavailable')), { once: true });
|
|
99
|
+
img.src = m.url;
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
if (typeof IntersectionObserver === 'undefined')
|
|
103
|
+
queueMicrotask(start);
|
|
104
|
+
else {
|
|
105
|
+
const io = new IntersectionObserver((entries) => { if (entries.some((e) => e.isIntersecting)) {
|
|
106
|
+
io.disconnect();
|
|
107
|
+
start();
|
|
108
|
+
} }, { rootMargin: '300px' });
|
|
109
|
+
io.observe(root);
|
|
110
|
+
}
|
|
111
|
+
return root;
|
|
112
|
+
}
|
|
113
|
+
const picturesOn = (book, sheet) => book?.sheets.find((s) => s.name === sheet)?.drawings.filter((d) => d.kind === 'picture' && d.media) ?? [];
|
|
114
|
+
const status = {
|
|
115
|
+
id: 'images-status', slot: 'status', group: 'images', label: 'Pictures', mode: 'both',
|
|
116
|
+
text: (snap, sel) => {
|
|
117
|
+
const pics = picturesOn(snap.book, sel.sheet);
|
|
118
|
+
if (!pics.length)
|
|
119
|
+
return '';
|
|
120
|
+
const bad = pics.filter((d) => isUnsupportedPath(d.media)).length;
|
|
121
|
+
return `${pics.length} ${pics.length === 1 ? 'picture' : 'pictures'}${bad ? ` · ${bad} preview unavailable` : ''}`;
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* The pictures plugin: draws each picture anchored in the sheet through the `overlay` hook, view only. Bytes are
|
|
126
|
+
* read only when a picture nears the viewport; PNG, JPEG, GIF, BMP, WebP and SVG show through `<img>`, and EMF, WMF,
|
|
127
|
+
* TIFF or a missing part show a labelled "preview unavailable" frame. In Edit a click on a picture selects the cell
|
|
128
|
+
* under it. Export copies the drawings unchanged. One instance serves one pane; its object URLs are revoked when
|
|
129
|
+
* that pane loads another workbook. Importing this module registers it as `images`.
|
|
130
|
+
*
|
|
131
|
+
* @returns The plugin (`id: 'images'`), with a status count of the sheet's pictures.
|
|
132
|
+
* @see https://github.com/arpitarya/sheetpane/blob/main/records/0107_IMAGES.md
|
|
133
|
+
* @example
|
|
134
|
+
* ```ts
|
|
135
|
+
* import { createWorkbook } from '@sheetpane/grid';
|
|
136
|
+
* import { images } from '@sheetpane/images';
|
|
137
|
+
*
|
|
138
|
+
* const book = createWorkbook({ plugins: [images()] });
|
|
139
|
+
* await book.load(bytes, 'catalogue.xlsx');
|
|
140
|
+
* ```
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* import '@sheetpane/images'; // registers `images`
|
|
144
|
+
* excel.setAttribute('plugins', 'images');
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export function images() {
|
|
148
|
+
const states = new WeakMap();
|
|
149
|
+
const stateFor = (store) => {
|
|
150
|
+
let st = states.get(store);
|
|
151
|
+
if (!st) {
|
|
152
|
+
const s = { book: store.book, cache: undefined };
|
|
153
|
+
st = s;
|
|
154
|
+
states.set(store, s);
|
|
155
|
+
// A replaced workbook (or a failed load) frees every object URL made for the old one.
|
|
156
|
+
store.subscribe(() => {
|
|
157
|
+
if (s.book === store.book)
|
|
158
|
+
return;
|
|
159
|
+
s.cache?.revoke();
|
|
160
|
+
s.book = store.book;
|
|
161
|
+
s.cache = undefined;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return st;
|
|
165
|
+
};
|
|
166
|
+
return {
|
|
167
|
+
id: 'images',
|
|
168
|
+
provides: ['overlay'],
|
|
169
|
+
controls: [status],
|
|
170
|
+
overlay(sheet, ctx) {
|
|
171
|
+
const st = stateFor(ctx.store);
|
|
172
|
+
if (st.book !== ctx.book) {
|
|
173
|
+
st.cache?.revoke();
|
|
174
|
+
st.book = ctx.book;
|
|
175
|
+
st.cache = undefined;
|
|
176
|
+
}
|
|
177
|
+
const book = ctx.book;
|
|
178
|
+
return pictureItems(sheet, ctx, () => {
|
|
179
|
+
// A picture that decodes after its workbook went gets a closed cache and shows the missing frame.
|
|
180
|
+
if (st.book !== book) {
|
|
181
|
+
const dead = new MediaCache(book);
|
|
182
|
+
dead.revoke();
|
|
183
|
+
return dead;
|
|
184
|
+
}
|
|
185
|
+
return (st.cache ??= new MediaCache(book));
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
registerPlugin('images', () => images());
|
|
191
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/media.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { PluginContext } from '@sheetpane/grid';
|
|
2
|
+
type Book = PluginContext['book'];
|
|
3
|
+
/** What a media part turned out to be: a drawable image with its object URL, a format no browser draws, or missing. */
|
|
4
|
+
export type MediaResult =
|
|
5
|
+
/** The browser can draw it. */
|
|
6
|
+
{
|
|
7
|
+
kind: 'image';
|
|
8
|
+
url: string;
|
|
9
|
+
mime: string;
|
|
10
|
+
}
|
|
11
|
+
/** A format a browser cannot draw (EMF, WMF, TIFF …): the item shows a labelled frame. */
|
|
12
|
+
| {
|
|
13
|
+
kind: 'unsupported';
|
|
14
|
+
format: string;
|
|
15
|
+
}
|
|
16
|
+
/** The part is missing from the package, or the cache was closed while it was being read. */
|
|
17
|
+
| {
|
|
18
|
+
kind: 'missing';
|
|
19
|
+
};
|
|
20
|
+
/** `xl/media/image1.PNG` → `png`. */
|
|
21
|
+
export declare const extensionOf: (path: string) => string;
|
|
22
|
+
/** The part's file name, for labels: `xl/media/logo.emf` → `logo.emf`. */
|
|
23
|
+
export declare const fileName: (path: string) => string;
|
|
24
|
+
/**
|
|
25
|
+
* Whether the extension alone says a browser cannot draw the part — no bytes need reading to know.
|
|
26
|
+
*
|
|
27
|
+
* @param path - The media part's path, e.g. `xl/media/image1.emf`.
|
|
28
|
+
* @returns `true` for anything but PNG, JPEG, GIF, SVG, BMP or WebP.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isUnsupportedPath(path: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* What the bytes are, by their signature — the file name lies more often than the header. Unrecognised bytes fall
|
|
33
|
+
* back to the extension.
|
|
34
|
+
*
|
|
35
|
+
* @param bytes - The part's bytes.
|
|
36
|
+
* @param path - The part's path, for the fallback.
|
|
37
|
+
* @returns `{ mime }` for a format `<img>` draws, else `{ format }` (`emf`, `wmf`, `tiff`, the extension, or `unknown`).
|
|
38
|
+
*/
|
|
39
|
+
export declare function sniff(bytes: Uint8Array, path: string): {
|
|
40
|
+
mime: string;
|
|
41
|
+
} | {
|
|
42
|
+
format: string;
|
|
43
|
+
};
|
|
44
|
+
/** The object URLs made for one workbook. Closed (`revoke()`) when the workbook is replaced. */
|
|
45
|
+
export declare class MediaCache {
|
|
46
|
+
private readonly book;
|
|
47
|
+
private readonly results;
|
|
48
|
+
private readonly urls;
|
|
49
|
+
private closed;
|
|
50
|
+
constructor(book: Book);
|
|
51
|
+
get isClosed(): boolean;
|
|
52
|
+
/** How many object URLs are alive. */
|
|
53
|
+
get size(): number;
|
|
54
|
+
/** Reads the part on first ask; every later ask for the same part shares the answer. */
|
|
55
|
+
get(path: string): Promise<MediaResult>;
|
|
56
|
+
private load;
|
|
57
|
+
/** Frees every object URL and refuses new ones. Safe to call twice. */
|
|
58
|
+
revoke(): void;
|
|
59
|
+
}
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,KAAK,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;AAElC,uHAAuH;AACvH,MAAM,MAAM,WAAW;AACrB,+BAA+B;AAC7B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAC9C,0FAA0F;GACxF;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACzC,6FAA6F;GAC3F;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAMxB,qCAAqC;AACrC,eAAO,MAAM,WAAW,SAAU,MAAM,KAAG,MAAkE,CAAC;AAE9G,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,MAA+C,CAAC;AAExF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAID;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAc5F;AAED,gGAAgG;AAChG,qBAAa,UAAU;IAKT,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IACnE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,MAAM,CAAS;IAEvB,YAA6B,IAAI,EAAE,IAAI,EAAI;IAE3C,IAAI,QAAQ,IAAI,OAAO,CAAwB;IAC/C,sCAAsC;IACtC,IAAI,IAAI,IAAI,MAAM,CAA2B;IAE7C,wFAAwF;IACxF,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAKtC;YAEa,IAAI;IAYlB,uEAAuE;IACvE,MAAM,IAAI,IAAI,CAKb;CACF"}
|
package/dist/media.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const BY_EXTENSION = {
|
|
2
|
+
png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', jpe: 'image/jpeg', gif: 'image/gif', svg: 'image/svg+xml', bmp: 'image/bmp', webp: 'image/webp',
|
|
3
|
+
};
|
|
4
|
+
/** `xl/media/image1.PNG` → `png`. */
|
|
5
|
+
export const extensionOf = (path) => /\.([A-Za-z0-9]+)$/.exec(path)?.[1]?.toLowerCase() ?? '';
|
|
6
|
+
/** The part's file name, for labels: `xl/media/logo.emf` → `logo.emf`. */
|
|
7
|
+
export const fileName = (path) => path.slice(path.lastIndexOf('/') + 1);
|
|
8
|
+
/**
|
|
9
|
+
* Whether the extension alone says a browser cannot draw the part — no bytes need reading to know.
|
|
10
|
+
*
|
|
11
|
+
* @param path - The media part's path, e.g. `xl/media/image1.emf`.
|
|
12
|
+
* @returns `true` for anything but PNG, JPEG, GIF, SVG, BMP or WebP.
|
|
13
|
+
*/
|
|
14
|
+
export function isUnsupportedPath(path) {
|
|
15
|
+
return !(extensionOf(path) in BY_EXTENSION);
|
|
16
|
+
}
|
|
17
|
+
const startsWith = (b, sig, at = 0) => sig.every((v, i) => b[at + i] === v);
|
|
18
|
+
/**
|
|
19
|
+
* What the bytes are, by their signature — the file name lies more often than the header. Unrecognised bytes fall
|
|
20
|
+
* back to the extension.
|
|
21
|
+
*
|
|
22
|
+
* @param bytes - The part's bytes.
|
|
23
|
+
* @param path - The part's path, for the fallback.
|
|
24
|
+
* @returns `{ mime }` for a format `<img>` draws, else `{ format }` (`emf`, `wmf`, `tiff`, the extension, or `unknown`).
|
|
25
|
+
*/
|
|
26
|
+
export function sniff(bytes, path) {
|
|
27
|
+
if (startsWith(bytes, [0x89, 0x50, 0x4e, 0x47]))
|
|
28
|
+
return { mime: 'image/png' };
|
|
29
|
+
if (startsWith(bytes, [0xff, 0xd8, 0xff]))
|
|
30
|
+
return { mime: 'image/jpeg' };
|
|
31
|
+
if (startsWith(bytes, [0x47, 0x49, 0x46, 0x38]))
|
|
32
|
+
return { mime: 'image/gif' };
|
|
33
|
+
if (startsWith(bytes, [0x42, 0x4d]) && extensionOf(path) !== 'svg')
|
|
34
|
+
return { mime: 'image/bmp' };
|
|
35
|
+
if (startsWith(bytes, [0x52, 0x49, 0x46, 0x46]) && startsWith(bytes, [0x57, 0x45, 0x42, 0x50], 8))
|
|
36
|
+
return { mime: 'image/webp' };
|
|
37
|
+
if (startsWith(bytes, [0x20, 0x45, 0x4d, 0x46], 40))
|
|
38
|
+
return { format: 'emf' };
|
|
39
|
+
if (startsWith(bytes, [0xd7, 0xcd, 0xc6, 0x9a]) || startsWith(bytes, [0x01, 0x00, 0x09, 0x00]) || startsWith(bytes, [0x02, 0x00, 0x09, 0x00]))
|
|
40
|
+
return { format: 'wmf' };
|
|
41
|
+
if (startsWith(bytes, [0x49, 0x49, 0x2a, 0x00]) || startsWith(bytes, [0x4d, 0x4d, 0x00, 0x2a]))
|
|
42
|
+
return { format: 'tiff' };
|
|
43
|
+
const head = new TextDecoder().decode(bytes.subarray(0, 1024));
|
|
44
|
+
if (/<svg[\s>]/i.test(head))
|
|
45
|
+
return { mime: 'image/svg+xml' };
|
|
46
|
+
// Unrecognised bytes: trust the extension; a lie there fails in <img> and shows the labelled frame.
|
|
47
|
+
const mime = BY_EXTENSION[extensionOf(path)];
|
|
48
|
+
return mime ? { mime } : { format: extensionOf(path) || 'unknown' };
|
|
49
|
+
}
|
|
50
|
+
/** The object URLs made for one workbook. Closed (`revoke()`) when the workbook is replaced. */
|
|
51
|
+
export class MediaCache {
|
|
52
|
+
book;
|
|
53
|
+
results = new Map();
|
|
54
|
+
urls = new Set();
|
|
55
|
+
closed = false;
|
|
56
|
+
constructor(book) {
|
|
57
|
+
this.book = book;
|
|
58
|
+
}
|
|
59
|
+
get isClosed() { return this.closed; }
|
|
60
|
+
/** How many object URLs are alive. */
|
|
61
|
+
get size() { return this.urls.size; }
|
|
62
|
+
/** Reads the part on first ask; every later ask for the same part shares the answer. */
|
|
63
|
+
get(path) {
|
|
64
|
+
if (this.closed)
|
|
65
|
+
return Promise.resolve({ kind: 'missing' });
|
|
66
|
+
let p = this.results.get(path);
|
|
67
|
+
if (!p) {
|
|
68
|
+
p = this.load(path);
|
|
69
|
+
this.results.set(path, p);
|
|
70
|
+
}
|
|
71
|
+
return p;
|
|
72
|
+
}
|
|
73
|
+
async load(path) {
|
|
74
|
+
let bytes;
|
|
75
|
+
try {
|
|
76
|
+
bytes = await this.book.part(path);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { kind: 'missing' };
|
|
80
|
+
}
|
|
81
|
+
if (this.closed || !bytes)
|
|
82
|
+
return { kind: 'missing' };
|
|
83
|
+
const s = sniff(bytes, path);
|
|
84
|
+
if ('format' in s)
|
|
85
|
+
return { kind: 'unsupported', format: s.format };
|
|
86
|
+
// A picture is only ever shown through an <img> (SVG included): no script in it can run.
|
|
87
|
+
const url = URL.createObjectURL(new Blob([bytes], { type: s.mime }));
|
|
88
|
+
this.urls.add(url);
|
|
89
|
+
return { kind: 'image', url, mime: s.mime };
|
|
90
|
+
}
|
|
91
|
+
/** Frees every object URL and refuses new ones. Safe to call twice. */
|
|
92
|
+
revoke() {
|
|
93
|
+
this.closed = true;
|
|
94
|
+
for (const u of this.urls)
|
|
95
|
+
URL.revokeObjectURL(u);
|
|
96
|
+
this.urls.clear();
|
|
97
|
+
this.results.clear();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sheetpane/images",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Images for sheetpane — pictures anchored in the sheet drawn in sheet space through the `overlay` hook; view only, kept on export.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/arpitarya/sheetpane.git",
|
|
9
|
+
"directory": "packages/images"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": [
|
|
13
|
+
"./dist/index.js",
|
|
14
|
+
"./src/index.ts"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@sheetpane/grid": "0.3.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@sheetpane/skills": "0.3.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@sheetpane/grid": "0.3.0"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.build.json",
|
|
42
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
43
|
+
}
|
|
6
44
|
}
|