@datawheel/data-explorer 0.2.6 → 0.2.7

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.
Files changed (2) hide show
  1. package/dist/main.js +416 -44
  2. package/package.json +2 -1
package/dist/main.js CHANGED
@@ -1,6 +1,6 @@
1
- import { keyframes, createStyles, Select, rem, Flex, Title, Text, Group, Button, Input, Box, Stack, SimpleGrid, ScrollArea, LoadingOverlay, Table, MultiSelect, Center, NumberInput, Menu, ActionIcon, Alert, Loader, Modal, MantineProvider, Paper, useComponentDefaultProps, Anchor, Tabs, Switch, ThemeIcon, Tooltip, useMantineTheme, CloseButton, Drawer, Divider, Checkbox, UnstyledButton, Accordion, Popover } from '@mantine/core';
1
+ import { keyframes, createStyles, Select, rem, Flex, Title, Text, Group, Button, Input, Box, Stack, SimpleGrid, ScrollArea, LoadingOverlay, Table, MultiSelect, Center, NumberInput, Menu, ActionIcon, UnstyledButton, Alert, Loader, Container, Modal, useMantineTheme, MantineProvider, Paper, useComponentDefaultProps, Anchor, Tabs, Switch, ThemeIcon, Tooltip, CloseButton, Drawer, Divider, Checkbox, packSx, Affix, Accordion, Popover } from '@mantine/core';
2
2
  import { useClipboard, useClickOutside, useFullscreen, useDebouncedState, useMediaQuery, useDisclosure } from '@mantine/hooks';
3
- import { IconWorld, IconExternalLink, IconClipboard, IconSettings, IconMathGreater, IconMathLower, IconArrowsLeftRight, IconAlertCircle, IconAlertTriangle, IconCopy, IconDownload, IconDotsVertical, IconArrowsMinimize, IconArrowsMaximize, IconTrash, IconInfoCircleFilled, IconSearch, IconPhotoDown, IconVectorTriangle, IconArrowsSort, IconSortDescendingNumbers, IconSortDescendingLetters, IconSortAscendingNumbers, IconSortAscendingLetters, IconPlus, IconStack3, IconFilterOff, IconFilter, IconBox, IconClock, IconLanguage } from '@tabler/icons-react';
3
+ import { IconWorld, IconExternalLink, IconClipboard, IconSettings, IconMathGreater, IconMathLower, IconArrowsLeftRight, IconAlertCircle, IconAlertTriangle, IconCopy, IconDownload, IconDotsVertical, IconArrowLeft, IconArrowRight, IconArrowsMinimize, IconArrowsMaximize, IconTrash, IconInfoCircleFilled, IconSearch, IconPhotoDown, IconVectorTriangle, IconArrowsSort, IconSortDescendingNumbers, IconSortDescendingLetters, IconSortAscendingNumbers, IconSortAscendingLetters, IconPlus, IconStack3, IconFilterOff, IconFilter, IconBox, IconClock, IconHelpCircle, IconLanguage } from '@tabler/icons-react';
4
4
  import * as React13 from 'react';
5
5
  import React13__default, { createContext, forwardRef, useMemo, useCallback, useContext, useState, useEffect, useRef, Suspense, useLayoutEffect } from 'react';
6
6
  import { translationFactory } from '@datawheel/use-translation';
@@ -12,7 +12,7 @@ import { QueryClient, QueryClientProvider, useQuery, useQueryClient } from '@tan
12
12
  import debounce from 'lodash.debounce';
13
13
  import { MRT_ProgressBar, flexRender, MRT_TableBodyCell, MRT_ToolbarAlertBanner, useMantineReactTable, MantineReactTable, MRT_TablePagination } from 'mantine-react-table';
14
14
  import { formatAbbreviate, format } from 'd3plus-format';
15
- import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
15
+ import { TourProvider, useTour } from '@reactour/tour';
16
16
  import yn from 'yn';
17
17
  import { matchSorter } from 'match-sorter';
18
18
  import { BarChart, Donut, Geomap, LinePlot, Pie, StackedArea, Treemap } from 'd3plus-react';
@@ -66,7 +66,7 @@ var require_FileSaver = __commonJS({
66
66
  }
67
67
  var doc = view.document, get_URL = function() {
68
68
  return view.URL || view.webkitURL || view;
69
- }, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"), can_use_save_link = "download" in save_link, click = function(node) {
69
+ }, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"), can_use_save_link = "download" in save_link, click2 = function(node) {
70
70
  var event = new MouseEvent("click");
71
71
  node.dispatchEvent(event);
