@datawheel/data-explorer 1.0.15 → 1.0.17

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/main.d.mts CHANGED
@@ -1187,8 +1187,7 @@ declare namespace ExplorerComponent {
1187
1187
  var displayName: string;
1188
1188
  }
1189
1189
 
1190
- /** */
1191
- declare function PivotView<TData extends Record<string, unknown>>(props: {} & ViewProps<TData> & MRT_TableOptions<TData>): React.JSX.Element | null;
1190
+ declare function PivotView<TData extends Record<string, unknown>>(props: {} & ViewProps<TData> & MRT_TableOptions<TData>): React.JSX.Element;
1192
1191
 
1193
1192
  type TData = Record<string, string | number> & Record<string, any>;
1194
1193
  type TableView = {
package/dist/main.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { keyframes, createStyles, Select, rem, Input, Box, Text, Stack, Group, Button, SimpleGrid, Flex, ScrollArea, LoadingOverlay, Table, MultiSelect, Center, NumberInput, Menu, ActionIcon, UnstyledButton, Alert, Loader, Container, Title, useMantineTheme, TextInput, CopyButton, MantineProvider, Modal, Space, useComponentDefaultProps, Anchor, Paper, Accordion, Tooltip, Tabs, Switch, ThemeIcon, CloseButton, Drawer, Divider, Checkbox, packSx, Affix, Popover } from '@mantine/core';
1
+ import { keyframes, createStyles, Select, rem, Input, Box, Text, Stack, Group, Button, SimpleGrid, Flex, ScrollArea, LoadingOverlay, Table, MultiSelect, Center, NumberInput, Menu, ActionIcon, UnstyledButton, Loader, Container, Title, useMantineTheme, TextInput, CopyButton, Alert, MantineProvider, Modal, Space, useComponentDefaultProps, Paper, Anchor, Accordion, Tooltip, Tabs, Switch, ThemeIcon, CloseButton, Drawer, Divider, Checkbox, packSx, Affix, Popover } from '@mantine/core';
2
2
  import { useClipboard, useFullscreen, useDebouncedState, useMediaQuery, useDisclosure } from '@mantine/hooks';
3
- import { IconWorld, IconExternalLink, IconClipboard, IconSettings, IconMathGreater, IconMathLower, IconArrowsLeftRight, IconAlertCircle, IconAlertTriangle, IconWorldWww, IconClipboardCheck, IconCopy, IconDownload, IconDotsVertical, IconBox, IconArrowRight, IconArrowLeft, IconBrandGithub, IconCheck, IconShare, IconLanguage, IconTrash, IconInfoCircleFilled, IconChevronLeft, IconChevronRight, IconSearch, IconArrowsMinimize, IconArrowsMaximize, IconCircleOff, IconArrowsSort, IconSortDescendingNumbers, IconSortDescendingLetters, IconSortAscendingNumbers, IconSortAscendingLetters, IconPlus, IconStack3, IconFilterOff, IconFilter, IconAdjustments, IconClock, IconHelpCircle, IconPhotoDown, IconVectorTriangle } from '@tabler/icons-react';
3
+ import { IconWorld, IconExternalLink, IconClipboard, IconSettings, IconMathGreater, IconMathLower, IconArrowsLeftRight, IconWorldWww, IconClipboardCheck, IconAlertCircle, IconAlertTriangle, IconCopy, IconDownload, IconDotsVertical, IconBox, IconArrowRight, IconArrowLeft, IconBrandGithub, IconCheck, IconShare, IconLanguage, IconTrash, IconInfoCircleFilled, IconChevronLeft, IconChevronRight, IconSearch, IconArrowsMinimize, IconArrowsMaximize, IconCircleOff, IconArrowsSort, IconSortDescendingNumbers, IconSortDescendingLetters, IconSortAscendingNumbers, IconSortAscendingLetters, IconPlus, IconStack3, IconFilterOff, IconFilter, IconAdjustments, IconClock, IconHelpCircle, IconPhotoDown, IconVectorTriangle } from '@tabler/icons-react';
4
4
  import * as React13 from 'react';
5
5
  import React13__default, { createContext, forwardRef, useMemo, useCallback, useContext, useRef, useEffect, useState, Suspense, Component, useLayoutEffect } from 'react';
6
6
  import { translationFactory } from '@datawheel/use-translation';
@@ -484,6 +484,7 @@ var vizbuilderTranslation = {
484
484
  total: "Total: {{value}}"
485
485
  },
486
486
  transient: {
487
+ title_one_row: "The dataset has only one row and can't be used to generate charts.",
487
488
  title_loading: "Generating charts...",
488
489
  title_empty: "No results",
489
490
  description_empty: "The selected combination of parameters can't be used to generate a meaningful set of charts. Try changing some parameters (maybe applying some restriction in a column) and generating charts again."
@@ -1396,11 +1397,14 @@ function httpFetch(url, params) {
1396
1397
  return fetch(url, params).then((response) => {
1397
1398
  if (!response.ok) {
1398
1399
  return response.json().then((content) => {
1400
+ var _a;
1399
1401
  console.debug("CONTENT", content);
1400
1402
  if (response.status === 500) {
1401
1403
  throw new Error("");
1402
1404
  }
1403
- throw new Error(`Request failed with error code ${response.status}`);
1405
+ throw new Error(
1406
+ `${(_a = content.detail) != null ? _a : `Request failed with error code ${response.status}`} `
1407
+ );
1404
1408
  });
1405
1409
  }
1406
1410
  return response;
@@ -2316,8 +2320,9 @@ function useDownloadQuery() {
2316
2320
  throw new Error("The current query is not valid.");
2317
2321
  }
2318
2322
  const queryParams = { ...params, pagiLimit: 0, pagiOffset: 0 };
2323
+ const request = queryParamsToRequest(queryParams);
2319
2324
  const response = await tesseract.fetchData({
2320
- request: queryParamsToRequest(queryParams),
2325
+ request,
2321
2326
  format: format2
2322
2327
  });
2323
2328
  const blob = await response.blob();
@@ -2578,13 +2583,49 @@ var DownloadQuery = ({ data }) => {
2578
2583
  const { translate: t } = useTranslation();
2579
2584
  const formats = Object.values(Format);
2580
2585
  const components = [];
2581
- const { mutateAsync: downloadQuery } = useDownloadQuery();
2586
+ const { mutateAsync: downloadQuery, isError, error } = useDownloadQuery();
2587
+ const [showError, setShowError] = useState(false);
2588
+ useEffect(() => {
2589
+ if (isError && error) {
2590
+ setShowError(true);
2591
+ const timer = setTimeout(() => {
2592
+ setShowError(false);
2593
+ }, 7e3);
2594
+ return () => clearTimeout(timer);
2595
+ }
2596
+ }, [isError, error]);
2597
+ const errorAlert = isError && error && showError ? /* @__PURE__ */ React13__default.createElement(
2598
+ Paper,
2599
+ {
2600
+ sx: {
2601
+ position: "absolute",
2602
+ bottom: "calc(100% + 15px)",
2603
+ right: 0,
2604
+ minWidth: "400px",
2605
+ maxWidth: "700px",
2606
+ width: "fit-content",
2607
+ zIndex: 1e3
2608
+ },
2609
+ shadow: "md"
2610
+ },
2611
+ /* @__PURE__ */ React13__default.createElement(
2612
+ Alert,
2613
+ {
2614
+ icon: /* @__PURE__ */ React13__default.createElement(IconAlertCircle, { size: "1rem" }),
2615
+ color: "red",
2616
+ p: "xs",
2617
+ withCloseButton: true,
2618
+ onClose: () => setShowError(false)
2619
+ },
2620
+ error.message
2621
+ )
2622
+ ) : null;
2582
2623
  components.push(
2583
2624
  /* @__PURE__ */ React13__default.createElement(
2584
2625
  ButtonDownload,
2585
2626
  {
2586
2627
  leftIcon: /* @__PURE__ */ React13__default.createElement(IconDownload, { size: 20 }),
2587
- sx: { height: 30 },
2628
+ sx: { height: 30, position: "relative" },
2588
2629
  key: "download_csv",
2589
2630
  provider: () => downloadQuery({ format: "csv" })
2590
2631
  },
@@ -2594,7 +2635,7 @@ var DownloadQuery = ({ data }) => {
2594
2635
  if (components.length === 0 || data.length === 0) {
2595
2636
  return null;
2596
2637
  }
2597
- return /* @__PURE__ */ React13__default.createElement(Box, { id: "dex-btn-group-download" }, /* @__PURE__ */ React13__default.createElement(Group, { spacing: "xs" }, components, /* @__PURE__ */ React13__default.createElement(MenuOpts, { formats: formats.filter((f) => f !== "csv") })));
2638
+ return /* @__PURE__ */ React13__default.createElement(Box, { id: "dex-btn-group-download", sx: { position: "relative" } }, errorAlert, /* @__PURE__ */ React13__default.createElement(Group, { spacing: "xs" }, components, /* @__PURE__ */ React13__default.createElement(MenuOpts, { formats: formats.filter((f) => f !== "csv") })));
2598
2639
  };
2599
2640
  var mimeTypes = {
2600
2641
  csv: "text/csv",
@@ -2687,19 +2728,30 @@ function MenuOpts({ formats }) {
2687
2728
  },
2688
2729
  /* @__PURE__ */ React13__default.createElement(Text, { size: "xs" }, t(`formats.${format2}`))
2689
2730
  ));
2690
- return /* @__PURE__ */ React13__default.createElement(Menu, { shadow: "md", width: 200, opened }, /* @__PURE__ */ React13__default.createElement(Menu.Target, null, /* @__PURE__ */ React13__default.createElement(
2691
- ActionIcon,
2731
+ return /* @__PURE__ */ React13__default.createElement(
2732
+ Menu,
2692
2733
  {
2693
- onClick: () => setOpened((o) => !o),
2694
- variant: "filled",
2695
- color: "primary",
2696
- sx: {
2697
- width: 20,
2698
- minWidth: 0
2699
- }
2734
+ shadow: "md",
2735
+ width: 200,
2736
+ opened,
2737
+ onClose: () => setOpened(false),
2738
+ closeOnItemClick: false
2700
2739
  },
2701
- /* @__PURE__ */ React13__default.createElement(IconDotsVertical, { size: "0.8rem" })
2702
- )), /* @__PURE__ */ React13__default.createElement(Menu.Dropdown, null, /* @__PURE__ */ React13__default.createElement(Menu.Label, null, t("params.title_downloaddata")), buttons));
2740
+ /* @__PURE__ */ React13__default.createElement(Menu.Target, null, /* @__PURE__ */ React13__default.createElement(
2741
+ ActionIcon,
2742
+ {
2743
+ onClick: () => setOpened((o) => !o),
2744
+ variant: "filled",
2745
+ color: "primary",
2746
+ sx: {
2747
+ width: 20,
2748
+ minWidth: 0
2749
+ }
2750
+ },
2751
+ /* @__PURE__ */ React13__default.createElement(IconDotsVertical, { size: "0.8rem" })
2752
+ )),
2753
+ /* @__PURE__ */ React13__default.createElement(Menu.Dropdown, null, /* @__PURE__ */ React13__default.createElement(Menu.Label, null, t("params.title_downloaddata")), buttons)
2754
+ );
2703
2755
  }
2704
2756
  var TableFooter_default = TableFooter;
2705
2757
 
@@ -4246,28 +4298,56 @@ function QueryProvider({ children, defaultCube }) {
4246
4298
  let newQuery = parsePermalink(cubeMap[cube], searchParams);
4247
4299
  newQuery = isValidQuery(newQuery == null ? void 0 : newQuery.params) ? newQuery : buildQuery({ params: { cube } });
4248
4300
  newQuery.params.locale = defaultLocale || newQuery.params.locale;
4301
+ const dimensions = {};
4302
+ const hierarchies = {};
4303
+ const levels = Object.fromEntries(
4304
+ cubeMap[cube].dimensions.flatMap(
4305
+ (dim) => dim.hierarchies.flatMap(
4306
+ (hie) => hie.levels.map((lvl) => {
4307
+ dimensions[lvl.name] = dim.name;
4308
+ hierarchies[lvl.name] = hie.name;
4309
+ return [lvl.name, lvl];
4310
+ })
4311
+ )
4312
+ )
4313
+ );
4314
+ const restDrilldownsKeys = Object.keys(levels).filter(
4315
+ (key) => !newQuery.params.drilldowns[key]
4316
+ );
4317
+ const restDrilldowns = restDrilldownsKeys.map((name4) => {
4318
+ const lvl = levels[name4];
4319
+ return buildDrilldown({
4320
+ active: false,
4321
+ key: lvl.name,
4322
+ dimension: dimensions[name4],
4323
+ hierarchy: hierarchies[name4],
4324
+ level: name4
4325
+ });
4326
+ });
4249
4327
  if (newQuery) {
4250
- const promises = Object.values(newQuery.params.drilldowns).map((dd) => {
4251
- const currentDrilldown = queryItem.params.drilldowns[dd.key];
4252
- const localeChanged = prevLocaleRef.current !== (newQuery == null ? void 0 : newQuery.params.locale);
4253
- if (currentDrilldown && currentDrilldown.members && currentDrilldown.members.length > 0 && !localeChanged) {
4254
- return Promise.resolve({
4255
- drilldown: currentDrilldown,
4256
- cut: buildCut({ ...currentDrilldown, active: false })
4257
- });
4258
- } else {
4259
- return fetchMembers(dd.level, newQuery == null ? void 0 : newQuery.params.locale, cube).then((levelMeta) => {
4260
- const cut = buildCut({ ...dd, active: false });
4261
- return {
4262
- drilldown: {
4263
- ...dd,
4264
- members: levelMeta.members
4265
- },
4266
- cut
4267
- };
4268
- });
4328
+ const promises = [...restDrilldowns, ...Object.values(newQuery.params.drilldowns)].map(
4329
+ (dd) => {
4330
+ const currentDrilldown = queryItem.params.drilldowns[dd.key];
4331
+ const localeChanged = prevLocaleRef.current !== (newQuery == null ? void 0 : newQuery.params.locale);
4332
+ if (currentDrilldown && currentDrilldown.members && currentDrilldown.members.length > 0 && !localeChanged) {
4333
+ return Promise.resolve({
4334
+ drilldown: currentDrilldown,
4335
+ cut: buildCut({ ...currentDrilldown, active: false })
4336
+ });
4337
+ } else {
4338
+ return fetchMembers(dd.level, newQuery == null ? void 0 : newQuery.params.locale, cube).then((levelMeta) => {
4339
+ const cut = buildCut({ ...dd, active: false });
4340
+ return {
4341
+ drilldown: {
4342
+ ...dd,
4343
+ members: levelMeta.members
4344
+ },
4345
+ cut
4346
+ };
4347
+ });
4348
+ }
4269
4349
  }
4270
- });
4350
+ );
4271
4351
  runFetchMembers(Promise.all(promises)).then((data) => {
4272
4352
  setTransintionLocaleLoading(false);
4273
4353
  prevLocaleRef.current = newQuery == null ? void 0 : newQuery.params.locale;
@@ -4920,6 +5000,7 @@ function SideBarItem({ children }) {
4920
5000
  function Auto() {
4921
5001
  const { translate: t } = useTranslation();
4922
5002
  const { expanded, input, setInput } = useSideBar();
5003
+ const [inputValue, setInputValue] = useState(input);
4923
5004
  return /* @__PURE__ */ React13__default.createElement(
4924
5005
  Input,
4925
5006
  {
@@ -4928,8 +5009,11 @@ function Auto() {
4928
5009
  radius: "xl",
4929
5010
  size: "md",
4930
5011
  placeholder: t("params.label_search"),
4931
- defaultValue: input,
4932
- onInput: (e) => setInput(e.currentTarget.value),
5012
+ value: inputValue,
5013
+ onInput: (e) => {
5014
+ setInputValue(e.currentTarget.value);
5015
+ setInput(e.currentTarget.value);
5016
+ },
4933
5017
  styles: {
4934
5018
  wrapper: {
4935
5019
  width: expanded ? "100%" : 0,
@@ -4944,7 +5028,10 @@ function Auto() {
4944
5028
  CloseButton,
4945
5029
  {
4946
5030
  "aria-label": "Clear input",
4947
- onClick: () => setInput(""),
5031
+ onClick: () => {
5032
+ setInput("");
5033
+ setInputValue("");
5034
+ },
4948
5035
  style: { display: input ? void 0 : "none" }
4949
5036
  }
4950
5037
  )
@@ -5302,6 +5389,20 @@ function ExplorerContent(props) {
5302
5389
  // src/components/PivotView.tsx
5303
5390
  init_esm_shims();
5304
5391
 
5392
+ // src/vizbuilder/hooks/usePivotTableData.ts
5393
+ init_esm_shims();
5394
+ function usePivotTableData() {
5395
+ const queryItem = useSelector$1(selectCurrentQueryItem);
5396
+ const queryLink = queryItem.link;
5397
+ const query = useFetchQuery(queryItem.params, queryLink, {
5398
+ withoutPagination: true
5399
+ });
5400
+ return query;
5401
+ }
5402
+
5403
+ // src/components/PivotViewTable.tsx
5404
+ init_esm_shims();
5405
+
5305
5406
  // src/hooks/pivot.ts
5306
5407
  init_esm_shims();
5307
5408
 
@@ -5520,16 +5621,27 @@ function useFormatParams(measures, valueProperty) {
5520
5621
  function usePivottedData(data, colProp, rowProp, valProp, initialState4 = null) {
5521
5622
  const [pivottedData, setPivottedData] = useState(initialState4);
5522
5623
  const [error, setError] = useState(null);
5624
+ const [isProcessing, setIsProcessing] = useState(false);
5523
5625
  useEffect(() => {
5524
5626
  setPivottedData(initialState4);
5525
5627
  setError(null);
5526
- serializeToArray(data, { colProp, rowProp, valProp }).then(setPivottedData, setError);
5628
+ if (data.length !== 0) {
5629
+ setIsProcessing(true);
5630
+ serializeToArray(data, { colProp, rowProp, valProp }).then((result) => {
5631
+ setPivottedData(result);
5632
+ setIsProcessing(false);
5633
+ }).catch((err) => {
5634
+ setError(err);
5635
+ setIsProcessing(false);
5636
+ });
5637
+ }
5527
5638
  return () => {
5528
5639
  setPivottedData(null);
5529
5640
  setError(null);
5641
+ setIsProcessing(false);
5530
5642
  };
5531
- }, [data, colProp, rowProp, valProp]);
5532
- return [pivottedData, error];
5643
+ }, [data, colProp, rowProp, valProp, initialState4]);
5644
+ return [pivottedData, error, isProcessing];
5533
5645
  }
5534
5646
  function serializeToArray(data, sides) {
5535
5647
  return new Promise((resolve, reject) => {
@@ -5611,7 +5723,7 @@ var NonIdealState = (props) => /* @__PURE__ */ React13__default.createElement(
5611
5723
  /* @__PURE__ */ React13__default.createElement(Stack, { align: "center", spacing: "xs" }, props.icon && props.icon, props.title && /* @__PURE__ */ React13__default.createElement(Title, { order: 5 }, props.title), props.description && /* @__PURE__ */ React13__default.createElement(Text, null, props.description), props.children && props.children, props.action && props.action)
5612
5724
  );
5613
5725
 
5614
- // src/components/PivotView.tsx
5726
+ // src/components/PivotViewTable.tsx
5615
5727
  var SelectOption = forwardRef(
5616
5728
  (props, ref) => {
5617
5729
  return /* @__PURE__ */ React13__default.createElement(SelectObject, { ref, ...props });
@@ -5637,50 +5749,44 @@ var useStyles5 = createStyles((theme) => ({
5637
5749
  }
5638
5750
  }
5639
5751
  }));
5640
- function PivotView(props) {
5752
+ function PivotViewTable(props) {
5641
5753
  var _a;
5642
- const { cube, params, result, isLoading, ...mantineReactTableProps } = props;
5643
- const locale = params.locale;
5754
+ const { cube, params, className, panelKey, data: result, ...mantineReactTableProps } = props;
5644
5755
  const { translate: t } = useTranslation();
5645
5756
  const { data: schema } = useServerSchema();
5646
- const measures = (_a = schema == null ? void 0 : schema.cubeMap[cube.name]) == null ? void 0 : _a.measures;
5647
- if (!result || !measures) return null;
5648
- const measureMap = Object.fromEntries(measures.map((item) => [item.name, item]));
5649
5757
  const { classes, cx } = useStyles5();
5650
- const measureOptions = useMemo(
5651
- () => filterMap(Object.values(params.measures), (item) => {
5652
- const entity = measureMap[item.name];
5653
- return !isActiveItem(item) ? null : {
5654
- value: item.name,
5655
- label: getCaption(entity, locale),
5656
- type: entity.aggregator
5657
- };
5658
- }),
5659
- [measureMap, params.measures, locale]
5660
- );
5661
- const drilldownOptions = useMemo(() => {
5662
- const levelMap = mapDimensionHierarchyLevels(cube);
5663
- return Object.values(params.drilldowns).filter(isActiveItem).flatMap((item) => {
5664
- const [level, hierarchy, dimension] = levelMap[item.level];
5665
- const caption = getCaption(level, locale);
5666
- const type = dimension.type;
5667
- const propertyMap = keyBy(level.properties, "name");
5668
- const levelOptions = [{ value: item.level, label: caption, type }];
5669
- if (result.data.length && `${item.level} ID` in result.data[0]) {
5670
- levelOptions.push({ value: `${item.level} ID`, label: `${caption} ID`, type });
5671
- }
5672
- return levelOptions.concat(
5673
- filterMap(item.properties, (item2) => {
5674
- const entity = propertyMap[item2.name];
5675
- return !isActiveItem(item2) ? null : {
5676
- value: item2.name,
5677
- label: `${caption} \u203A ${getCaption(entity, locale)}`,
5678
- type: "prop"
5679
- };
5680
- })
5681
- );
5682
- });
5683
- }, [cube, params.drilldowns, locale]);
5758
+ const locale = params.locale;
5759
+ const measures = ((_a = schema == null ? void 0 : schema.cubeMap[cube.name]) == null ? void 0 : _a.measures) || [];
5760
+ const measureMap = Object.fromEntries(measures.map((item) => [item.name, item]));
5761
+ const measureOptions = filterMap(Object.values(params.measures), (item) => {
5762
+ const entity = measureMap[item.name];
5763
+ return !isActiveItem(item) ? null : {
5764
+ value: item.name,
5765
+ label: getCaption(entity, locale),
5766
+ type: entity.aggregator
5767
+ };
5768
+ });
5769
+ const levelMap = mapDimensionHierarchyLevels(cube);
5770
+ const drilldownOptions = Object.values(params.drilldowns).filter(isActiveItem).flatMap((item) => {
5771
+ const [level, hierarchy, dimension] = levelMap[item.level];
5772
+ const caption = getCaption(level, locale);
5773
+ const type = dimension.type;
5774
+ const propertyMap = keyBy(level.properties, "name");
5775
+ const levelOptions = [{ value: item.level, label: caption, type }];
5776
+ if (result.length && `${item.level} ID` in result[0]) {
5777
+ levelOptions.push({ value: `${item.level} ID`, label: `${caption} ID`, type });
5778
+ }
5779
+ return levelOptions.concat(
5780
+ filterMap(item.properties, (item2) => {
5781
+ const entity = propertyMap[item2.name];
5782
+ return !isActiveItem(item2) ? null : {
5783
+ value: item2.name,
5784
+ label: `${caption} \u203A ${getCaption(entity, locale)}`,
5785
+ type: "prop"
5786
+ };
5787
+ })
5788
+ );
5789
+ });
5684
5790
  const [colProp, setColumnProp] = useState(
5685
5791
  () => drilldownOptions.find((item) => item.type === "time") || drilldownOptions[0]
5686
5792
  );
@@ -5689,8 +5795,8 @@ function PivotView(props) {
5689
5795
  );
5690
5796
  const [valProp, setValueProp] = useState(() => measureOptions[0]);
5691
5797
  const fileName = [params.cube, colProp.label, rowProp.label, valProp.value].join("_");
5692
- const [pivottedData, pivottingError] = usePivottedData(
5693
- result.data,
5798
+ const [pivottedData, pivottingError, isProcessing] = usePivottedData(
5799
+ result,
5694
5800
  colProp.value,
5695
5801
  rowProp.value,
5696
5802
  valProp.value
@@ -5790,7 +5896,7 @@ function PivotView(props) {
5790
5896
  data: pivottedData.data,
5791
5897
  headers: pivottedData.headers,
5792
5898
  formatter: formatter2,
5793
- tableProps: mantineReactTableProps
5899
+ tableProps: { ...mantineReactTableProps, data: result }
5794
5900
  }
5795
5901
  );
5796
5902
  }
@@ -5867,7 +5973,7 @@ function MatrixTable(props) {
5867
5973
  }
5868
5974
  })
5869
5975
  ),
5870
- [headers]
5976
+ [headers, formatter2]
5871
5977
  );
5872
5978
  const tableProps = useMemo(
5873
5979
  () => ({
@@ -5936,6 +6042,30 @@ function stringifyMatrix(matrix, formatter2, format2) {
5936
6042
  ].join("\n");
5937
6043
  }
5938
6044
 
6045
+ // src/components/PivotView.tsx
6046
+ function PivotView(props) {
6047
+ const { translate: t } = useTranslation();
6048
+ const { isLoading, data: result } = usePivotTableData();
6049
+ const { isLoading: schemaLoading } = useServerSchema();
6050
+ if (isLoading || schemaLoading) {
6051
+ return /* @__PURE__ */ React13__default.createElement(
6052
+ NonIdealState,
6053
+ {
6054
+ icon: /* @__PURE__ */ React13__default.createElement(Loader, { size: "xl" }),
6055
+ title: t("pivot_view.loading_title"),
6056
+ description: t("pivot_view.loading_details")
6057
+ }
6058
+ );
6059
+ }
6060
+ return /* @__PURE__ */ React13__default.createElement(
6061
+ PivotViewTable,
6062
+ {
6063
+ ...props,
6064
+ data: (result == null ? void 0 : result.data) || []
6065
+ }
6066
+ );
6067
+ }
6068
+
5939
6069
  // src/components/tour/ExplorerTour.tsx
5940
6070
  init_esm_shims();
5941
6071
 
@@ -7549,6 +7679,9 @@ function NonIdealState2(props) {
7549
7679
  if (status === "loading") {
7550
7680
  return /* @__PURE__ */ React13__default.createElement(Flex, { justify: "center", align: "center", direction: "column" }, /* @__PURE__ */ React13__default.createElement(Loader, { size: "xl" }), /* @__PURE__ */ React13__default.createElement(Title, { mt: "md", order: 4 }, t("vizbuilder.transient.title_loading")));
7551
7681
  }
7682
+ if (status === "one-row") {
7683
+ return /* @__PURE__ */ React13__default.createElement(Flex, { justify: "center", align: "center", direction: "column", w: "50%" }, /* @__PURE__ */ React13__default.createElement(IconCircleOff, { size: 92 }), /* @__PURE__ */ React13__default.createElement(Title, { mt: "md", mb: "md", order: 4 }, t("vizbuilder.transient.title_one_row")));
7684
+ }
7552
7685
  return /* @__PURE__ */ React13__default.createElement(Flex, { justify: "center", align: "center", direction: "column", w: "50%" }, /* @__PURE__ */ React13__default.createElement(IconCircleOff, { size: 92 }), /* @__PURE__ */ React13__default.createElement(Title, { mt: "md", mb: "md", order: 4 }, t("vizbuilder.transient.title_empty")), /* @__PURE__ */ React13__default.createElement(Text, null, t("vizbuilder.transient.description_empty")));
7553
7686
  }, [status, t]);
7554
7687
  return /* @__PURE__ */ React13__default.createElement(Center, { h: "100%" }, description);
@@ -7569,9 +7702,12 @@ function Vizbuilder(props) {
7569
7702
  const queryItem = useSelector$1(selectCurrentQueryItem);
7570
7703
  const currentChart = (queryItem == null ? void 0 : queryItem.chart) || "";
7571
7704
  const { actions: actions2 } = useSettings();
7572
- const setCurrentChart = useCallback((chart) => {
7573
- actions2.updateChart(chart);
7574
- }, [actions2]);
7705
+ const setCurrentChart = useCallback(
7706
+ (chart) => {
7707
+ actions2.updateChart(chart);
7708
+ },
7709
+ [actions2]
7710
+ );
7575
7711
  const getMeasureConfig = useMemo(() => {
7576
7712
  const config = measureConfig || {};
7577
7713
  return typeof config === "function" ? config : (item) => config[item.name];
@@ -7598,9 +7734,14 @@ function Vizbuilder(props) {
7598
7734
  console.debug("Loading datasets...", datasets);
7599
7735
  return /* @__PURE__ */ React13__default.createElement(Notice, { status: "loading" });
7600
7736
  }
7601
- const chartList = Object.values(charts);
7737
+ let chartList = Object.values(charts);
7738
+ if (chartList.length === 0 && !Array.isArray(datasets) && datasets.data.length === 1)
7739
+ return /* @__PURE__ */ React13__default.createElement(Notice, { status: "one-row" });
7602
7740
  if (chartList.length === 0) return /* @__PURE__ */ React13__default.createElement(Notice, { status: "empty" });
7603
7741
  const isSingleChart = chartList.length === 1;
7742
+ if (chartList.length > 10) {
7743
+ chartList = chartList.slice(0, 10);
7744
+ }
7604
7745
  return /* @__PURE__ */ React13__default.createElement(ErrorBoundary, null, /* @__PURE__ */ React13__default.createElement(
7605
7746
  SimpleGrid,
7606
7747
  {
@@ -7650,14 +7791,7 @@ function Vizbuilder(props) {
7650
7791
  isFullMode: true
7651
7792
  }
7652
7793
  );
7653
- }, [
7654
- charts,
7655
- currentChart,
7656
- downloadFormats,
7657
- getMeasureConfig,
7658
- showConfidenceInt,
7659
- userConfig
7660
- ]);
7794
+ }, [charts, currentChart, downloadFormats, getMeasureConfig, showConfidenceInt, userConfig]);
7661
7795
  return /* @__PURE__ */ React13__default.createElement("div", { style: { height: "100%" }, className: clsx_m_default("vb-wrapper", props.className) }, props.customHeader, content, props.customFooter, /* @__PURE__ */ React13__default.createElement(
7662
7796
  Modal,
7663
7797
  {
@@ -7689,7 +7823,7 @@ function useVizbuilderData() {
7689
7823
 
7690
7824
  // src/components/LoadingOverlay.jsx
7691
7825
  init_esm_shims();
7692
- var LoadingOverlay2 = () => {
7826
+ var LoadingOverlay3 = () => {
7693
7827
  const { translate: t } = useTranslation();
7694
7828
  const { loading: isLoading, message } = useSelector$1(selectLoadingState);
7695
7829
  const description = !message ? void 0 : message.type === "HEAVY_QUERY" ? t("loading.message_heavyquery", message) : (
@@ -7728,7 +7862,7 @@ function VizbuilderView(props) {
7728
7862
  const { cube, params } = props;
7729
7863
  const query = useVizbuilderData();
7730
7864
  if (query.isLoading) {
7731
- return /* @__PURE__ */ React13__default.createElement(LoadingOverlay2, { visible: true });
7865
+ return /* @__PURE__ */ React13__default.createElement(LoadingOverlay3, { visible: true });
7732
7866
  }
7733
7867
  const data = query.data;
7734
7868
  const types = (_a = query.data) == null ? void 0 : _a.types;
@@ -7740,14 +7874,14 @@ function VizbuilderView(props) {
7740
7874
  data: (data == null ? void 0 : data.data) || [],
7741
7875
  locale: params.locale || "en"
7742
7876
  };
7743
- return !query.isLoading && /* @__PURE__ */ React13__default.createElement(
7877
+ return /* @__PURE__ */ React13__default.createElement(React13__default.Fragment, null, /* @__PURE__ */ React13__default.createElement(
7744
7878
  Vizbuilder,
7745
7879
  {
7746
7880
  datasets: dataset,
7747
7881
  chartLimits: CHART_LIMITS,
7748
7882
  downloadFormats: DOWNLOAD_FORMATS
7749
7883
  }
7750
- );
7884
+ ));
7751
7885
  }
7752
7886
 
7753
7887
  // src/components/RawResponseView.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datawheel/data-explorer",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "main": "./dist/main.mjs",
5
5
  "types": "./dist/main.d.mts",
6
6
  "files": [