@datawheel/data-explorer 1.2.0-rc.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.d.mts CHANGED
@@ -204,6 +204,7 @@ interface TesseractDimension {
204
204
  type: DimensionType;
205
205
  hierarchies: TesseractHierarchy[];
206
206
  default_hierarchy: string;
207
+ required?: boolean;
207
208
  }
208
209
  interface TesseractHierarchy {
209
210
  name: string;
@@ -407,7 +408,9 @@ interface QueryResult<D = Record<string, unknown>> {
407
408
  url: string;
408
409
  }
409
410
  interface ResultEntityType {
410
- level: TesseractLevel;
411
+ level: TesseractLevel & {
412
+ dimension?: string;
413
+ };
411
414
  property: TesseractProperty;
412
415
  measure: TesseractMeasure;
413
416
  }
@@ -766,20 +769,22 @@ declare const defaultTranslation: {
766
769
  title: string;
767
770
  };
768
771
  list: {
769
- join: string;
770
- suffix: string;
771
- prefix: string;
772
772
  n_more: string;
773
773
  };
774
774
  title: {
775
+ main: string;
775
776
  main_on_period: string;
776
777
  main_over_period: string;
777
- main: string;
778
778
  measure_on_period: string;
779
779
  measure_over_period: string;
780
780
  nonidealstate: string;
781
- series_members: string;
781
+ scale_day: string;
782
+ scale_month: string;
783
+ scale_quarter: string;
784
+ scale_week: string;
785
+ scale_year: string;
782
786
  series: string;
787
+ series_members: string;
783
788
  time_range: string;
784
789
  total: string;
785
790
  };
package/dist/main.mjs CHANGED
@@ -309,15 +309,20 @@ var vizbuilderTranslation = {
309
309
  },
310
310
  title: {
311
311
  main_on_period: "{{values}} by {{series}} on {{time_period}}",
312
- main_over_period: "{{values}} by {{series}} over {{time}}",
312
+ main_over_period: "{{values}} by {{series}} over {{time_scale}}",
313
313
  main: "{{values}} by {{series}}",
314
314
  measure_on_period: "{{measure}} on {{period}}",
315
- measure_over_period: "{{values}} over {{time}}",
315
+ measure_over_period: "{{values}} over {{time_scale}}",
316
316
  nonidealstate: "No results",
317
317
  series_members: "{{series}} ({{members}})",
318
318
  series: "{{series}}",
319
319
  time_range: "in {{from}}-{{to}}",
320
- total: "Total: {{value}}"
320
+ total: "Total: {{value}}",
321
+ scale_year: "Annualy",
322
+ scale_quarter: "Quarterly",
323
+ scale_month: "Monthly",
324
+ scale_week: "Weekly",
325
+ scale_day: "Daily"
321
326
  },
322
327
  transient: {
323
328
  title_one_row: "The dataset has only one row and can't be used to generate charts.",
@@ -330,7 +335,10 @@ var defaultTranslation = {
330
335
  ...explorerTranslation,
331
336
  vizbuilder: vizbuilderTranslation
332
337
  };
333
- var { useTranslation, TranslationConsumer, TranslationProvider } = translationFactory({ defaultLocale: "en", defaultTranslation });
338
+ var { useTranslation, TranslationConsumer, TranslationProvider } = translationFactory({
339
+ defaultLocale: "en",
340
+ defaultTranslation
341
+ });
334
342
 
335
343
  // src/components/DebugView.tsx
336
344
  function DebugView(props) {
@@ -1725,17 +1733,19 @@ var useCubeItems = () => {
1725
1733
  return getValues(cubeItems || {});
1726
1734
  };
1727
1735
  var useDimensionItems = () => {
1728
- var _a;
1736
+ var _a, _b;
1729
1737
  const { data: schema } = useServerSchema();
1730
1738
  const { params } = useSelector(selectCurrentQueryItem);
1731
1739
  const dimensions = ((_a = schema == null ? void 0 : schema.cubeMap[params.cube]) == null ? void 0 : _a.dimensions) || [];
1740
+ const requiredDimensions = ((_b = schema == null ? void 0 : schema.cubeMap[params.cube]) == null ? void 0 : _b.annotations.required_dimensions) || [];
1732
1741
  return dimensions.map((dim) => ({
1733
1742
  item: {
1734
1743
  ...dim,
1735
1744
  hierarchies: dim.hierarchies.slice().map((hierarchy) => {
1736
1745
  hierarchy.levels.slice().sort((a, b) => getOrderValue(a) - getOrderValue(b));
1737
1746
  return hierarchy;
1738
- }).sort((a, b) => getOrderValue(a) - getOrderValue(b))
1747
+ }).sort((a, b) => getOrderValue(a) - getOrderValue(b)),
1748
+ required: requiredDimensions.includes(dim.name)
1739
1749
  },
1740
1750
  count: dim.hierarchies.reduce((acc, hie) => acc + hie.levels.length, 0),
1741
1751
  alpha: dim.hierarchies.reduce((acc, hie) => acc.concat(hie.name, "-"), "")
@@ -1947,27 +1957,27 @@ function calcMaxMemberCount(lengths) {
1947
1957
  return lengths.reduce((prev, curr) => prev * curr);
1948
1958
  }
1949
1959
  function pickDefaultDrilldowns(dimensions, cube) {
1950
- var _a;
1960
+ var _a, _b;
1951
1961
  const levels = [];
1952
- let timeComplete = void 0;
1962
+ let timeComplete;
1953
1963
  let suggestedLevels = [];
1964
+ let requiredDimensions = [];
1954
1965
  for (const key in cube.annotations) {
1955
1966
  if (key === "suggested_levels") {
1956
1967
  suggestedLevels = ((_a = cube.annotations[key]) == null ? void 0 : _a.split(",")) || [];
1957
1968
  }
1969
+ if (key === "required_dimensions") {
1970
+ requiredDimensions = ((_b = cube.annotations[key]) == null ? void 0 : _b.split(",")) || [];
1971
+ }
1958
1972
  }
1959
1973
  const findDefaultHierarchy = (dim) => dim.hierarchies.find((h) => h.name === dim.default_hierarchy) || dim.hierarchies[0];
1960
1974
  for (const dimension of dimensions) {
1961
- if (dimension.type === "time" || levels.length < 4) {
1975
+ if (dimension.type === "time" || requiredDimensions.includes(dimension.name) || levels.length < 4) {
1962
1976
  const hierarchy = findDefaultHierarchy(dimension);
1963
1977
  const hierarchyDepth = Math.max(...hierarchy.levels.map((l) => l.depth));
1964
1978
  const levelIndex = dimension.type === "geo" ? hierarchy.levels.length - 1 : 0;
1965
1979
  const defaultLevel = hierarchy.levels[levelIndex];
1966
- const suggestedLevelInHierarchy = hierarchy.levels.find(
1967
- (level) => suggestedLevels.includes(level.name)
1968
- );
1969
- const isSuggestedLevelDeepest = (suggestedLevelInHierarchy == null ? void 0 : suggestedLevelInHierarchy.depth) === hierarchyDepth;
1970
- if (dimension.type === "time" && dimension.annotations.de_time_complete === "true" && defaultLevel.depth < hierarchyDepth && !isSuggestedLevelDeepest) {
1980
+ if (dimension.type === "time" && dimension.annotations.de_time_complete === "true" && defaultLevel.depth < hierarchyDepth) {
1971
1981
  timeComplete = defaultLevel.name;
1972
1982
  }
1973
1983
  levels.push({ ...defaultLevel, type: dimension.type });
@@ -2990,6 +3000,15 @@ function FullScreenSVG() {
2990
3000
  function isColumnSorted(column, key) {
2991
3001
  return column == key;
2992
3002
  }
3003
+ function isRequiredColumn(column, finalKeys, dimensions) {
3004
+ if (column.entityType !== "level") return false;
3005
+ const dimCount = finalKeys.filter(
3006
+ (c) => c.entityType === "level" && c.entity.dimension === column.entity.dimension
3007
+ ).length;
3008
+ const dimension = dimensions.find((dim) => dim.name === column.entity.dimension);
3009
+ const isRequired = dimension ? dimension.required && dimCount <= 1 : false;
3010
+ return isRequired;
3011
+ }
2993
3012
  var removeColumn = (queryItem, entity) => {
2994
3013
  const newQuery = buildQuery(cloneDeep(queryItem));
2995
3014
  const params = newQuery.params;
@@ -3026,9 +3045,9 @@ var removeColumn = (queryItem, entity) => {
3026
3045
  }
3027
3046
  };
3028
3047
  var isProperty = (entity) => entity === "property";
3029
- function showTrashIcon(columns, type) {
3030
- const result = columns.filter((c) => c.entityType === type);
3031
- return result.length > 1 || isProperty(type);
3048
+ function showTrashIcon(entity, columns) {
3049
+ const result = columns.filter((c) => c.entityType === entity.entityType);
3050
+ return result.length > 1 || isProperty(entity.entityType);
3032
3051
  }
3033
3052
  var getActionIcon = (entityType) => {
3034
3053
  if (entityType === "measure") {
@@ -3129,6 +3148,7 @@ function useTable({
3129
3148
  const drilldowns = useSelector$1(selectDrilldownItems);
3130
3149
  const { code: locale } = useSelector$1(selectLocale);
3131
3150
  const measures = useSelector$1(selectMeasureItems);
3151
+ const dimensions = useDimensionItems();
3132
3152
  const actions2 = useActions();
3133
3153
  const { limit, offset } = useSelector$1(selectPaginationParams);
3134
3154
  const queryItem = useSelector$1(selectCurrentQueryItem);
@@ -3195,7 +3215,19 @@ function useTable({
3195
3215
  const tableData = (data == null ? void 0 : data.data) || [];
3196
3216
  const tableTypes = (data == null ? void 0 : data.types) || {};
3197
3217
  const totalRowCount = data == null ? void 0 : data.page.total;
3198
- const finalKeys = Object.values(tableTypes).filter((t2) => !t2.isId).filter(columnFilter).sort(columnSorting);
3218
+ const finalKeys = Object.values(tableTypes).map((d) => {
3219
+ var _a;
3220
+ if (d.entityType === "level") {
3221
+ return {
3222
+ ...d,
3223
+ entity: {
3224
+ ...d.entity,
3225
+ dimension: ((_a = drilldowns.find((dd) => dd.key === d.entity.name)) == null ? void 0 : _a.dimension) || ""
3226
+ }
3227
+ };
3228
+ }
3229
+ return d;
3230
+ }).filter((t2) => !t2.isId).filter(columnFilter).sort(columnSorting);
3199
3231
  const { translate: t } = useTranslation();
3200
3232
  const { getFormat, getFormatter } = useFormatter();
3201
3233
  const { idFormatters } = useidFormatters();
@@ -3216,7 +3248,7 @@ function useTable({
3216
3248
  maxSize: 50,
3217
3249
  size: 50
3218
3250
  };
3219
- const columnsDef = finalKeys.map((column) => {
3251
+ const columnsDef = finalKeys.map((keyCol) => {
3220
3252
  const {
3221
3253
  entity,
3222
3254
  entityType,
@@ -3225,8 +3257,8 @@ function useTable({
3225
3257
  valueType,
3226
3258
  range,
3227
3259
  isId
3228
- } = column;
3229
- const isNumeric = valueType === "number" && columnKey !== "Year";
3260
+ } = keyCol;
3261
+ const isNumeric = valueType === "number" && !/Year/i.test(columnKey);
3230
3262
  const formatterKey = getFormat(
3231
3263
  "aggregator" in entity ? entity : columnKey,
3232
3264
  isNumeric ? "Decimal" : "identity"
@@ -3249,8 +3281,9 @@ function useTable({
3249
3281
  }
3250
3282
  return 0;
3251
3283
  },
3252
- Header: ({ column: column2 }) => {
3284
+ Header: ({ column }) => {
3253
3285
  const isSorted = isColumnSorted(entity.name, sortKey);
3286
+ const showTrash = showTrashIcon(keyCol, finalKeys) && !isRequiredColumn(keyCol, finalKeys, dimensions);
3254
3287
  const isMobile = useMediaQuery(
3255
3288
  `(max-width: ${theme.breakpoints.sm}${/(?:px|em|rem|vh|vw|%)$/.test(theme.breakpoints.xs) ? "" : "px"})`
3256
3289
  );
@@ -3318,12 +3351,12 @@ function useTable({
3318
3351
  /* @__PURE__ */ React15__default.createElement(Text, { size: "sm" }, header),
3319
3352
  !isMobile && /* @__PURE__ */ React15__default.createElement(Box, { ml: "auto" }, actionSort)
3320
3353
  )),
3321
- /* @__PURE__ */ React15__default.createElement(Group, { position: "apart", w: { base: "100%", sm: "auto" } }, isMobile && actionSort, showTrashIcon(finalKeys, entityType) && /* @__PURE__ */ React15__default.createElement(
3354
+ /* @__PURE__ */ React15__default.createElement(Group, { position: "apart", w: { base: "100%", sm: "auto" } }, isMobile && actionSort, showTrash && /* @__PURE__ */ React15__default.createElement(
3322
3355
  CustomActionIcon_default,
3323
3356
  {
3324
3357
  label: `At least one ${getEntityText(entityType)} is required.`,
3325
- key: `remove-${column2.columnDef.header}`,
3326
- disabled: !showTrashIcon(finalKeys, entityType) || isLoading || isFetching,
3358
+ key: `remove-${column.columnDef.header}`,
3359
+ disabled: !showTrash || isLoading || isFetching,
3327
3360
  onClick: () => {
3328
3361
  const nextQueryItem = removeColumn(queryItem, entity);
3329
3362
  if (nextQueryItem) {
@@ -3331,7 +3364,7 @@ function useTable({
3331
3364
  updateURL(nextQueryItem);
3332
3365
  }
3333
3366
  },
3334
- showTooltip: !showTrashIcon(finalKeys, entityType),
3367
+ showTooltip: !showTrash,
3335
3368
  size: 25,
3336
3369
  ml: rem(5)
3337
3370
  },
@@ -3349,7 +3382,7 @@ function useTable({
3349
3382
  const cellValue = cell.getValue();
3350
3383
  const row = cell.row;
3351
3384
  const cellId = row.original[`${cell.column.id} ID`];
3352
- const idFormatter = idFormatters[`${column.localeLabel} ID`];
3385
+ const idFormatter = idFormatters[`${keyCol.localeLabel} ID`];
3353
3386
  return /* @__PURE__ */ React15__default.createElement(Flex, { justify: "space-between", sx: { width: "100%", maxWidth: 400 }, gap: "sm" }, /* @__PURE__ */ React15__default.createElement(
3354
3387
  Text,
3355
3388
  {
@@ -3903,14 +3936,14 @@ function LevelItem({
3903
3936
  });
3904
3937
  }, [locale, dimension, hierarchy, level, isSubMenu, t]);
3905
3938
  const currentDrilldown = drilldowns[level.name];
3906
- const otherHierarchyItems = activeItems.filter(
3939
+ const isOtherHierarchySelected = activeItems.some(
3907
3940
  (activeItem) => activeItem.dimension === dimension.name && activeItem.hierarchy !== hierarchy.name
3908
3941
  );
3909
- const isOtherHierarchySelected = otherHierarchyItems.length > 0;
3942
+ const isLastLevelInRequiredDimension = dimension.required && activeItems.filter((item) => item.dimension === dimension.name).length === 1;
3910
3943
  const cut = cutItems.find((cut2) => cut2.level === level.name);
3911
3944
  const checked = activeItems.map((i) => i.level).includes(level.name);
3912
- const disableUncheck = activeItems.length === 1 && checked && !otherHierarchyItems.length;
3913
- const isDisabled = otherHierarchyItems.length || !checked;
3945
+ const disableUncheck = activeItems.length === 1 && checked;
3946
+ const isDisabled = isLastLevelInRequiredDimension && checked;
3914
3947
  if (!currentDrilldown) return;
3915
3948
  const paddingLeft = `${5 * depth + 5}px`;
3916
3949
  const properties = currentDrilldown.properties.length ? currentDrilldown.properties : null;
@@ -3921,12 +3954,11 @@ function LevelItem({
3921
3954
  sx: { cursor: "pointer", paddingLeft },
3922
3955
  onChange: () => {
3923
3956
  var _a;
3924
- if (isOtherHierarchySelected) {
3925
- otherHierarchyItems.forEach((item) => {
3926
- actions2.updateDrilldown({
3927
- ...item,
3928
- active: false
3929
- });
3957
+ if (isOtherHierarchySelected && !checked) {
3958
+ activeItems.filter(
3959
+ (item) => item.dimension === dimension.name && item.hierarchy !== hierarchy.name
3960
+ ).forEach((item) => {
3961
+ actions2.updateDrilldown({ ...item, active: false });
3930
3962
  });
3931
3963
  }
3932
3964
  actions2.updateDrilldown({
@@ -3960,7 +3992,7 @@ function LevelItem({
3960
3992
  checked,
3961
3993
  label,
3962
3994
  size: "xs",
3963
- disabled: disableUncheck
3995
+ disabled: isDisabled || disableUncheck
3964
3996
  }
3965
3997
  ), /* @__PURE__ */ React15__default.createElement(Group, { sx: { flexWrap: "nowrap" } }, /* @__PURE__ */ React15__default.createElement(
3966
3998
  ActionIcon,
@@ -6679,7 +6711,15 @@ function ChartCard(props) {
6679
6711
  w: "100%",
6680
6712
  style: { overflow: "hidden", height: resolvedHeight, ...props.style }
6681
6713
  },
6682
- /* @__PURE__ */ React15__default.createElement(Stack, { spacing: "xs", p: "xs", style: { position: "relative" }, h: "100%", w: "100%" }, /* @__PURE__ */ React15__default.createElement(Group, { position: "center", spacing: "xs", align: "center" }, isFullMode && shareButton, downloadButtons, onFocus && focusButton), /* @__PURE__ */ React15__default.createElement(Box, { style: { flex: "1 1 auto" }, ref: setRefs, sx: { "& > .viz": { height: "100%" } } }, ChartComponent && (inView || hasBeenInView) ? /* @__PURE__ */ React15__default.createElement(ChartComponent, { config }) : /* @__PURE__ */ React15__default.createElement("div", { style: { height: "100%", width: "100%" } })))
6714
+ /* @__PURE__ */ React15__default.createElement(Stack, { spacing: "xs", p: "xs", style: { position: "relative" }, h: "100%", w: "100%" }, /* @__PURE__ */ React15__default.createElement(Group, { position: "center", spacing: "xs", align: "center" }, isFullMode && shareButton, downloadButtons, onFocus && focusButton), /* @__PURE__ */ React15__default.createElement(
6715
+ Box,
6716
+ {
6717
+ style: { flex: "1 1 auto" },
6718
+ ref: setRefs,
6719
+ sx: { "& > .viz": { height: "100%" } }
6720
+ },
6721
+ ChartComponent && (inView || hasBeenInView) ? /* @__PURE__ */ React15__default.createElement(ChartComponent, { config }) : /* @__PURE__ */ React15__default.createElement("div", { style: { height: "100%", width: "100%" } })
6722
+ ))
6683
6723
  ));
6684
6724
  }
6685
6725
  var getBackground = (node) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datawheel/data-explorer",
3
- "version": "1.2.0-rc.5",
3
+ "version": "1.3.0",
4
4
  "main": "./dist/main.mjs",
5
5
  "types": "./dist/main.d.mts",
6
6
  "files": [
@@ -79,4 +79,4 @@
79
79
  "vite": "^6.3.0",
80
80
  "wrangler": "^4.11.0"
81
81
  }
82
- }
82
+ }