@carto/ps-react-ui 6.0.0 → 6.1.1
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/actions-BWlS5tpP.js +1150 -0
- package/dist/actions-BWlS5tpP.js.map +1 -0
- package/dist/category-alzjKzvQ.js +839 -0
- package/dist/category-alzjKzvQ.js.map +1 -0
- package/dist/{download-Vlzn2jzb.js → download-B4sykOFm.js} +94 -83
- package/dist/download-B4sykOFm.js.map +1 -0
- package/dist/{echart-DjcbtKgi.js → echart-CV8F50gn.js} +6 -5
- package/dist/echart-CV8F50gn.js.map +1 -0
- package/dist/{formula-CdHWtepq.js → formula-DJS-JLpk.js} +10 -16
- package/dist/{formula-CdHWtepq.js.map → formula-DJS-JLpk.js.map} +1 -1
- package/dist/legend.js +34 -21
- package/dist/legend.js.map +1 -1
- package/dist/{markdown-CY-67NRE.js → markdown-Bn2nKCbn.js} +12 -18
- package/dist/{markdown-CY-67NRE.js.map → markdown-Bn2nKCbn.js.map} +1 -1
- package/dist/{range-DHQSsl2_.js → range-BpU2zBmq.js} +2 -2
- package/dist/{range-DHQSsl2_.js.map → range-BpU2zBmq.js.map} +1 -1
- package/dist/{spread-DuM5oTI1.js → spread-CQwFIwuz.js} +10 -16
- package/dist/{spread-DuM5oTI1.js.map → spread-CQwFIwuz.js.map} +1 -1
- package/dist/{stores-B8CHbgw-.js → stores-DcBOZlU3.js} +30 -24
- package/dist/stores-DcBOZlU3.js.map +1 -0
- package/dist/{table-BE4uO9pp.js → table-DEdW_fDj.js} +7 -13
- package/dist/{table-BE4uO9pp.js.map → table-DEdW_fDj.js.map} +1 -1
- package/dist/types/legend/stores/types.d.ts +4 -2
- package/dist/types/widgets/actions/download/csv-item.d.ts +3 -3
- package/dist/types/widgets/actions/download/index.d.ts +2 -1
- package/dist/types/widgets/actions/download/png-item.d.ts +2 -2
- package/dist/types/widgets/actions/download/types.d.ts +12 -1
- package/dist/types/widgets/actions/download/widget-download.d.ts +26 -0
- package/dist/types/widgets/actions/fullscreen/fullscreen.d.ts +20 -13
- package/dist/types/widgets/actions/index.d.ts +1 -1
- package/dist/types/widgets/actions/searcher/filter.d.ts +3 -2
- package/dist/types/widgets/actions/searcher/searcher-toggle.d.ts +3 -0
- package/dist/types/widgets/bar/download.d.ts +6 -20
- package/dist/types/widgets/category/category-ui.d.ts +9 -1
- package/dist/types/widgets/category/download.d.ts +5 -10
- package/dist/types/widgets/category/helpers.d.ts +10 -0
- package/dist/types/widgets/echart/style.d.ts +1 -0
- package/dist/types/widgets/formula/download.d.ts +5 -15
- package/dist/types/widgets/histogram/download.d.ts +3 -8
- package/dist/types/widgets/index.d.ts +1 -1
- package/dist/types/widgets/markdown/download.d.ts +5 -11
- package/dist/types/widgets/pie/download.d.ts +3 -8
- package/dist/types/widgets/scatterplot/download.d.ts +6 -11
- package/dist/types/widgets/spread/download.d.ts +6 -12
- package/dist/types/widgets/stores/index.d.ts +1 -1
- package/dist/types/widgets/stores/widget-store-registry.d.ts +14 -0
- package/dist/types/widgets/table/download.d.ts +7 -13
- package/dist/types/widgets/timeseries/download.d.ts +7 -12
- package/dist/types/widgets/toolbox/toolbox.d.ts +1 -1
- package/dist/types/widgets/wrapper/style.d.ts +31 -2
- package/dist/types/widgets/wrapper/widget-actions.d.ts +2 -2
- package/dist/types/widgets/wrapper/widget-wrapper.d.ts +2 -2
- package/dist/{utils-BStJWhZN.js → utils-C-eWwOhE.js} +2 -2
- package/dist/{utils-BStJWhZN.js.map → utils-C-eWwOhE.js.map} +1 -1
- package/dist/widgets/actions.js +4 -4
- package/dist/widgets/bar.js +11 -17
- package/dist/widgets/bar.js.map +1 -1
- package/dist/widgets/category.js +1 -1
- package/dist/widgets/echart.js +1 -1
- package/dist/widgets/formula.js +1 -1
- package/dist/widgets/histogram.js +14 -20
- package/dist/widgets/histogram.js.map +1 -1
- package/dist/widgets/markdown.js +1 -1
- package/dist/widgets/pie.js +13 -19
- package/dist/widgets/pie.js.map +1 -1
- package/dist/widgets/range.js +1 -1
- package/dist/widgets/scatterplot.js +13 -19
- package/dist/widgets/scatterplot.js.map +1 -1
- package/dist/widgets/spread.js +1 -1
- package/dist/widgets/stores.js +2 -2
- package/dist/widgets/table.js +1 -1
- package/dist/widgets/timeseries.js +16 -22
- package/dist/widgets/timeseries.js.map +1 -1
- package/dist/widgets/utils.js +1 -1
- package/dist/widgets.js +364 -376
- package/dist/widgets.js.map +1 -1
- package/dist/{zoom-toggle-D_VuA01l.js → zoom-toggle-MmLjMTEw.js} +2 -2
- package/dist/{zoom-toggle-D_VuA01l.js.map → zoom-toggle-MmLjMTEw.js.map} +1 -1
- package/package.json +1 -1
- package/src/legend/components/legend-item/legend-item-ui.test.tsx +22 -1
- package/src/legend/components/legend-ramp/legend-ramp-ui.tsx +22 -6
- package/src/legend/stores/types.ts +4 -2
- package/src/widgets/actions/download/csv-item.test.tsx +3 -3
- package/src/widgets/actions/download/csv-item.tsx +6 -6
- package/src/widgets/actions/download/download.tsx +5 -3
- package/src/widgets/actions/download/index.ts +2 -1
- package/src/widgets/actions/download/png-item.test.tsx +5 -3
- package/src/widgets/actions/download/png-item.tsx +4 -4
- package/src/widgets/actions/download/types.ts +15 -1
- package/src/widgets/actions/download/widget-download.test.ts +30 -0
- package/src/widgets/actions/download/widget-download.ts +41 -0
- package/src/widgets/actions/fullscreen/fullscreen-store.test.tsx +155 -0
- package/src/widgets/actions/fullscreen/fullscreen.test.tsx +39 -41
- package/src/widgets/actions/fullscreen/fullscreen.tsx +198 -96
- package/src/widgets/actions/index.ts +3 -0
- package/src/widgets/actions/searcher/filter.test.ts +28 -0
- package/src/widgets/actions/searcher/filter.ts +22 -4
- package/src/widgets/actions/searcher/searcher-toggle.tsx +16 -8
- package/src/widgets/actions/searcher/searcher.test.tsx +22 -0
- package/src/widgets/actions/searcher/searcher.tsx +17 -7
- package/src/widgets/bar/download.test.tsx +5 -3
- package/src/widgets/bar/download.tsx +11 -35
- package/src/widgets/category/category-ui.tsx +26 -14
- package/src/widgets/category/category.tsx +37 -2
- package/src/widgets/category/components/category-legend.tsx +1 -2
- package/src/widgets/category/download.test.ts +4 -2
- package/src/widgets/category/download.ts +14 -27
- package/src/widgets/category/helpers.test.ts +17 -0
- package/src/widgets/category/helpers.ts +16 -0
- package/src/widgets/echart/style.ts +6 -0
- package/src/widgets/formula/download.test.tsx +3 -1
- package/src/widgets/formula/download.tsx +10 -30
- package/src/widgets/histogram/download.test.ts +6 -4
- package/src/widgets/histogram/download.ts +13 -26
- package/src/widgets/index.ts +1 -0
- package/src/widgets/markdown/download.test.ts +2 -2
- package/src/widgets/markdown/download.tsx +11 -26
- package/src/widgets/pie/download.test.ts +7 -3
- package/src/widgets/pie/download.ts +12 -25
- package/src/widgets/scatterplot/download.test.ts +4 -2
- package/src/widgets/scatterplot/download.ts +15 -28
- package/src/widgets/spread/download.test.ts +3 -1
- package/src/widgets/spread/download.ts +13 -27
- package/src/widgets/stores/index.ts +2 -0
- package/src/widgets/stores/widget-store-registry.test.ts +25 -0
- package/src/widgets/stores/widget-store-registry.ts +22 -0
- package/src/widgets/table/download.test.ts +5 -3
- package/src/widgets/table/download.ts +15 -29
- package/src/widgets/timeseries/download.test.ts +6 -4
- package/src/widgets/timeseries/download.ts +16 -29
- package/src/widgets/toolbox/toolbox.tsx +1 -1
- package/src/widgets/wrapper/style.ts +38 -2
- package/src/widgets/wrapper/widget-actions.tsx +2 -2
- package/src/widgets/wrapper/widget-wrapper.test.tsx +32 -2
- package/src/widgets/wrapper/widget-wrapper.tsx +62 -36
- package/dist/actions-KsX1xdDM.js +0 -1024
- package/dist/actions-KsX1xdDM.js.map +0 -1
- package/dist/category-CsdDqDzo.js +0 -833
- package/dist/category-CsdDqDzo.js.map +0 -1
- package/dist/download-Vlzn2jzb.js.map +0 -1
- package/dist/echart-DjcbtKgi.js.map +0 -1
- package/dist/stores-B8CHbgw-.js.map +0 -1
|
@@ -1,43 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DOWNLOAD_ITEM_IDS,
|
|
3
3
|
MarkdownIcon,
|
|
4
|
-
|
|
4
|
+
withPngItem,
|
|
5
5
|
toCsvString,
|
|
6
6
|
triggerLinkDownload,
|
|
7
7
|
type DownloadItem,
|
|
8
|
+
type WidgetDownloadArgs,
|
|
8
9
|
} from '../actions/download'
|
|
9
10
|
import type { MarkdownWidgetData } from './types'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Download menu items for the Markdown widget. Always includes a `.md`
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Download menu items for the Markdown widget. Always includes a `.md` item
|
|
14
|
+
* that saves the markdown source verbatim. When `getCaptureEl` is given,
|
|
15
|
+
* prepends a PNG item that rasterises the captured element via
|
|
15
16
|
* `html2canvas`.
|
|
16
17
|
*/
|
|
17
|
-
export function createMarkdownDownloadConfig(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
pngPixelRatio?: number
|
|
22
|
-
pngBackgroundColor?: string | null
|
|
23
|
-
}): DownloadItem[] {
|
|
24
|
-
const items: DownloadItem[] = []
|
|
25
|
-
if (args.getCaptureEl) {
|
|
26
|
-
items.push(
|
|
27
|
-
buildPngDownloadItem({
|
|
28
|
-
filename: args.filename,
|
|
29
|
-
getCaptureEl: args.getCaptureEl,
|
|
30
|
-
pixelRatio: args.pngPixelRatio,
|
|
31
|
-
backgroundColor: args.pngBackgroundColor,
|
|
32
|
-
}),
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
items.push({
|
|
18
|
+
export function createMarkdownDownloadConfig(
|
|
19
|
+
args: WidgetDownloadArgs<MarkdownWidgetData>,
|
|
20
|
+
): DownloadItem[] {
|
|
21
|
+
return withPngItem(args, {
|
|
36
22
|
id: DOWNLOAD_ITEM_IDS.markdown,
|
|
37
23
|
label: 'Markdown',
|
|
38
24
|
icon: <MarkdownIcon fontSize='small' />,
|
|
39
|
-
resolve: () => {
|
|
40
|
-
const data = args.getData()
|
|
25
|
+
resolve: (ctx) => {
|
|
26
|
+
const data = args.getData(ctx)
|
|
41
27
|
const blob = new Blob([data.content ?? ''], {
|
|
42
28
|
type: 'text/markdown;charset=utf-8;',
|
|
43
29
|
})
|
|
@@ -49,7 +35,6 @@ export function createMarkdownDownloadConfig(args: {
|
|
|
49
35
|
})
|
|
50
36
|
},
|
|
51
37
|
})
|
|
52
|
-
return items
|
|
53
38
|
}
|
|
54
39
|
|
|
55
40
|
// Re-export pure helpers consumers may want when building a custom Download item.
|
|
@@ -51,7 +51,9 @@ describe('createPieDownloadConfig', () => {
|
|
|
51
51
|
getCaptureEl: () => null,
|
|
52
52
|
})
|
|
53
53
|
expect(items.map((i) => i.id)).toEqual(['png', 'csv'])
|
|
54
|
-
await expect(items[0]!.resolve()).rejects.toThrow(
|
|
54
|
+
await expect(items[0]!.resolve({ id: null })).rejects.toThrow(
|
|
55
|
+
/No PNG capture element/,
|
|
56
|
+
)
|
|
55
57
|
})
|
|
56
58
|
|
|
57
59
|
it('CSV resolve uses default series_<n> when seriesNames is absent', async () => {
|
|
@@ -59,7 +61,9 @@ describe('createPieDownloadConfig', () => {
|
|
|
59
61
|
filename: 'p',
|
|
60
62
|
getData: () => multi,
|
|
61
63
|
})
|
|
62
|
-
const handle = await items
|
|
64
|
+
const handle = await items
|
|
65
|
+
.find((i) => i.id === 'csv')!
|
|
66
|
+
.resolve({ id: null })
|
|
63
67
|
expect(handle.filename).toBe('p.csv')
|
|
64
68
|
expect(csvText).toBe('series,name,value\nseries_1,a,1\nseries_2,c,3')
|
|
65
69
|
handle.revoke?.()
|
|
@@ -72,7 +76,7 @@ describe('createPieDownloadConfig', () => {
|
|
|
72
76
|
getData: () => multi,
|
|
73
77
|
seriesNames: ['alpha', 'beta'],
|
|
74
78
|
})
|
|
75
|
-
await items.find((i) => i.id === 'csv')!.resolve()
|
|
79
|
+
await items.find((i) => i.id === 'csv')!.resolve({ id: null })
|
|
76
80
|
expect(csvText).toBe('series,name,value\nalpha,a,1\nbeta,c,3')
|
|
77
81
|
})
|
|
78
82
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildCsvDownloadItem,
|
|
3
|
-
|
|
3
|
+
withPngItem,
|
|
4
4
|
type DownloadItem,
|
|
5
|
+
type WidgetDownloadArgs,
|
|
5
6
|
} from '../actions/download'
|
|
6
7
|
import type { PieWidgetData } from './types'
|
|
7
8
|
|
|
@@ -9,33 +10,20 @@ import type { PieWidgetData } from './types'
|
|
|
9
10
|
* Download menu items for the Pie widget. Always includes a CSV item with
|
|
10
11
|
* `series, name, value` columns (one row per slice; series labels come from
|
|
11
12
|
* `seriesNames` if provided, else `series_<n>`). When `getCaptureEl` is
|
|
12
|
-
*
|
|
13
|
+
* given, prepends a PNG item that rasterises the captured element via
|
|
13
14
|
* `html2canvas`.
|
|
14
15
|
*/
|
|
15
|
-
export function createPieDownloadConfig(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}): DownloadItem[] {
|
|
23
|
-
const items: DownloadItem[] = []
|
|
24
|
-
if (args.getCaptureEl) {
|
|
25
|
-
items.push(
|
|
26
|
-
buildPngDownloadItem({
|
|
27
|
-
filename: args.filename,
|
|
28
|
-
getCaptureEl: args.getCaptureEl,
|
|
29
|
-
pixelRatio: args.pngPixelRatio,
|
|
30
|
-
backgroundColor: args.pngBackgroundColor,
|
|
31
|
-
}),
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
items.push(
|
|
16
|
+
export function createPieDownloadConfig(
|
|
17
|
+
args: WidgetDownloadArgs<PieWidgetData> & {
|
|
18
|
+
seriesNames?: readonly string[]
|
|
19
|
+
},
|
|
20
|
+
): DownloadItem[] {
|
|
21
|
+
return withPngItem(
|
|
22
|
+
args,
|
|
35
23
|
buildCsvDownloadItem({
|
|
36
24
|
filename: args.filename,
|
|
37
|
-
getRows: () => {
|
|
38
|
-
const data = args.getData()
|
|
25
|
+
getRows: (ctx) => {
|
|
26
|
+
const data = args.getData(ctx)
|
|
39
27
|
const rows: unknown[][] = [['series', 'name', 'value']]
|
|
40
28
|
for (const [i, series] of data.entries()) {
|
|
41
29
|
const seriesName = args.seriesNames?.[i] ?? `series_${i + 1}`
|
|
@@ -47,5 +35,4 @@ export function createPieDownloadConfig(args: {
|
|
|
47
35
|
},
|
|
48
36
|
}),
|
|
49
37
|
)
|
|
50
|
-
return items
|
|
51
38
|
}
|
|
@@ -52,7 +52,9 @@ describe('createScatterplotDownloadConfig', () => {
|
|
|
52
52
|
filename: 's',
|
|
53
53
|
getData: () => data,
|
|
54
54
|
})
|
|
55
|
-
const handle = await items
|
|
55
|
+
const handle = await items
|
|
56
|
+
.find((i) => i.id === 'csv')!
|
|
57
|
+
.resolve({ id: null })
|
|
56
58
|
expect(handle.filename).toBe('s.csv')
|
|
57
59
|
expect(csvText).toBe(
|
|
58
60
|
'series,x,y\nseries_1,1,10\nseries_1,2,20\nseries_2,3,30',
|
|
@@ -65,7 +67,7 @@ describe('createScatterplotDownloadConfig', () => {
|
|
|
65
67
|
getData: () => data,
|
|
66
68
|
seriesNames: ['A', 'B'],
|
|
67
69
|
})
|
|
68
|
-
await items.find((i) => i.id === 'csv')!.resolve()
|
|
70
|
+
await items.find((i) => i.id === 'csv')!.resolve({ id: null })
|
|
69
71
|
expect(csvText.split('\n')[1]).toBe('A,1,10')
|
|
70
72
|
})
|
|
71
73
|
})
|
|
@@ -1,40 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildCsvDownloadItem,
|
|
3
|
-
|
|
3
|
+
withPngItem,
|
|
4
4
|
type DownloadItem,
|
|
5
|
+
type WidgetDownloadArgs,
|
|
5
6
|
} from '../actions/download'
|
|
6
7
|
import type { ScatterplotWidgetData } from './types'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* Download menu items for the Scatterplot widget. Always includes a CSV
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Download menu items for the Scatterplot widget. Always includes a CSV item
|
|
11
|
+
* with `series, x, y` columns (one row per point). When `getCaptureEl` is
|
|
12
|
+
* given, prepends a PNG item that rasterises the captured element via
|
|
13
|
+
* `html2canvas`.
|
|
13
14
|
*/
|
|
14
|
-
export function createScatterplotDownloadConfig(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}): DownloadItem[] {
|
|
22
|
-
const items: DownloadItem[] = []
|
|
23
|
-
if (args.getCaptureEl) {
|
|
24
|
-
items.push(
|
|
25
|
-
buildPngDownloadItem({
|
|
26
|
-
filename: args.filename,
|
|
27
|
-
getCaptureEl: args.getCaptureEl,
|
|
28
|
-
pixelRatio: args.pngPixelRatio,
|
|
29
|
-
backgroundColor: args.pngBackgroundColor,
|
|
30
|
-
}),
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
items.push(
|
|
15
|
+
export function createScatterplotDownloadConfig(
|
|
16
|
+
args: WidgetDownloadArgs<ScatterplotWidgetData> & {
|
|
17
|
+
seriesNames?: readonly string[]
|
|
18
|
+
},
|
|
19
|
+
): DownloadItem[] {
|
|
20
|
+
return withPngItem(
|
|
21
|
+
args,
|
|
34
22
|
buildCsvDownloadItem({
|
|
35
23
|
filename: args.filename,
|
|
36
|
-
getRows: () => {
|
|
37
|
-
const data = args.getData()
|
|
24
|
+
getRows: (ctx) => {
|
|
25
|
+
const data = args.getData(ctx)
|
|
38
26
|
const rows: unknown[][] = [['series', 'x', 'y']]
|
|
39
27
|
for (const [i, series] of data.entries()) {
|
|
40
28
|
const seriesName = args.seriesNames?.[i] ?? `series_${i + 1}`
|
|
@@ -46,5 +34,4 @@ export function createScatterplotDownloadConfig(args: {
|
|
|
46
34
|
},
|
|
47
35
|
}),
|
|
48
36
|
)
|
|
49
|
-
return items
|
|
50
37
|
}
|
|
@@ -55,7 +55,9 @@ describe('createSpreadDownloadConfig', () => {
|
|
|
55
55
|
filename: 's',
|
|
56
56
|
getData: () => data,
|
|
57
57
|
})
|
|
58
|
-
const handle = await items
|
|
58
|
+
const handle = await items
|
|
59
|
+
.find((i) => i.id === 'csv')!
|
|
60
|
+
.resolve({ id: null })
|
|
59
61
|
expect(handle.filename).toBe('s.csv')
|
|
60
62
|
expect(csvText).toBe(
|
|
61
63
|
'series,prefix,min,max,suffix,note\nRevenue,$,1,9,k,q1\n,,0,10,,',
|
|
@@ -1,39 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildCsvDownloadItem,
|
|
3
|
-
|
|
3
|
+
withPngItem,
|
|
4
4
|
type DownloadItem,
|
|
5
|
+
type WidgetDownloadArgs,
|
|
5
6
|
} from '../actions/download'
|
|
6
7
|
import type { SpreadWidgetData } from './types'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* Download menu items for the Spread widget. Always includes a CSV item
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Download menu items for the Spread widget. Always includes a CSV item with
|
|
11
|
+
* `series, prefix, min, max, suffix, note` columns (one row per entry). When
|
|
12
|
+
* `getCaptureEl` is given, prepends a PNG item that rasterises the captured
|
|
13
|
+
* element via `html2canvas`.
|
|
13
14
|
*/
|
|
14
|
-
export function createSpreadDownloadConfig(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
pngBackgroundColor?: string | null
|
|
20
|
-
}): DownloadItem[] {
|
|
21
|
-
const items: DownloadItem[] = []
|
|
22
|
-
if (args.getCaptureEl) {
|
|
23
|
-
items.push(
|
|
24
|
-
buildPngDownloadItem({
|
|
25
|
-
filename: args.filename,
|
|
26
|
-
getCaptureEl: args.getCaptureEl,
|
|
27
|
-
pixelRatio: args.pngPixelRatio,
|
|
28
|
-
backgroundColor: args.pngBackgroundColor,
|
|
29
|
-
}),
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
items.push(
|
|
15
|
+
export function createSpreadDownloadConfig(
|
|
16
|
+
args: WidgetDownloadArgs<SpreadWidgetData>,
|
|
17
|
+
): DownloadItem[] {
|
|
18
|
+
return withPngItem(
|
|
19
|
+
args,
|
|
33
20
|
buildCsvDownloadItem({
|
|
34
21
|
filename: args.filename,
|
|
35
|
-
getRows: () => {
|
|
36
|
-
const data = args.getData()
|
|
22
|
+
getRows: (ctx) => {
|
|
23
|
+
const data = args.getData(ctx)
|
|
37
24
|
const rows: unknown[][] = [
|
|
38
25
|
['series', 'prefix', 'min', 'max', 'suffix', 'note'],
|
|
39
26
|
]
|
|
@@ -51,5 +38,4 @@ export function createSpreadDownloadConfig(args: {
|
|
|
51
38
|
},
|
|
52
39
|
}),
|
|
53
40
|
)
|
|
54
|
-
return items
|
|
55
41
|
}
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
setEchartInstance,
|
|
21
21
|
subscribeEchartInstance,
|
|
22
22
|
unregisterWidgetStore,
|
|
23
|
+
widgetCaptureEl,
|
|
24
|
+
widgetStoreData,
|
|
23
25
|
} from './widget-store-registry'
|
|
24
26
|
import type { ECharts } from 'echarts'
|
|
25
27
|
|
|
@@ -290,3 +292,26 @@ describe('widget-store-registry', () => {
|
|
|
290
292
|
})
|
|
291
293
|
})
|
|
292
294
|
})
|
|
295
|
+
|
|
296
|
+
describe('download getters', () => {
|
|
297
|
+
beforeEach(() => clearAllWidgetStores())
|
|
298
|
+
afterEach(() => clearAllWidgetStores())
|
|
299
|
+
|
|
300
|
+
it('read the store the menu was opened from, else the given id', () => {
|
|
301
|
+
registerWidgetStore('card', createWidgetStore('card', { data: 'card' }))
|
|
302
|
+
registerWidgetStore('modal', createWidgetStore('modal', { data: 'modal' }))
|
|
303
|
+
const getData = widgetStoreData<string>('card')
|
|
304
|
+
expect(getData({ id: 'modal' })).toBe('modal')
|
|
305
|
+
expect(getData({ id: null })).toBe('card')
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('capture element: same rule', () => {
|
|
309
|
+
const card = document.createElement('div')
|
|
310
|
+
const modal = document.createElement('div')
|
|
311
|
+
setCaptureEl('card', card)
|
|
312
|
+
setCaptureEl('modal', modal)
|
|
313
|
+
const getEl = widgetCaptureEl('card')
|
|
314
|
+
expect(getEl({ id: 'modal' })).toBe(modal)
|
|
315
|
+
expect(getEl({ id: null })).toBe(card)
|
|
316
|
+
})
|
|
317
|
+
})
|
|
@@ -6,6 +6,7 @@ import { useShallow } from 'zustand/react/shallow'
|
|
|
6
6
|
import { isDev } from '@carto/ps-utils'
|
|
7
7
|
import { pipelineMiddleware } from './pipeline-middleware'
|
|
8
8
|
import type { WidgetInit, WidgetState, WidgetStoreApi } from './types'
|
|
9
|
+
import type { DownloadContext } from '../actions/download/types'
|
|
9
10
|
|
|
10
11
|
const widgetStores = new Map<string, WidgetStoreApi>()
|
|
11
12
|
|
|
@@ -86,6 +87,27 @@ export function getCaptureEl(id: string): HTMLElement | null {
|
|
|
86
87
|
return captureEls.get(id) ?? null
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
/**
|
|
91
|
+
* `getData` for a `create*DownloadConfig`: the `data` of the store the
|
|
92
|
+
* download menu was opened from (`ctx.id` — the fullscreen dialog's own
|
|
93
|
+
* while it's open), or of widget `id` when the menu sits outside a
|
|
94
|
+
* `<Widget.Provider>`.
|
|
95
|
+
*/
|
|
96
|
+
export function widgetStoreData<T>(id: string): (ctx: DownloadContext) => T {
|
|
97
|
+
return (ctx) => getWidgetStore(ctx.id ?? id).getState().data as T
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* `getCaptureEl` for a `create*DownloadConfig`: the capture element of the
|
|
102
|
+
* store the download menu was opened from (`ctx.id`), or of widget `id`
|
|
103
|
+
* outside a `<Widget.Provider>`.
|
|
104
|
+
*/
|
|
105
|
+
export function widgetCaptureEl(
|
|
106
|
+
id: string,
|
|
107
|
+
): (ctx: DownloadContext) => HTMLElement | null {
|
|
108
|
+
return (ctx) => getCaptureEl(ctx.id ?? id)
|
|
109
|
+
}
|
|
110
|
+
|
|
89
111
|
/** @internal — exposed for tests and debugging only. */
|
|
90
112
|
export function clearAllCaptureEls(): void {
|
|
91
113
|
captureEls.clear()
|
|
@@ -56,7 +56,9 @@ describe('createTableDownloadConfig', () => {
|
|
|
56
56
|
getData: () => data,
|
|
57
57
|
columns,
|
|
58
58
|
})
|
|
59
|
-
const handle = await items
|
|
59
|
+
const handle = await items
|
|
60
|
+
.find((i) => i.id === 'csv')!
|
|
61
|
+
.resolve({ id: null })
|
|
60
62
|
expect(handle.filename).toBe('t.csv')
|
|
61
63
|
expect(csvText).toBe('Name,Score\nAlpha,10\nBeta,20')
|
|
62
64
|
})
|
|
@@ -67,7 +69,7 @@ describe('createTableDownloadConfig', () => {
|
|
|
67
69
|
getData: () => [{ id: 1, name: '=HYPERLINK("x")', score: 1 }],
|
|
68
70
|
columns,
|
|
69
71
|
})
|
|
70
|
-
await items.find((i) => i.id === 'csv')!.resolve()
|
|
72
|
+
await items.find((i) => i.id === 'csv')!.resolve({ id: null })
|
|
71
73
|
expect(csvText).toBe('Name,Score\n"\'=HYPERLINK(""x"")",1')
|
|
72
74
|
})
|
|
73
75
|
|
|
@@ -79,7 +81,7 @@ describe('createTableDownloadConfig', () => {
|
|
|
79
81
|
columns,
|
|
80
82
|
})
|
|
81
83
|
snapshot = [{ id: 9, name: 'Gamma', score: 99 }]
|
|
82
|
-
await items.find((i) => i.id === 'csv')!.resolve()
|
|
84
|
+
await items.find((i) => i.id === 'csv')!.resolve({ id: null })
|
|
83
85
|
expect(csvText).toBe('Name,Score\nGamma,99')
|
|
84
86
|
})
|
|
85
87
|
})
|
|
@@ -1,43 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildCsvDownloadItem,
|
|
3
|
-
|
|
3
|
+
withPngItem,
|
|
4
4
|
type DownloadItem,
|
|
5
|
+
type WidgetDownloadArgs,
|
|
5
6
|
} from '../actions/download'
|
|
6
7
|
import { tableDataToCsv } from './helpers'
|
|
7
8
|
import type { TableColumn, TableWidgetData } from './types'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
|
-
* Download menu items for the Table widget. Always includes a CSV item
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `html2canvas`.
|
|
11
|
+
* Download menu items for the Table widget. Always includes a CSV item that
|
|
12
|
+
* pulls the latest data from the widget store via `getData()` (so locked /
|
|
13
|
+
* sorted / filtered state is honored); headers come from `column.label`,
|
|
14
|
+
* cells from `row[column.id]`. When `getCaptureEl` is given, prepends a PNG
|
|
15
|
+
* item that rasterises the captured element via `html2canvas`.
|
|
16
16
|
*/
|
|
17
|
-
export function createTableDownloadConfig(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}): DownloadItem[] {
|
|
25
|
-
const items: DownloadItem[] = []
|
|
26
|
-
if (opts.getCaptureEl) {
|
|
27
|
-
items.push(
|
|
28
|
-
buildPngDownloadItem({
|
|
29
|
-
filename: opts.filename,
|
|
30
|
-
getCaptureEl: opts.getCaptureEl,
|
|
31
|
-
pixelRatio: opts.pngPixelRatio,
|
|
32
|
-
backgroundColor: opts.pngBackgroundColor,
|
|
33
|
-
}),
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
items.push(
|
|
17
|
+
export function createTableDownloadConfig(
|
|
18
|
+
opts: WidgetDownloadArgs<TableWidgetData> & {
|
|
19
|
+
columns: readonly TableColumn[]
|
|
20
|
+
},
|
|
21
|
+
): DownloadItem[] {
|
|
22
|
+
return withPngItem(
|
|
23
|
+
opts,
|
|
37
24
|
buildCsvDownloadItem({
|
|
38
25
|
filename: opts.filename,
|
|
39
|
-
getCsv: () => tableDataToCsv(opts.getData(), opts.columns),
|
|
26
|
+
getCsv: (ctx) => tableDataToCsv(opts.getData(ctx), opts.columns),
|
|
40
27
|
}),
|
|
41
28
|
)
|
|
42
|
-
return items
|
|
43
29
|
}
|
|
@@ -53,7 +53,9 @@ describe('createTimeseriesDownloadConfig', () => {
|
|
|
53
53
|
filename: 't',
|
|
54
54
|
getData: () => data,
|
|
55
55
|
})
|
|
56
|
-
const handle = await items
|
|
56
|
+
const handle = await items
|
|
57
|
+
.find((i) => i.id === 'csv')!
|
|
58
|
+
.resolve({ id: null })
|
|
57
59
|
expect(handle.filename).toBe('t.csv')
|
|
58
60
|
expect(csvText).toBe(
|
|
59
61
|
'time,series_1,series_2\n' +
|
|
@@ -69,7 +71,7 @@ describe('createTimeseriesDownloadConfig', () => {
|
|
|
69
71
|
filename: 't',
|
|
70
72
|
getData: () => data,
|
|
71
73
|
})
|
|
72
|
-
await items.find((i) => i.id === 'csv')!.resolve()
|
|
74
|
+
await items.find((i) => i.id === 'csv')!.resolve({ id: null })
|
|
73
75
|
expect(csvText).toContain('2024-01-01T00:00:00.000Z,7')
|
|
74
76
|
})
|
|
75
77
|
|
|
@@ -79,7 +81,7 @@ describe('createTimeseriesDownloadConfig', () => {
|
|
|
79
81
|
filename: 't',
|
|
80
82
|
getData: () => data,
|
|
81
83
|
})
|
|
82
|
-
await items.find((i) => i.id === 'csv')!.resolve()
|
|
84
|
+
await items.find((i) => i.id === 'csv')!.resolve({ id: null })
|
|
83
85
|
expect(csvText).toContain('2024-Q1,1')
|
|
84
86
|
})
|
|
85
87
|
|
|
@@ -89,7 +91,7 @@ describe('createTimeseriesDownloadConfig', () => {
|
|
|
89
91
|
getData: () => [[{ name: '2024', value: 1 }]],
|
|
90
92
|
seriesNames: ['revenue'],
|
|
91
93
|
})
|
|
92
|
-
await items.find((i) => i.id === 'csv')!.resolve()
|
|
94
|
+
await items.find((i) => i.id === 'csv')!.resolve({ id: null })
|
|
93
95
|
expect(csvText.split('\n')[0]).toBe('time,revenue')
|
|
94
96
|
})
|
|
95
97
|
})
|
|
@@ -1,41 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildCsvDownloadItem,
|
|
3
|
-
|
|
3
|
+
withPngItem,
|
|
4
4
|
type DownloadItem,
|
|
5
|
+
type WidgetDownloadArgs,
|
|
5
6
|
} from '../actions/download'
|
|
6
7
|
import type { TimeseriesWidgetData } from './types'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* Download menu items for the Timeseries widget. Always includes a CSV
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `getCaptureEl` is
|
|
13
|
-
*
|
|
10
|
+
* Download menu items for the Timeseries widget. Always includes a CSV item
|
|
11
|
+
* with `time, series_1, series_2, …` columns (one row per unique time across
|
|
12
|
+
* all series; ISO-8601 strings for `Date`/numeric times). When
|
|
13
|
+
* `getCaptureEl` is given, prepends a PNG item that rasterises the captured
|
|
14
|
+
* element via `html2canvas`.
|
|
14
15
|
*/
|
|
15
|
-
export function createTimeseriesDownloadConfig(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}): DownloadItem[] {
|
|
23
|
-
const items: DownloadItem[] = []
|
|
24
|
-
if (args.getCaptureEl) {
|
|
25
|
-
items.push(
|
|
26
|
-
buildPngDownloadItem({
|
|
27
|
-
filename: args.filename,
|
|
28
|
-
getCaptureEl: args.getCaptureEl,
|
|
29
|
-
pixelRatio: args.pngPixelRatio,
|
|
30
|
-
backgroundColor: args.pngBackgroundColor,
|
|
31
|
-
}),
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
items.push(
|
|
16
|
+
export function createTimeseriesDownloadConfig(
|
|
17
|
+
args: WidgetDownloadArgs<TimeseriesWidgetData> & {
|
|
18
|
+
seriesNames?: readonly string[]
|
|
19
|
+
},
|
|
20
|
+
): DownloadItem[] {
|
|
21
|
+
return withPngItem(
|
|
22
|
+
args,
|
|
35
23
|
buildCsvDownloadItem({
|
|
36
24
|
filename: args.filename,
|
|
37
|
-
getRows: () => {
|
|
38
|
-
const data = args.getData()
|
|
25
|
+
getRows: (ctx) => {
|
|
26
|
+
const data = args.getData(ctx)
|
|
39
27
|
const seriesCount = data.length
|
|
40
28
|
|
|
41
29
|
// Collect every unique time, preserving insertion order.
|
|
@@ -72,7 +60,6 @@ export function createTimeseriesDownloadConfig(args: {
|
|
|
72
60
|
},
|
|
73
61
|
}),
|
|
74
62
|
)
|
|
75
|
-
return items
|
|
76
63
|
}
|
|
77
64
|
|
|
78
65
|
function formatTime(v: Date | number | string): string {
|
|
@@ -56,7 +56,7 @@ export interface ToolboxProps {
|
|
|
56
56
|
* Smart overflow toolbar with **single-mount** items. Children mount once
|
|
57
57
|
* into a stable, hidden host element and are *physically reparented* via
|
|
58
58
|
* `appendChild` between the inline preview area and the overflow `<Paper>`
|
|
59
|
-
* as the popper opens / closes
|
|
59
|
+
* as the popper opens / closes.
|
|
60
60
|
* Items with no inline space when closed are kept mounted but visually
|
|
61
61
|
* hidden via `display: none`.
|
|
62
62
|
*
|
|
@@ -57,14 +57,50 @@ export const styles = {
|
|
|
57
57
|
height: ({ spacing }: Theme) => spacing(0.35),
|
|
58
58
|
zIndex: 1,
|
|
59
59
|
},
|
|
60
|
+
// Title button, then Actions / Options, then the chevron, as siblings.
|
|
61
|
+
// Same spacing as when they all sat inside the summary: its 16px end
|
|
62
|
+
// padding moves to the row, the content's 6px end padding sits before
|
|
63
|
+
// the chevron.
|
|
64
|
+
header: {
|
|
65
|
+
display: 'flex',
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
gap: ({ spacing }: Theme) => spacing(0.5),
|
|
68
|
+
minHeight: ({ spacing }: Theme) => spacing(7),
|
|
69
|
+
paddingInlineEnd: ({ spacing }: Theme) => spacing(2),
|
|
70
|
+
},
|
|
60
71
|
summary: {
|
|
72
|
+
flex: 1,
|
|
73
|
+
minWidth: 0,
|
|
61
74
|
minHeight: ({ spacing }: Theme) => spacing(7),
|
|
75
|
+
paddingInlineEnd: 0,
|
|
62
76
|
'& .MuiAccordionSummary-content': {
|
|
63
|
-
gap: ({ spacing }: Theme) => spacing(0.5),
|
|
64
|
-
paddingInlineEnd: ({ spacing }: Theme) => spacing(0.75),
|
|
65
77
|
alignItems: 'center',
|
|
78
|
+
minWidth: 0,
|
|
79
|
+
marginInlineEnd: 0,
|
|
80
|
+
paddingInlineEnd: 0,
|
|
66
81
|
},
|
|
67
82
|
},
|
|
83
|
+
// No chevron (disabled): keep the actions where they sat when the
|
|
84
|
+
// chevron's slot was empty — 10px further from the edge.
|
|
85
|
+
headerNoIcon: {
|
|
86
|
+
paddingInlineEnd: ({ spacing }: Theme) => spacing(3.25),
|
|
87
|
+
},
|
|
88
|
+
// Looks like the icon the summary used to render in its
|
|
89
|
+
// `expandIconWrapper`: 18px, secondary text colour, rotated when open.
|
|
90
|
+
expandIcon: {
|
|
91
|
+
display: 'flex',
|
|
92
|
+
marginInlineStart: ({ spacing }: Theme) => spacing(0.75),
|
|
93
|
+
color: 'text.secondary',
|
|
94
|
+
cursor: 'pointer',
|
|
95
|
+
'& svg': { width: 18, height: 18, color: 'inherit' },
|
|
96
|
+
transition: ({ transitions }: Theme) =>
|
|
97
|
+
transitions.create('transform', {
|
|
98
|
+
duration: transitions.duration.shortest,
|
|
99
|
+
}),
|
|
100
|
+
},
|
|
101
|
+
expanded: {
|
|
102
|
+
transform: 'rotate(180deg)',
|
|
103
|
+
},
|
|
68
104
|
// Disabled state — only the collapse toggle is inert (enforced by the
|
|
69
105
|
// guarded `onChange` handler, not `pointer-events`). Actions and Options in
|
|
70
106
|
// the summary row, and the details panel, all keep their events. We just
|
|
@@ -14,8 +14,8 @@ export interface ActionsProps {
|
|
|
14
14
|
* pointer devices), and stay full-opacity when a descendant is marked with
|
|
15
15
|
* `.active` — typically applied by toggled actions.
|
|
16
16
|
*
|
|
17
|
-
* The container also stops click propagation so
|
|
18
|
-
*
|
|
17
|
+
* The container also stops click propagation, so a click on an action never
|
|
18
|
+
* reaches handlers further up the widget.
|
|
19
19
|
*/
|
|
20
20
|
export function Actions({ sx, children }: ActionsProps) {
|
|
21
21
|
return (
|