72
72
  }, is_safari = /constructor/i.test(view.HTMLElement) || view.safari, is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent), throw_outside = function(ex) {
@@ -142,7 +142,7 @@ var require_FileSaver = __commonJS({
142
142
  setTimeout(function() {
143
143
  save_link.href = object_url;
144
144
  save_link.download = name4;
145
- click(save_link);
145
+ click2(save_link);
146
146
  dispatch_all();
147
147
  revoke(object_url);
148
148
  filesaver.readyState = filesaver.DONE;
@@ -366,6 +366,67 @@ var defaultTranslation = {
366
366
  sort_asc: "Sort Asc",
367
367
  sort_desc: "Sort Desc"
368
368
  },
369
+ tour: {
370
+ controls: {
371
+ prev: "Previous",
372
+ next: "Next",
373
+ help: "Help"
374
+ },
375
+ steps: {
376
+ welcome: {
377
+ title: "Welcome to the Data Explorer",
378
+ text1: "This tutorial will guide you through the steps on how to use the Data Explorer",
379
+ text2: "By following this tutorial, you will be able to find data of interest and generate your own data tables and visualizations."
380
+ },
381
+ locale: {
382
+ title: "Multilingual",
383
+ text1: "The Data Explorer makes data available multiple languages.",
384
+ text2: "Use the dropdown menu to change the output language of the data."
385
+ },
386
+ dataset: {
387
+ title: "Selecting a Dataset",
388
+ text1: "Begin by selecting a topic of interest.",
389
+ text2: "Then, select a specific data table within that topic grouping."
390
+ },
391
+ search: {
392
+ title: "Searching for a Dataset",
393
+ text1: "You can also explore the list of topics and datasets.",
394
+ text2: "Start typing to filter the list of datasets with your search"
395
+ },
396
+ table: {
397
+ title: "Data Table",
398
+ text1: "Results are initially displayed as a data table.",
399
+ text2: "Each column header allows live sorting and filtering options."
400
+ },
401
+ columns: {
402
+ title: "Columns",
403
+ text1: "Select the columns that you are interested in viewing.",
404
+ text2: "When you toggle any column checkbox, the data table will automatically update to reflect your selection."
405
+ },
406
+ filters: {
407
+ title: "Filters",
408
+ text1: "You can filter the elements of each column that you want to display in the data table and visualization.",
409
+ text2: "If you want all available elements to be displayed, you don't need to apply filters."
410
+ },
411
+ download: {
412
+ title: "Downloading the Data",
413
+ text1: "You are able to download the information in CSV and JSON formats."
414
+ },
415
+ api: {
416
+ title: "API",
417
+ text1: "You can access the raw data programmatically using the Data Explorer JSON REST API."
418
+ },
419
+ vizbuilderTab: {
420
+ title: "Vizbuilder",
421
+ text1: "You can see the results of your query in different types of visualizations."
422
+ },
423
+ last: {
424
+ title: "Let's get started!",
425
+ text: "You are ready to start exploring the Data Explorer database and generate your own tables and visualizations.",
426
+ button: "Begin"
427
+ }
428
+ }
429
+ },
369
430
  transfer_input: {
370
431
  count_hidden: "{{n}} item hidden",
371
432
  count_hidden_plural: "{{n}} items hidden",
@@ -1578,6 +1639,10 @@ function useKey(params = {}) {
1578
1639
  }
1579
1640
 
1580
1641
  // src/hooks/settings.tsx
1642
+ var defaultToolbarConfig = {
1643
+ buttons: [],
1644
+ showLabels: true
1645
+ };
1581
1646
  var SettingsContext = createContext(void 0);
1582
1647
  var { Consumer: ContextConsumer, Provider: ContextProvider } = SettingsContext;
1583
1648
  function SettingsProvider(props) {
@@ -1592,10 +1657,11 @@ function SettingsProvider(props) {
1592
1657
  panels: props.panels,
1593
1658
  previewLimit: props.previewLimit || 50,
1594
1659
  paginationConfig: (_a = props.pagination) != null ? _a : { rowsLimits: [100, 300, 500, 1e3], defaultLimit: 100 },
1595
- measuresActive: props.measuresActive
1660
+ measuresActive: props.measuresActive,
1661
+ toolbarConfig: { ...defaultToolbarConfig, ...props.toolbarConfig }
1596
1662
  };
1597
1663
  },
1598
- [props.formatters, props.previewLimit]
1664
+ [props.formatters, props.previewLimit, props.toolbarConfig]
1599
1665
  );
1600
1666
  return /* @__PURE__ */ React13__default.createElement(ContextProvider, { value }, props.children);
1601
1667
  }
@@ -2489,6 +2555,7 @@ var ApiAndCsvButtons = (props) => {
2489
2555
  return /* @__PURE__ */ React13__default.createElement(Box, { id: "query-results-debug-view" }, /* @__PURE__ */ React13__default.createElement(Group, { spacing: "xs" }, url && /* @__PURE__ */ React13__default.createElement(
2490
2556
  Button,
2491
2557
  {
2558
+ id: "dex-api-btn",
2492
2559
  variant: "filled",
2493
2560
  color: "dark",
2494
2561
  leftIcon: /* @__PURE__ */ React13__default.createElement(IconCopy, { size: 20 }),
@@ -2521,7 +2588,7 @@ var DownloadQuery = ({ data }) => {
2521
2588
  if (components.length === 0 || data.length === 0) {
2522
2589
  return null;
2523
2590
  }
2524
- return /* @__PURE__ */ React13__default.createElement(Box, { id: "button-group-download-results" }, /* @__PURE__ */ React13__default.createElement(Group, { spacing: "xs" }, components, /* @__PURE__ */ React13__default.createElement(MenuOpts, { formats: formats.filter((f) => f !== "csv") })));
2591
+ 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") })));
2525
2592
  };
2526
2593
  var mimeTypes = {
2527
2594
  csv: "text/csv",
@@ -2695,7 +2762,7 @@ function BarsSVG() {
2695
2762
  );
2696
2763
  }
2697
2764
  function FullScreenSVG() {
2698
- return /* @__PURE__ */ React13__default.createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React13__default.createElement("g", { clipPath: "url(#clip0_905_15763)" }, /* @__PURE__ */ React13__default.createElement(
2765
+ return /* @__PURE__ */ React13__default.createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", strokeWidth: 1.5 }, /* @__PURE__ */ React13__default.createElement("g", { clipPath: "url(#clip0_905_15763)" }, /* @__PURE__ */ React13__default.createElement(
2699
2766
  "path",
2700
2767
  {
2701
2768
  d: "M3.33325 6.66668V5.00001C3.33325 4.55798 3.50885 4.13406 3.82141 3.8215C4.13397 3.50894 4.55789 3.33334 4.99992 3.33334H6.66659",
@@ -3158,6 +3225,7 @@ function TableView({
3158
3225
  return /* @__PURE__ */ React13__default.createElement(Box, { sx: { height: "100%" } }, /* @__PURE__ */ React13__default.createElement(Flex, { direction: "column", justify: "space-between", sx: { height: "100%", flex: "1 1 auto" } }, /* @__PURE__ */ React13__default.createElement(MRT_ProgressBar, { isTopToolbar: false, table }), /* @__PURE__ */ React13__default.createElement(
3159
3226
  ScrollArea,
3160
3227
  {
3228
+ id: "dex-table",
3161
3229
  h: isData ? "100%" : "auto",
3162
3230
  sx: {
3163
3231
  flex: "1 1 auto",
@@ -3220,11 +3288,22 @@ function TableView({
3220
3288
  key: header.id,
3221
3289
  sx: (theme) => isRowIndex ? index(theme) : base(theme)
3222
3290
  },
3223
- header.isPlaceholder ? null : flexRender(
3224
- (_a = header.column.columnDef.Header) != null ? _a : header.column.columnDef.header,
3225
- header.getContext()
3226
- ),
3227
- !isRowIndex && /* @__PURE__ */ React13__default.createElement(ColumnFilterCell, { isNumeric, header, table })
3291
+ /* @__PURE__ */ React13__default.createElement(
3292
+ Box,
3293
+ {
3294
+ sx: {
3295
+ display: "flex",
3296
+ flexFlow: "column",
3297
+ height: "100%",
3298
+ justifyContent: "space-between"
3299
+ }
3300
+ },
3301
+ header.isPlaceholder ? null : flexRender(
3302
+ (_a = header.column.columnDef.Header) != null ? _a : header.column.columnDef.header,
3303
+ header.getContext()
3304
+ ),
3305
+ !isRowIndex && /* @__PURE__ */ React13__default.createElement(ColumnFilterCell, { isNumeric, header, table })
3306
+ )
3228
3307
  );
3229
3308
  })))
3230
3309
  ),
@@ -3331,9 +3410,13 @@ function AddColumnsDrawer() {
3331
3410
  return /* @__PURE__ */ React13__default.createElement(React13__default.Fragment, null, /* @__PURE__ */ React13__default.createElement(
3332
3411
  Drawer,
3333
3412
  {
3413
+ id: "dex-column-drawer",
3334
3414
  opened,
3335
3415
  position: "right",
3336
3416
  onClose: close2,
3417
+ closeButtonProps: {
3418
+ id: "dex-column-drawer-close"
3419
+ },
3337
3420
  title: /* @__PURE__ */ React13__default.createElement(Group, null, /* @__PURE__ */ React13__default.createElement(IconPlus, { size: "1.2rem" }), /* @__PURE__ */ React13__default.createElement(Text, { fw: 700 }, t("params.add_columns"))),
3338
3421
  styles,
3339
3422
  overlayProps: {
@@ -3342,7 +3425,7 @@ function AddColumnsDrawer() {
3342
3425
  },
3343
3426
  /* @__PURE__ */ React13__default.createElement(MeasuresOptions, null),
3344
3427
  /* @__PURE__ */ React13__default.createElement(DrillDownOptions, null)
3345
- ), /* @__PURE__ */ React13__default.createElement(Group, { position: "center", sx: { flexWrap: "nowrap" } }, smallerThanMd ? /* @__PURE__ */ React13__default.createElement(ActionIcon, { onClick: open, size: "md", variant: "filled", color: theme.primaryColor }, /* @__PURE__ */ React13__default.createElement(IconStack3, { size: "0.75rem" })) : /* @__PURE__ */ React13__default.createElement(Button, { leftIcon: /* @__PURE__ */ React13__default.createElement(IconPlus, { size: "1.2rem" }), onClick: open, m: "md", size: "sm" }, t("params.add_columns"))));
3428
+ ), /* @__PURE__ */ React13__default.createElement(Group, { position: "center", sx: { flexWrap: "nowrap" } }, smallerThanMd ? /* @__PURE__ */ React13__default.createElement(ActionIcon, { onClick: open, size: "md", variant: "filled", color: theme.primaryColor }, /* @__PURE__ */ React13__default.createElement(IconStack3, { size: "0.75rem" })) : /* @__PURE__ */ React13__default.createElement(Button, { id: "dex-column-btn", leftIcon: /* @__PURE__ */ React13__default.createElement(IconPlus, { size: "1.2rem" }), onClick: open, m: "md", size: "sm" }, t("params.add_columns"))));
3346
3429
  }
3347
3430
  function DrillDownOptions() {
3348
3431
  const locale = useSelector$1(selectLocale);
@@ -3380,7 +3463,7 @@ function DimensionItem({
3380
3463
  activeItems
3381
3464
  }
3382
3465
  ));
3383
- return /* @__PURE__ */ React13__default.createElement("div", { key: dimension.name }, /* @__PURE__ */ React13__default.createElement(
3466
+ return /* @__PURE__ */ React13__default.createElement("div", { key: dimension.name, className: "dex-dimension-control", id: `dex-dimension-${dimension.name}` }, /* @__PURE__ */ React13__default.createElement(
3384
3467
  Divider,
3385
3468
  {
3386
3469
  my: "md",
@@ -3496,7 +3579,7 @@ function LevelItem({
3496
3579
  const isDisabled = isOtherHierarchySelected && !checked;
3497
3580
  if (!currentDrilldown) return;
3498
3581
  const paddingLeft = `${5 * depth + 5}px`;
3499
- return currentDrilldown && /* @__PURE__ */ React13__default.createElement(React13__default.Fragment, null, /* @__PURE__ */ React13__default.createElement(Group, { mt: "sm", position: "apart", key: level.name, noWrap: true }, /* @__PURE__ */ React13__default.createElement(
3582
+ return currentDrilldown && /* @__PURE__ */ React13__default.createElement(React13__default.Fragment, null, /* @__PURE__ */ React13__default.createElement(Group, { className: "dex-level-control", mt: "sm", position: "apart", key: level.name, noWrap: true }, /* @__PURE__ */ React13__default.createElement(
3500
3583
  Checkbox,
3501
3584
  {
3502
3585
  sx: { cursor: "pointer", paddingLeft },
@@ -3518,6 +3601,7 @@ function LevelItem({
3518
3601
  ), /* @__PURE__ */ React13__default.createElement(Group, { sx: { flexWrap: "nowrap" } }, /* @__PURE__ */ React13__default.createElement(
3519
3602
  ActionIcon,
3520
3603
  {
3604
+ className: "dex-level-filter",
3521
3605
  size: "sm",
3522
3606
  onClick: () => setActiveFilter((value) => !value),
3523
3607
  disabled: isDisabled
@@ -3843,6 +3927,7 @@ var toolbarSx = (t) => ({
3843
3927
  height: "fit-content"
3844
3928
  });
3845
3929
  function ToolbarButton({ icon, label, onClick = () => void 0 }) {
3930
+ const { toolbarConfig } = useSettings();
3846
3931
  return /* @__PURE__ */ React13__default.createElement(
3847
3932
  UnstyledButton,
3848
3933
  {
@@ -3850,7 +3935,23 @@ function ToolbarButton({ icon, label, onClick = () => void 0 }) {
3850
3935
  py: { base: "0.2rem", md: 0 },
3851
3936
  sx: (t) => ({ "& svg path": { stroke: t.colorScheme === "dark" ? "white" : "black" } })
3852
3937
  },
3853
- /* @__PURE__ */ React13__default.createElement(Group, { spacing: "xs", noWrap: true }, icon, /* @__PURE__ */ React13__default.createElement(Text, { size: "sm" }, label))
3938
+ /* @__PURE__ */ React13__default.createElement(Group, { spacing: "xs", noWrap: true }, icon, (toolbarConfig == null ? void 0 : toolbarConfig.showLabels) && label && /* @__PURE__ */ React13__default.createElement(Text, { size: "sm" }, label))
3939
+ );
3940
+ }
3941
+ function TourButton() {
3942
+ const { setIsOpen, setCurrentStep } = useTour();
3943
+ const { translate: t } = useTranslation();
3944
+ const startTour = () => {
3945
+ setCurrentStep(0);
3946
+ setIsOpen(true);
3947
+ };
3948
+ return /* @__PURE__ */ React13__default.createElement(
3949
+ ToolbarButton,
3950
+ {
3951
+ icon: /* @__PURE__ */ React13__default.createElement(IconHelpCircle, { strokeWidth: 1.5 }),
3952
+ label: t("tour.controls.help"),
3953
+ onClick: startTour
3954
+ }
3854
3955
  );
3855
3956
  }
3856
3957
  function Toolbar({
@@ -3860,6 +3961,7 @@ function Toolbar({
3860
3961
  const { translate: t } = useTranslation();
3861
3962
  const theme = useMantineTheme();
3862
3963
  const smallerThanMd = useMediaQuery(`(max-width: ${theme.breakpoints.md})`);
3964
+ const { toolbarConfig } = useSettings();
3863
3965
  const settings = /* @__PURE__ */ React13__default.createElement(
3864
3966
  Flex,
3865
3967
  {
@@ -3871,6 +3973,8 @@ function Toolbar({
3871
3973
  wrap: "nowrap",
3872
3974
  gap: "xs"
3873
3975
  },
3976
+ toolbarConfig == null ? void 0 : toolbarConfig.buttons.map((b) => /* @__PURE__ */ React13__default.createElement(ToolbarButton, { ...b })),
3977
+ /* @__PURE__ */ React13__default.createElement(TourButton, null),
3874
3978
  /* @__PURE__ */ React13__default.createElement(
3875
3979
  ToolbarButton,
3876
3980
  {
@@ -3882,6 +3986,8 @@ function Toolbar({
3882
3986
  );
3883
3987
  return smallerThanMd ? /* @__PURE__ */ React13__default.createElement(Menu, null, /* @__PURE__ */ React13__default.createElement(Menu.Target, null, /* @__PURE__ */ React13__default.createElement(ActionIcon, null, /* @__PURE__ */ React13__default.createElement(IconSettings, null))), /* @__PURE__ */ React13__default.createElement(Menu.Dropdown, null, settings)) : settings;
3884
3988
  }
3989
+
3990
+ // src/components/ExplorerResults.tsx
3885
3991
  var useStyles2 = createStyles(() => ({
3886
3992
  container: {
3887
3993
  minHeight: "40vh",
@@ -4011,7 +4117,7 @@ function SuccessResult(props) {
4011
4117
  pagination,
4012
4118
  setPagination
4013
4119
  }
4014
- )))))), /* @__PURE__ */ React13__default.createElement(ReactQueryDevtools, { initialIsOpen: true }));
4120
+ )))))));
4015
4121
  }
4016
4122
 
4017
4123
  // src/components/ParamsExplorer.tsx
@@ -4229,7 +4335,7 @@ function LocaleSelector() {
4229
4335
  if (localeOptions.length < 2) {
4230
4336
  return null;
4231
4337
  }
4232
- return /* @__PURE__ */ React13__default.createElement(Box, { id: "select-locale" }, /* @__PURE__ */ React13__default.createElement(Tooltip, { label: t("params.label_locale") }, /* @__PURE__ */ React13__default.createElement(
4338
+ return /* @__PURE__ */ React13__default.createElement(Box, { id: "dex-select-locale" }, /* @__PURE__ */ React13__default.createElement(Tooltip, { label: t("params.label_locale") }, /* @__PURE__ */ React13__default.createElement(
4233
4339
  SelectObject,
4234
4340
  {
4235
4341
  getLabel: "value",
@@ -4323,10 +4429,42 @@ function SideBarProvider(props) {
4323
4429
  }
4324
4430
  );
4325
4431
  }
4432
+ function SideBarControlBtn({ actionIconProps = {} }) {
4433
+ const { expanded, setExpanded } = useSideBar();
4434
+ const sx = (t) => ({
4435
+ alignSelf: "center",
4436
+ color: t.colorScheme === "dark" ? t.white : t.colors.gray[7]
4437
+ });
4438
+ if (expanded) return null;
4439
+ return /* @__PURE__ */ React13__default.createElement(
4440
+ ActionIcon,
4441
+ {
4442
+ onClick: () => setExpanded(!expanded),
4443
+ variant: "subtle",
4444
+ ...actionIconProps,
4445
+ sx: [sx, ...packSx(actionIconProps.sx)]
4446
+ },
4447
+ /* @__PURE__ */ React13__default.createElement(DataSetSVG, null)
4448
+ );
4449
+ }
4450
+ function SideBarControlBtnFixed() {
4451
+ const actionIconProps = {
4452
+ sx: (t) => ({
4453
+ backgroundColor: `${t.colorScheme === "dark" ? t.colors.dark[8] : t.colors.gray[1]} !important`,
4454
+ padding: `calc(${t.spacing.xs} / 2)`,
4455
+ borderRadius: t.radius.xl,
4456
+ width: 50,
4457
+ height: 50
4458
+ })
4459
+ };
4460
+ return /* @__PURE__ */ React13__default.createElement(Affix, { position: { left: "1rem", bottom: 150 } }, /* @__PURE__ */ React13__default.createElement(SideBarControlBtn, { actionIconProps }));
4461
+ }
4326
4462
  function SideBar(props) {
4327
4463
  const { expanded, setExpanded } = useSideBar();
4328
4464
  const { translate: t } = useTranslation();
4329
- return /* @__PURE__ */ React13__default.createElement(
4465
+ const theme = useMantineTheme();
4466
+ const smallerThanMd = useMediaQuery(`(max-width: ${theme.breakpoints.md})`);
4467
+ return /* @__PURE__ */ React13__default.createElement(React13__default.Fragment, null, smallerThanMd && /* @__PURE__ */ React13__default.createElement(SideBarControlBtnFixed, null), /* @__PURE__ */ React13__default.createElement(
4330
4468
  Box,
4331
4469
  {
4332
4470
  id: "dex-sidebar",
@@ -4341,8 +4479,8 @@ function SideBar(props) {
4341
4479
  boxSizing: "border-box",
4342
4480
  [t2.fn.smallerThan("md")]: {
4343
4481
  position: "absolute",
4344
- width: expanded ? 300 : 54,
4345
- height: expanded ? "calc(100vh - 75px)" : 54,
4482
+ width: expanded ? 300 : 0,
4483
+ height: expanded ? "calc(100vh - 50px)" : 0,
4346
4484
  bottom: expanded ? "unset" : t2.spacing.md,
4347
4485
  left: expanded ? "unset" : t2.spacing.md,
4348
4486
  overflow: "hidden",
@@ -4352,18 +4490,7 @@ function SideBar(props) {
4352
4490
  }
4353
4491
  })
4354
4492
  },
4355
- /* @__PURE__ */ React13__default.createElement(Flex, { h: "100%", direction: "column", justify: "flex-start" }, /* @__PURE__ */ React13__default.createElement(Box, { px: "sm", my: "sm" }, /* @__PURE__ */ React13__default.createElement(Flex, { direction: "column", sx: { flex: 1 } }, /* @__PURE__ */ React13__default.createElement(Flex, { align: "center", justify: "apart" }, /* @__PURE__ */ React13__default.createElement(
4356
- ActionIcon,
4357
- {
4358
- onClick: () => setExpanded(!expanded),
4359
- variant: "subtle",
4360
- sx: (t2) => ({
4361
- alignSelf: "center",
4362
- color: t2.colorScheme === "dark" ? t2.white : t2.colors.gray[7]
4363
- })
4364
- },
4365
- /* @__PURE__ */ React13__default.createElement(DataSetSVG, null)
4366
- ), /* @__PURE__ */ React13__default.createElement(
4493
+ /* @__PURE__ */ React13__default.createElement(Flex, { h: "100%", direction: "column", justify: "flex-start" }, /* @__PURE__ */ React13__default.createElement(Box, { px: "sm", my: "sm" }, /* @__PURE__ */ React13__default.createElement(Flex, { direction: "column", sx: { flex: 1 } }, /* @__PURE__ */ React13__default.createElement(Flex, { align: "center", justify: "apart" }, /* @__PURE__ */ React13__default.createElement(SideBarControlBtn, null), /* @__PURE__ */ React13__default.createElement(
4367
4494
  Group,
4368
4495
  {
4369
4496
  position: "apart",
@@ -4392,8 +4519,9 @@ function SideBar(props) {
4392
4519
  ), /* @__PURE__ */ React13__default.createElement(Box, { sx: { flexGrow: 1 } }))), /* @__PURE__ */ React13__default.createElement(
4393
4520
  ScrollArea,
4394
4521
  {
4395
- sx: (theme) => ({
4396
- borderTopColor: theme.colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[3],
4522
+ id: "dex-select-cube-area",
4523
+ sx: (theme2) => ({
4524
+ borderTopColor: theme2.colorScheme === "dark" ? theme2.colors.dark[6] : theme2.colors.gray[3],
4397
4525
  borderTopWidth: "1px",
4398
4526
  borderTopStyle: expanded ? "solid" : "none"
4399
4527
  })
@@ -4420,7 +4548,7 @@ function SideBar(props) {
4420
4548
  expanded ? /* @__PURE__ */ React13__default.createElement(IconChevronLeft, null) : /* @__PURE__ */ React13__default.createElement(IconChevronRight, null)
4421
4549
  )
4422
4550
  ))
4423
- );
4551
+ ));
4424
4552
  }
4425
4553
  var SideBar_default = SideBar;
4426
4554
  function SideBarItem({ children }) {
@@ -4445,6 +4573,7 @@ function Auto() {
4445
4573
  Input,
4446
4574
  {
4447
4575
  icon: /* @__PURE__ */ React13__default.createElement(IconSearch, null),
4576
+ id: "dex-search",
4448
4577
  radius: "xl",
4449
4578
  size: "md",
4450
4579
  placeholder: t("params.label_search"),
@@ -4597,7 +4726,7 @@ function SelectCubeInternal(props) {
4597
4726
  }
4598
4727
  }
4599
4728
  }, [selectedItem, cube, measuresActive]);
4600
- return /* @__PURE__ */ React13__default.createElement(Stack, { id: "select-cube", spacing: "xs", w: "100%" }, /* @__PURE__ */ React13__default.createElement(CubeTree, { items, locale, selectedItem }));
4729
+ return /* @__PURE__ */ React13__default.createElement(Stack, { id: "dex-select-cube", spacing: "xs", w: "100%" }, /* @__PURE__ */ React13__default.createElement(CubeTree, { items, locale, selectedItem }));
4601
4730
  }
4602
4731
  function AccordionControl(props) {
4603
4732
  return /* @__PURE__ */ React13__default.createElement(Box, { sx: { display: "flex", alignItems: "center" } }, /* @__PURE__ */ React13__default.createElement(Accordion.Control, { ...props }));
@@ -4764,7 +4893,7 @@ function CubeButton({
4764
4893
  }),
4765
4894
  onClick: () => onSelectCube(item, subtopic)
4766
4895
  },
4767
- table
4896
+ table != null ? table : item
4768
4897
  );
4769
4898
  }
4770
4899
  function SubtopicAccordion({
@@ -5534,7 +5663,248 @@ function stringifyMatrix(matrix, formatter2, format2) {
5534
5663
  ].join("\n");
5535
5664
  }
5536
5665
 
5666
+ // src/components/tour/ExplorerTour.tsx
5667
+ init_esm_shims();
5668
+
5669
+ // src/components/tour/useTourSteps.tsx
5670
+ init_esm_shims();
5671
+
5672
+ // src/components/tour/FirstStep.tsx
5673
+ init_esm_shims();
5674
+ function FirstStep({ introImage }) {
5675
+ const { translate: t } = useTranslation();
5676
+ return /* @__PURE__ */ React13__default.createElement(Container, { className: "tour-item tour-welcome", px: 0 }, introImage && /* @__PURE__ */ React13__default.createElement("div", { className: "tour-img" }, introImage), /* @__PURE__ */ React13__default.createElement(Box, { className: "tour-text", px: "md" }, /* @__PURE__ */ React13__default.createElement("h3", null, t("tour.steps.welcome.title")), /* @__PURE__ */ React13__default.createElement("p", null, t("tour.steps.welcome.text1")), /* @__PURE__ */ React13__default.createElement("p", null, t("tour.steps.welcome.text2"))));
5677
+ }
5678
+
5679
+ // src/components/tour/TourStep.tsx
5680
+ init_esm_shims();
5681
+ function TourStep(props) {
5682
+ const { title, texts } = props;
5683
+ const paragraphs = Array.isArray(texts) ? texts : [texts];
5684
+ return /* @__PURE__ */ React13__default.createElement(Container, { className: "tour-item tour-step", pt: "md" }, /* @__PURE__ */ React13__default.createElement("div", { className: "tour-text" }, /* @__PURE__ */ React13__default.createElement(Title, { order: 3 }, title), paragraphs.map((p, i) => /* @__PURE__ */ React13__default.createElement(Text, { component: "p", key: `p-${i + 1}` }, p))));
5685
+ }
5686
+
5687
+ // src/components/tour/LastStep.tsx
5688
+ init_esm_shims();
5689
+ function LastStep({ t }) {
5690
+ return /* @__PURE__ */ React13__default.createElement(Container, { className: "tour-item tour-last", pt: "md" }, /* @__PURE__ */ React13__default.createElement("div", { className: "tour-text" }, /* @__PURE__ */ React13__default.createElement(Title, { order: 3 }, t("tour.steps.last.title")), /* @__PURE__ */ React13__default.createElement(Text, { component: "p" }, t("tour.steps.last.text"))));
5691
+ }
5692
+
5693
+ // src/components/tour/useTourSteps.tsx
5694
+ var click = (selector) => () => {
5695
+ const element = document.querySelector(selector);
5696
+ element == null ? void 0 : element.click();
5697
+ };
5698
+ var useTourSteps = (tourConfig) => {
5699
+ const { translate: t } = useTranslation();
5700
+ const steps = useMemo(() => [
5701
+ {
5702
+ selector: "document",
5703
+ content: /* @__PURE__ */ React13__default.createElement(FirstStep, { introImage: tourConfig.introImage }),
5704
+ position: "center"
5705
+ },
5706
+ {
5707
+ selector: "#dex-select-locale",
5708
+ content: /* @__PURE__ */ React13__default.createElement(
5709
+ TourStep,
5710
+ {
5711
+ title: t("tour.steps.locale.title"),
5712
+ texts: [
5713
+ t("tour.steps.locale.text1"),
5714
+ t("tour.steps.locale.text2")
5715
+ ]
5716
+ }
5717
+ )
5718
+ },
5719
+ {
5720
+ selector: "#dex-select-cube-area",
5721
+ content: /* @__PURE__ */ React13__default.createElement(
5722
+ TourStep,
5723
+ {
5724
+ title: t("tour.steps.dataset.title"),
5725
+ texts: [
5726
+ t("tour.steps.dataset.text1"),
5727
+ t("tour.steps.dataset.text2")
5728
+ ]
5729
+ }
5730
+ )
5731
+ },
5732
+ {
5733
+ selector: "#dex-search",
5734
+ content: /* @__PURE__ */ React13__default.createElement(
5735
+ TourStep,
5736
+ {
5737
+ title: t("tour.steps.search.title"),
5738
+ texts: [
5739
+ t("tour.steps.search.text1"),
5740
+ t("tour.steps.search.text2")
5741
+ ]
5742
+ }
5743
+ )
5744
+ },
5745
+ {
5746
+ selector: "#dex-table",
5747
+ content: /* @__PURE__ */ React13__default.createElement(
5748
+ TourStep,
5749
+ {
5750
+ title: t("tour.steps.table.title"),
5751
+ texts: [
5752
+ t("tour.steps.table.text1"),
5753
+ t("tour.steps.table.text2")
5754
+ ]
5755
+ }
5756
+ )
5757
+ },
5758
+ {
5759
+ selector: "#dex-column-drawer-body",
5760
+ actionBefore: click("#dex-column-btn"),
5761
+ stepInteraction: true,
5762
+ content: /* @__PURE__ */ React13__default.createElement(
5763
+ TourStep,
5764
+ {
5765
+ title: t("tour.steps.columns.title"),
5766
+ texts: [
5767
+ t("tour.steps.columns.text1"),
5768
+ t("tour.steps.columns.text2")
5769
+ ]
5770
+ }
5771
+ )
5772
+ },
5773
+ {
5774
+ selector: ".dex-dimension-control",
5775
+ actionBefore: click(".dex-level-filter"),
5776
+ actionAfter: click("#dex-column-drawer-close"),
5777
+ stepInteraction: true,
5778
+ content: /* @__PURE__ */ React13__default.createElement(
5779
+ TourStep,
5780
+ {
5781
+ title: t("tour.steps.filters.title"),
5782
+ texts: [
5783
+ t("tour.steps.filters.text1"),
5784
+ t("tour.steps.filters.text2")
5785
+ ]
5786
+ }
5787
+ )
5788
+ },
5789
+ {
5790
+ selector: "#dex-btn-group-download",
5791
+ content: /* @__PURE__ */ React13__default.createElement(
5792
+ TourStep,
5793
+ {
5794
+ title: t("tour.steps.download.title"),
5795
+ texts: [t("tour.steps.download.text1")]
5796
+ }
5797
+ )
5798
+ },
5799
+ {
5800
+ selector: "#dex-api-btn",
5801
+ content: /* @__PURE__ */ React13__default.createElement(
5802
+ TourStep,
5803
+ {
5804
+ title: t("tour.steps.api.title"),
5805
+ texts: [t("tour.steps.api.text1")]
5806
+ }
5807
+ )
5808
+ },
5809
+ ...tourConfig.extraSteps || [],
5810
+ {
5811
+ selector: "viewport",
5812
+ content: /* @__PURE__ */ React13__default.createElement(LastStep, { t }),
5813
+ position: "center"
5814
+ }
5815
+ // ...
5816
+ ], [tourConfig.extraSteps]);
5817
+ return steps;
5818
+ };
5819
+
5820
+ // src/components/tour/ExplorerTour.tsx
5821
+ function PrevButton(props) {
5822
+ const { translate: t } = useTranslation();
5823
+ const handleClick = () => props.setCurrentStep(
5824
+ (s) => s === 0 ? props.steps.length - 1 : s - 1
5825
+ );
5826
+ return /* @__PURE__ */ React13__default.createElement(
5827
+ Button,
5828
+ {
5829
+ leftIcon: /* @__PURE__ */ React13__default.createElement(IconArrowLeft, { size: "0.8rem" }),
5830
+ onClick: handleClick,
5831
+ radius: 0,
5832
+ size: "lg",
5833
+ sx: { flex: "0 0 50%" },
5834
+ variant: "light",
5835
+ disabled: props.currentStep === 0,
5836
+ w: "50%"
5837
+ },
5838
+ t("tour.controls.prev")
5839
+ );
5840
+ }
5841
+ function NextButton(props) {
5842
+ const { translate: t } = useTranslation();
5843
+ const handleClick = () => {
5844
+ if (props.currentStep === props.steps.length - 1) {
5845
+ props.setIsOpen(false);
5846
+ props.setCurrentStep(0);
5847
+ } else {
5848
+ const nextStep = props.steps[props.currentStep + 1];
5849
+ if (nextStep.hasOwnProperty("actionBefore")) {
5850
+ nextStep.actionBefore();
5851
+ setTimeout(() => props.setCurrentStep((s) => s + 1), 250);
5852
+ } else {
5853
+ props.setCurrentStep((s) => s + 1);
5854
+ }
5855
+ }
5856
+ };
5857
+ return /* @__PURE__ */ React13__default.createElement(
5858
+ Button,
5859
+ {
5860
+ variant: "filled",
5861
+ w: "50%",
5862
+ size: "lg",
5863
+ sx: { flex: "0 0 50%" },
5864
+ onClick: handleClick,
5865
+ rightIcon: /* @__PURE__ */ React13__default.createElement(IconArrowRight, { size: "0.8rem" }),
5866
+ radius: 0
5867
+ },
5868
+ t("tour.controls.next")
5869
+ );
5870
+ }
5871
+ var withBase = (styles2) => (base) => ({ ...base, ...styles2 });
5872
+ function ExplorerTour({ children, tourConfig }) {
5873
+ const theme = useMantineTheme();
5874
+ const steps = useTourSteps(tourConfig);
5875
+ const styles2 = {
5876
+ popover: withBase({ padding: 0, borderRadius: theme.radius.md, overflow: "hidden" }),
5877
+ controls: withBase({ flexWrap: "wrap" }),
5878
+ navigation: withBase({
5879
+ justifyContent: "center",
5880
+ order: -1,
5881
+ width: "100%",
5882
+ margin: `0 auto ${theme.spacing.xs}`
5883
+ })
5884
+ };
5885
+ return /* @__PURE__ */ React13__default.createElement(
5886
+ TourProvider,
5887
+ {
5888
+ steps,
5889
+ position: "right",
5890
+ prevButton: PrevButton,
5891
+ nextButton: NextButton,
5892
+ showBadge: false,
5893
+ styles: styles2,
5894
+ rtl: theme.dir === "rtl",
5895
+ disableInteraction: true,
5896
+ ...tourConfig == null ? void 0 : tourConfig.tourProps
5897
+ },
5898
+ children
5899
+ );
5900
+ }
5901
+
5537
5902
  // src/components/Explorer.tsx
5903
+ var defaultTourConfig = {
5904
+ extraSteps: [],
5905
+ introImage: null,
5906
+ tourProps: {}
5907
+ };
5538
5908
  function ExplorerComponent(props) {
5539
5909
  const {
5540
5910
  locale = "en",
@@ -5544,6 +5914,7 @@ function ExplorerComponent(props) {
5544
5914
  withinReduxProvider = false,
5545
5915
  withMultiQuery = false,
5546
5916
  pagination,
5917
+ tourConfig,
5547
5918
  measuresActive
5548
5919
  } = props;
5549
5920
  const panels = useMemo(
@@ -5568,9 +5939,10 @@ function ExplorerComponent(props) {
5568
5939
  withPermalink: props.withPermalink,
5569
5940
  panels,
5570
5941
  pagination,
5571
- measuresActive
5942
+ measuresActive,
5943
+ toolbarConfig: props.toolbarConfig
5572
5944
  },
5573
- /* @__PURE__ */ React13__default.createElement(TranslationProvider, { defaultLocale: locale, translations: props.translations }, /* @__PURE__ */ React13__default.createElement(
5945
+ /* @__PURE__ */ React13__default.createElement(TranslationProvider, { defaultLocale: locale, translations: props.translations }, /* @__PURE__ */ React13__default.createElement(ExplorerTour, { tourConfig: { ...defaultTourConfig, ...tourConfig } }, /* @__PURE__ */ React13__default.createElement(
5574
5946
  ExplorerContent,
5575
5947
  {
5576
5948
  defaultCube: props.defaultCube,
@@ -5584,7 +5956,7 @@ function ExplorerComponent(props) {
5584
5956
  splash: props.splash,
5585
5957
  withMultiQuery
5586
5958
  }
5587
- ))
5959
+ )))
5588
5960
  );
5589
5961
  if (withinMantineProvider) {
5590
5962
  content = /* @__PURE__ */ React13__default.createElement(
@@ -6794,4 +7166,4 @@ file-saver/FileSaver.js:
6794
7166
  (*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js *)
6795
7167
  */
6796
7168
 
6797
- export { DebugView, ExplorerComponent as Explorer, PivotView, SettingsConsumer, TableView, TranslationConsumer, createVizbuilderView, reducer as explorerReducer, thunkExtraArg as explorerThunkExtraArg, defaultTranslation as translationDict, useSettings, useTranslation };
7169
+ export { DebugView, ExplorerComponent as Explorer, PivotView, SettingsConsumer, TableView, ToolbarButton, TourStep, TranslationConsumer, createVizbuilderView, reducer as explorerReducer, thunkExtraArg as explorerThunkExtraArg, defaultTranslation as translationDict, useSettings, useTranslation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datawheel/data-explorer",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./dist/main.js"
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@datawheel/use-translation": "^0.2.0",
22
+ "@reactour/tour": "^3.7.0",
22
23
  "@reduxjs/toolkit": "^1.9.3",
23
24
  "@tabler/icons-react": "^2.7.0",
24
25
  "@tanstack/react-query": "^5.51.23",