@carto/ps-react-ui 4.11.3 → 4.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/dist/chat.js +962 -733
- package/dist/chat.js.map +1 -1
- package/dist/csv-item-hH_Gt7ur.js +32 -0
- package/dist/csv-item-hH_Gt7ur.js.map +1 -0
- package/dist/png-item-9dNbB37T.js +57 -0
- package/dist/png-item-9dNbB37T.js.map +1 -0
- package/dist/table-B3ZWWhJt.js +383 -0
- package/dist/table-B3ZWWhJt.js.map +1 -0
- package/dist/types/chat/containers/chat-footer.d.ts +1 -1
- package/dist/types/chat/containers/styles.d.ts +79 -12
- package/dist/types/chat/index.d.ts +1 -1
- package/dist/types/chat/types.d.ts +21 -0
- package/dist/types/chat/use-typewriter.d.ts +5 -3
- package/dist/types/widgets-v2/actions/download/constants.d.ts +12 -0
- package/dist/types/widgets-v2/actions/download/csv-item.d.ts +38 -0
- package/dist/types/widgets-v2/actions/download/icons.d.ts +6 -0
- package/dist/types/widgets-v2/actions/download/index.d.ts +3 -1
- package/dist/types/widgets-v2/actions/index.d.ts +1 -1
- package/dist/widgets-v2/actions.js +40 -36
- package/dist/widgets-v2/actions.js.map +1 -1
- package/dist/widgets-v2/bar.js +77 -84
- package/dist/widgets-v2/bar.js.map +1 -1
- package/dist/widgets-v2/category.js +50 -55
- package/dist/widgets-v2/category.js.map +1 -1
- package/dist/widgets-v2/formula.js +37 -43
- package/dist/widgets-v2/formula.js.map +1 -1
- package/dist/widgets-v2/histogram.js +138 -144
- package/dist/widgets-v2/histogram.js.map +1 -1
- package/dist/widgets-v2/markdown.js +18 -17
- package/dist/widgets-v2/markdown.js.map +1 -1
- package/dist/widgets-v2/pie.js +67 -73
- package/dist/widgets-v2/pie.js.map +1 -1
- package/dist/widgets-v2/scatterplot.js +75 -81
- package/dist/widgets-v2/scatterplot.js.map +1 -1
- package/dist/widgets-v2/spread.js +36 -41
- package/dist/widgets-v2/spread.js.map +1 -1
- package/dist/widgets-v2/table.js +46 -55
- package/dist/widgets-v2/table.js.map +1 -1
- package/dist/widgets-v2/timeseries.js +81 -87
- package/dist/widgets-v2/timeseries.js.map +1 -1
- package/dist/widgets-v2.js +1 -1
- package/package.json +1 -1
- package/src/chat/bubbles/styles.ts +5 -1
- package/src/chat/containers/chat-content.tsx +4 -1
- package/src/chat/containers/chat-footer.test.tsx +59 -0
- package/src/chat/containers/chat-footer.tsx +124 -36
- package/src/chat/containers/styles.ts +107 -16
- package/src/chat/feedback/styles.ts +11 -4
- package/src/chat/index.ts +1 -0
- package/src/chat/types.ts +22 -0
- package/src/chat/use-typewriter.ts +32 -24
- package/src/widgets-v2/actions/download/constants.ts +14 -0
- package/src/widgets-v2/actions/download/csv-item.test.tsx +77 -0
- package/src/widgets-v2/actions/download/csv-item.tsx +71 -0
- package/src/widgets-v2/actions/download/icons.tsx +10 -1
- package/src/widgets-v2/actions/download/index.ts +3 -1
- package/src/widgets-v2/actions/download/png-item.tsx +2 -1
- package/src/widgets-v2/actions/index.ts +5 -0
- package/src/widgets-v2/bar/download.tsx +16 -22
- package/src/widgets-v2/category/download.test.ts +9 -0
- package/src/widgets-v2/category/download.ts +16 -20
- package/src/widgets-v2/formula/download.tsx +23 -29
- package/src/widgets-v2/histogram/download.ts +22 -26
- package/src/widgets-v2/markdown/{download.ts → download.tsx} +5 -2
- package/src/widgets-v2/pie/download.ts +16 -20
- package/src/widgets-v2/scatterplot/download.ts +16 -20
- package/src/widgets-v2/spread/download.ts +23 -27
- package/src/widgets-v2/table/download.test.ts +10 -0
- package/src/widgets-v2/table/download.ts +11 -15
- package/src/widgets-v2/table/helpers.test.ts +19 -0
- package/src/widgets-v2/table/helpers.ts +7 -12
- package/src/widgets-v2/timeseries/download.ts +36 -40
- package/dist/png-item-BE9uEqlD.js +0 -45
- package/dist/png-item-BE9uEqlD.js.map +0 -1
- package/dist/table-C9IMbTr0.js +0 -385
- package/dist/table-C9IMbTr0.js.map +0 -1
- package/dist/types/chat/feedback/styles.d.ts +0 -211
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildCsvDownloadItem,
|
|
2
3
|
buildPngDownloadItem,
|
|
3
|
-
downloadToCSV,
|
|
4
4
|
type DownloadItem,
|
|
5
5
|
} from '../actions/download'
|
|
6
6
|
import type { PieWidgetData } from './types'
|
|
@@ -31,25 +31,21 @@ export function createPieDownloadConfig(args: {
|
|
|
31
31
|
}),
|
|
32
32
|
)
|
|
33
33
|
}
|
|
34
|
-
items.push(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
items.push(
|
|
35
|
+
buildCsvDownloadItem({
|
|
36
|
+
filename: args.filename,
|
|
37
|
+
getRows: () => {
|
|
38
|
+
const data = args.getData()
|
|
39
|
+
const rows: unknown[][] = [['series', 'name', 'value']]
|
|
40
|
+
for (const [i, series] of data.entries()) {
|
|
41
|
+
const seriesName = args.seriesNames?.[i] ?? `series_${i + 1}`
|
|
42
|
+
for (const slice of series) {
|
|
43
|
+
rows.push([seriesName, slice.name, slice.value])
|
|
44
|
+
}
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
filename: `${args.filename}.csv`,
|
|
50
|
-
revoke: handle.revoke,
|
|
51
|
-
})
|
|
52
|
-
},
|
|
53
|
-
})
|
|
46
|
+
return rows
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
)
|
|
54
50
|
return items
|
|
55
51
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildCsvDownloadItem,
|
|
2
3
|
buildPngDownloadItem,
|
|
3
|
-
downloadToCSV,
|
|
4
4
|
type DownloadItem,
|
|
5
5
|
} from '../actions/download'
|
|
6
6
|
import type { ScatterplotWidgetData } from './types'
|
|
@@ -30,25 +30,21 @@ export function createScatterplotDownloadConfig(args: {
|
|
|
30
30
|
}),
|
|
31
31
|
)
|
|
32
32
|
}
|
|
33
|
-
items.push(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
items.push(
|
|
34
|
+
buildCsvDownloadItem({
|
|
35
|
+
filename: args.filename,
|
|
36
|
+
getRows: () => {
|
|
37
|
+
const data = args.getData()
|
|
38
|
+
const rows: unknown[][] = [['series', 'x', 'y']]
|
|
39
|
+
for (const [i, series] of data.entries()) {
|
|
40
|
+
const seriesName = args.seriesNames?.[i] ?? `series_${i + 1}`
|
|
41
|
+
for (const [x, y] of series) {
|
|
42
|
+
rows.push([seriesName, x, y])
|
|
43
|
+
}
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
filename: `${args.filename}.csv`,
|
|
49
|
-
revoke: handle.revoke,
|
|
50
|
-
})
|
|
51
|
-
},
|
|
52
|
-
})
|
|
45
|
+
return rows
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
)
|
|
53
49
|
return items
|
|
54
50
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildCsvDownloadItem,
|
|
2
3
|
buildPngDownloadItem,
|
|
3
|
-
downloadToCSV,
|
|
4
4
|
type DownloadItem,
|
|
5
5
|
} from '../actions/download'
|
|
6
6
|
import type { SpreadWidgetData } from './types'
|
|
@@ -29,31 +29,27 @@ export function createSpreadDownloadConfig(args: {
|
|
|
29
29
|
}),
|
|
30
30
|
)
|
|
31
31
|
}
|
|
32
|
-
items.push(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
revoke: handle.revoke,
|
|
55
|
-
})
|
|
56
|
-
},
|
|
57
|
-
})
|
|
32
|
+
items.push(
|
|
33
|
+
buildCsvDownloadItem({
|
|
34
|
+
filename: args.filename,
|
|
35
|
+
getRows: () => {
|
|
36
|
+
const data = args.getData()
|
|
37
|
+
const rows: unknown[][] = [
|
|
38
|
+
['series', 'prefix', 'min', 'max', 'suffix', 'note'],
|
|
39
|
+
]
|
|
40
|
+
for (const item of data) {
|
|
41
|
+
rows.push([
|
|
42
|
+
item.series?.name ?? '',
|
|
43
|
+
item.prefix ?? '',
|
|
44
|
+
item.min,
|
|
45
|
+
item.max,
|
|
46
|
+
item.suffix ?? '',
|
|
47
|
+
item.note ?? '',
|
|
48
|
+
])
|
|
49
|
+
}
|
|
50
|
+
return rows
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
)
|
|
58
54
|
return items
|
|
59
55
|
}
|
|
@@ -61,6 +61,16 @@ describe('createTableDownloadConfig', () => {
|
|
|
61
61
|
expect(csvText).toBe('Name,Score\nAlpha,10\nBeta,20')
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
+
it('CSV guards formula-injection cells end-to-end', async () => {
|
|
65
|
+
const items = createTableDownloadConfig({
|
|
66
|
+
filename: 't',
|
|
67
|
+
getData: () => [{ id: 1, name: '=HYPERLINK("x")', score: 1 }],
|
|
68
|
+
columns,
|
|
69
|
+
})
|
|
70
|
+
await items.find((i) => i.id === 'csv')!.resolve()
|
|
71
|
+
expect(csvText).toBe('Name,Score\n"\'=HYPERLINK(""x"")",1')
|
|
72
|
+
})
|
|
73
|
+
|
|
64
74
|
it('CSV calls getData() at click time (not at config creation)', async () => {
|
|
65
75
|
let snapshot: TableWidgetData = data
|
|
66
76
|
const items = createTableDownloadConfig({
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
buildCsvDownloadItem,
|
|
3
|
+
buildPngDownloadItem,
|
|
4
|
+
type DownloadItem,
|
|
5
|
+
} from '../actions/download'
|
|
2
6
|
import { tableDataToCsv } from './helpers'
|
|
3
7
|
import type { TableColumn, TableWidgetData } from './types'
|
|
4
8
|
|
|
@@ -29,19 +33,11 @@ export function createTableDownloadConfig(opts: {
|
|
|
29
33
|
}),
|
|
30
34
|
)
|
|
31
35
|
}
|
|
32
|
-
items.push(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const url = URL.createObjectURL(blob)
|
|
39
|
-
return Promise.resolve({
|
|
40
|
-
url,
|
|
41
|
-
filename: `${opts.filename}.csv`,
|
|
42
|
-
revoke: () => URL.revokeObjectURL(url),
|
|
43
|
-
})
|
|
44
|
-
},
|
|
45
|
-
})
|
|
36
|
+
items.push(
|
|
37
|
+
buildCsvDownloadItem({
|
|
38
|
+
filename: opts.filename,
|
|
39
|
+
getCsv: () => tableDataToCsv(opts.getData(), opts.columns),
|
|
40
|
+
}),
|
|
41
|
+
)
|
|
46
42
|
return items
|
|
47
43
|
}
|
|
@@ -211,4 +211,23 @@ describe('tableDataToCsv', () => {
|
|
|
211
211
|
expect(csv).toContain('"[""x"",""y""]"')
|
|
212
212
|
expect(csv).toContain('"{""z"":1}"')
|
|
213
213
|
})
|
|
214
|
+
|
|
215
|
+
it('guards against formula injection by prefixing leading =,+,-,@', () => {
|
|
216
|
+
const csv = tableDataToCsv(
|
|
217
|
+
[
|
|
218
|
+
{ id: 1, name: '=SUM(A1)', score: '+1' },
|
|
219
|
+
{ id: 2, name: '-2', score: '@cmd' },
|
|
220
|
+
],
|
|
221
|
+
cols,
|
|
222
|
+
)
|
|
223
|
+
const lines = csv.split('\n')
|
|
224
|
+
expect(lines[1]).toBe("'=SUM(A1),'+1")
|
|
225
|
+
expect(lines[2]).toBe("'-2,'@cmd")
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('still wraps a formula cell that also contains a comma', () => {
|
|
229
|
+
const csv = tableDataToCsv([{ id: 1, name: '=A1,B2', score: 1 }], cols)
|
|
230
|
+
// Prefixed against injection, then quoted for the embedded comma.
|
|
231
|
+
expect(csv.split('\n')[1]).toBe('"\'=A1,B2",1')
|
|
232
|
+
})
|
|
214
233
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toCsvString } from '../actions/download'
|
|
1
2
|
import type {
|
|
2
3
|
TableColumn,
|
|
3
4
|
TableRow,
|
|
@@ -102,20 +103,14 @@ export function tableDataToCsv(
|
|
|
102
103
|
data: TableWidgetData,
|
|
103
104
|
columns: readonly TableColumn[],
|
|
104
105
|
): string {
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
// Pre-stringify with the table's own header/cell rules, then delegate the
|
|
107
|
+
// CSV assembly (quoting + spreadsheet formula-injection guard) to the shared
|
|
108
|
+
// `toCsvString` so escaping stays identical to every other widget's export.
|
|
109
|
+
const rows: string[][] = [columns.map((c) => stringifyHeader(c.label))]
|
|
107
110
|
for (const row of data) {
|
|
108
|
-
|
|
109
|
-
lines.push(cells.join(','))
|
|
111
|
+
rows.push(columns.map((c) => stringifyCell(row[c.id])))
|
|
110
112
|
}
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function csvEscape(value: string): string {
|
|
115
|
-
if (/[",\n\r]/.test(value)) {
|
|
116
|
-
return `"${value.replace(/"/g, '""')}"`
|
|
117
|
-
}
|
|
118
|
-
return value
|
|
113
|
+
return toCsvString(rows)
|
|
119
114
|
}
|
|
120
115
|
|
|
121
116
|
function stringifyHeader(label: unknown): string {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildCsvDownloadItem,
|
|
2
3
|
buildPngDownloadItem,
|
|
3
|
-
downloadToCSV,
|
|
4
4
|
type DownloadItem,
|
|
5
5
|
} from '../actions/download'
|
|
6
6
|
import type { TimeseriesWidgetData } from './types'
|
|
@@ -31,51 +31,47 @@ export function createTimeseriesDownloadConfig(args: {
|
|
|
31
31
|
}),
|
|
32
32
|
)
|
|
33
33
|
}
|
|
34
|
-
items.push(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
items.push(
|
|
35
|
+
buildCsvDownloadItem({
|
|
36
|
+
filename: args.filename,
|
|
37
|
+
getRows: () => {
|
|
38
|
+
const data = args.getData()
|
|
39
|
+
const seriesCount = data.length
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
// Collect every unique time, preserving insertion order.
|
|
42
|
+
const timeKeys: (Date | number | string)[] = []
|
|
43
|
+
const seenKeys = new Set<string>()
|
|
44
|
+
for (const series of data) {
|
|
45
|
+
for (const point of series) {
|
|
46
|
+
const key = String(point.name)
|
|
47
|
+
if (!seenKeys.has(key)) {
|
|
48
|
+
seenKeys.add(key)
|
|
49
|
+
timeKeys.push(point.name)
|
|
50
|
+
}
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
|
-
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
// Build a quick lookup per series for O(rows × series) emit.
|
|
55
|
+
const lookups = data.map(
|
|
56
|
+
(series) => new Map(series.map((p) => [String(p.name), p.value])),
|
|
57
|
+
)
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
const header: unknown[] = ['time']
|
|
60
|
+
for (let i = 0; i < seriesCount; i++) {
|
|
61
|
+
header.push(args.seriesNames?.[i] ?? `series_${i + 1}`)
|
|
62
|
+
}
|
|
63
|
+
const rows: unknown[][] = [header]
|
|
64
|
+
for (const key of timeKeys) {
|
|
65
|
+
const row: unknown[] = [formatTime(key)]
|
|
66
|
+
const lookupKey = String(key)
|
|
67
|
+
for (const lookup of lookups) row.push(lookup.get(lookupKey) ?? '')
|
|
68
|
+
rows.push(row)
|
|
69
|
+
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
revoke: handle.revoke,
|
|
76
|
-
})
|
|
77
|
-
},
|
|
78
|
-
})
|
|
71
|
+
return rows
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
)
|
|
79
75
|
return items
|
|
80
76
|
}
|
|
81
77
|
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { jsx as n } from "react/jsx-runtime";
|
|
2
|
-
import { d as a } from "./exports-Cx-f6m6U.js";
|
|
3
|
-
import { c as r } from "react/compiler-runtime";
|
|
4
|
-
import { SvgIcon as i } from "@mui/material";
|
|
5
|
-
import { ImageOutlined as c } from "@mui/icons-material";
|
|
6
|
-
function m(o) {
|
|
7
|
-
const e = r(2);
|
|
8
|
-
let l;
|
|
9
|
-
return e[0] !== o ? (l = /* @__PURE__ */ n(c, { ...o }), e[0] = o, e[1] = l) : l = e[1], l;
|
|
10
|
-
}
|
|
11
|
-
function s(o) {
|
|
12
|
-
const e = r(3);
|
|
13
|
-
let l;
|
|
14
|
-
e[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (l = /* @__PURE__ */ n("path", { fill: "currentColor", d: "M4.313 11.25h2.25v-1.125H4.688v-2.25h1.875V6.75h-2.25a.726.726 0 0 0-.535.216.726.726 0 0 0-.216.534v3c0 .213.072.39.216.534a.726.726 0 0 0 .534.216Zm2.925 0h2.25c.212 0 .39-.072.534-.216a.726.726 0 0 0 .216-.534V9.375a.931.931 0 0 0-.216-.59.658.658 0 0 0-.534-.273H8.363v-.637h1.875V6.75h-2.25a.726.726 0 0 0-.535.216.726.726 0 0 0-.216.534v1.125c0 .213.072.403.216.572a.675.675 0 0 0 .534.253h1.126v.675H7.238v1.125Zm4.95 0h1.124l1.313-4.5H13.5l-.75 2.588L12 6.75h-1.125l1.313 4.5ZM3 15c-.413 0-.766-.147-1.06-.44a1.445 1.445 0 0 1-.44-1.06v-9c0-.412.147-.766.44-1.06C2.235 3.148 2.588 3 3 3h12c.412 0 .766.147 1.06.44.293.294.44.648.44 1.06v9c0 .412-.147.766-.44 1.06-.294.293-.647.44-1.06.44H3Zm0-1.5h12v-9H3v9Z" }), e[0] = l) : l = e[0];
|
|
15
|
-
let t;
|
|
16
|
-
return e[1] !== o ? (t = /* @__PURE__ */ n(i, { viewBox: "0 0 18 18", ...o, children: l }), e[1] = o, e[2] = t) : t = e[2], t;
|
|
17
|
-
}
|
|
18
|
-
function b(o) {
|
|
19
|
-
return {
|
|
20
|
-
id: "png",
|
|
21
|
-
label: o.label ?? "PNG",
|
|
22
|
-
icon: /* @__PURE__ */ n(m, { fontSize: "small" }),
|
|
23
|
-
resolve: async () => {
|
|
24
|
-
const e = o.getCaptureEl();
|
|
25
|
-
if (!e)
|
|
26
|
-
throw new Error("[widgets-v2] No PNG capture element available");
|
|
27
|
-
const l = await a({
|
|
28
|
-
element: e,
|
|
29
|
-
pixelRatio: o.pixelRatio,
|
|
30
|
-
backgroundColor: o.backgroundColor
|
|
31
|
-
});
|
|
32
|
-
return {
|
|
33
|
-
url: l.url,
|
|
34
|
-
filename: `${o.filename}.png`,
|
|
35
|
-
revoke: l.revoke
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
export {
|
|
41
|
-
s as C,
|
|
42
|
-
m as P,
|
|
43
|
-
b
|
|
44
|
-
};
|
|
45
|
-
//# sourceMappingURL=png-item-BE9uEqlD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"png-item-BE9uEqlD.js","sources":["../src/widgets-v2/actions/download/icons.tsx","../src/widgets-v2/actions/download/png-item.tsx"],"sourcesContent":["import { SvgIcon, type SvgIconProps } from '@mui/material'\nimport { ImageOutlined } from '@mui/icons-material'\n\n/**\n * Generic \"image\" glyph used for the PNG download item. Wraps MUI's\n * `ImageOutlined` so callers can swap it via the per-widget download\n * config's `icon` override without pulling MUI directly.\n */\nexport function PNGIcon(props: SvgIconProps) {\n return <ImageOutlined {...props} />\n}\n\n/**\n * \"CSV\" rectangle with the letters spelled inside — matches v1 visual and is\n * easier to recognise in a download menu than a generic table glyph.\n */\nexport function CSVIcon(props: SvgIconProps) {\n return (\n <SvgIcon viewBox='0 0 18 18' {...props}>\n <path\n fill='currentColor'\n d='M4.313 11.25h2.25v-1.125H4.688v-2.25h1.875V6.75h-2.25a.726.726 0 0 0-.535.216.726.726 0 0 0-.216.534v3c0 .213.072.39.216.534a.726.726 0 0 0 .534.216Zm2.925 0h2.25c.212 0 .39-.072.534-.216a.726.726 0 0 0 .216-.534V9.375a.931.931 0 0 0-.216-.59.658.658 0 0 0-.534-.273H8.363v-.637h1.875V6.75h-2.25a.726.726 0 0 0-.535.216.726.726 0 0 0-.216.534v1.125c0 .213.072.403.216.572a.675.675 0 0 0 .534.253h1.126v.675H7.238v1.125Zm4.95 0h1.124l1.313-4.5H13.5l-.75 2.588L12 6.75h-1.125l1.313 4.5ZM3 15c-.413 0-.766-.147-1.06-.44a1.445 1.445 0 0 1-.44-1.06v-9c0-.412.147-.766.44-1.06C2.235 3.148 2.588 3 3 3h12c.412 0 .766.147 1.06.44.293.294.44.648.44 1.06v9c0 .412-.147.766-.44 1.06-.294.293-.647.44-1.06.44H3Zm0-1.5h12v-9H3v9Z'\n />\n </SvgIcon>\n )\n}\n","import { downloadDOMToPNG } from './exports'\nimport { PNGIcon } from './icons'\nimport type { DownloadItem } from './types'\n\nexport interface BuildPngDownloadItemArgs {\n /** Base filename (without extension). The item appends `.png`. */\n filename: string\n /**\n * Reads the capture element to rasterise. Called at click time so the\n * download config doesn't capture a stale reference. Wire it to\n * `() => getCaptureEl(id)` from `widgets-v2/stores`.\n */\n getCaptureEl: () => HTMLElement | null\n /** html2canvas `scale`. Default 2. */\n pixelRatio?: number\n /** html2canvas `backgroundColor`. Default transparent (`null`). */\n backgroundColor?: string | null\n /** Override the menu label. Default `'PNG'`. */\n label?: string\n}\n\n/**\n * Builds the standard PNG `DownloadItem` used by every per-widget download\n * config. Centralised so the menu label, icon, error message, and filename\n * suffix stay consistent across widgets without each `create*DownloadConfig`\n * re-deriving the same shape.\n */\nexport function buildPngDownloadItem(\n args: BuildPngDownloadItemArgs,\n): DownloadItem {\n return {\n id: 'png',\n label: args.label ?? 'PNG',\n icon: <PNGIcon fontSize='small' />,\n resolve: async () => {\n const el = args.getCaptureEl()\n if (!el) {\n throw new Error('[widgets-v2] No PNG capture element available')\n }\n const handle = await downloadDOMToPNG({\n element: el,\n pixelRatio: args.pixelRatio,\n backgroundColor: args.backgroundColor,\n })\n return {\n url: handle.url,\n filename: `${args.filename}.png`,\n revoke: handle.revoke,\n }\n },\n }\n}\n"],"names":["PNGIcon","props","$","_c","t0","jsx","ImageOutlined","CSVIcon","Symbol","for","t1","SvgIcon","buildPngDownloadItem","args","id","label","icon","resolve","el","getCaptureEl","Error","handle","downloadDOMToPNG","element","pixelRatio","backgroundColor","url","filename","revoke"],"mappings":";;;;;AAQO,SAAAA,EAAAC,GAAA;AAAA,QAAAC,IAAAC,EAAA,CAAA;AAAA,MAAAC;AAAA,SAAAF,SAAAD,KACEG,IAAA,gBAAAC,EAACC,GAAA,EAAa,GAAKL,EAAAA,CAAK,GAAIC,OAAAD,GAAAC,OAAAE,KAAAA,IAAAF,EAAA,CAAA,GAA5BE;AAA4B;AAO9B,SAAAG,EAAAN,GAAA;AAAA,QAAAC,IAAAC,EAAA,CAAA;AAAA,MAAAC;AAAA,EAAAF,EAAA,CAAA,MAAAM,uBAAAC,IAAA,2BAAA,KAGDL,IAAA,gBAAAC,EAAA,QAAA,EACO,MAAA,gBACH,GAAA,gtBAA8sB,GAChtBH,OAAAE,KAAAA,IAAAF,EAAA,CAAA;AAAA,MAAAQ;AAAA,SAAAR,SAAAD,KAJJS,sBAACC,GAAA,EAAgB,SAAA,aAAW,GAAKV,GAC/BG,UAAAA,GAIF,GAAUF,OAAAD,GAAAC,OAAAQ,KAAAA,IAAAR,EAAA,CAAA,GALVQ;AAKU;ACIP,SAASE,EACdC,GACc;AACd,SAAO;AAAA,IACLC,IAAI;AAAA,IACJC,OAAOF,EAAKE,SAAS;AAAA,IACrBC,MAAM,gBAAAX,EAACL,GAAA,EAAQ,UAAS,QAAA,CAAO;AAAA,IAC/BiB,SAAS,YAAY;AACnB,YAAMC,IAAKL,EAAKM,aAAAA;AAChB,UAAI,CAACD;AACH,cAAM,IAAIE,MAAM,+CAA+C;AAEjE,YAAMC,IAAS,MAAMC,EAAiB;AAAA,QACpCC,SAASL;AAAAA,QACTM,YAAYX,EAAKW;AAAAA,QACjBC,iBAAiBZ,EAAKY;AAAAA,MAAAA,CACvB;AACD,aAAO;AAAA,QACLC,KAAKL,EAAOK;AAAAA,QACZC,UAAU,GAAGd,EAAKc,QAAQ;AAAA,QAC1BC,QAAQP,EAAOO;AAAAA,MAAAA;AAAAA,IAEnB;AAAA,EAAA;AAEJ;"}
|