@hisptz/dhis2-analytics 1.0.10 → 1.0.11

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 (154) hide show
  1. package/build/cjs/components/ChartAnalytics/ChartAnalytics.stories.js +1 -1
  2. package/build/cjs/components/CircularProgressDashboard/CircularProgressIndicator.stories.js +1 -1
  3. package/build/cjs/components/Map/Map.stories.js +1 -1
  4. package/build/cjs/components/Map/components/EarthEngineLayerConfiguration/EarthEngineLayerConfigModal.stories.js +1 -1
  5. package/build/cjs/components/Map/components/EarthEngineLayerConfiguration/EarthEngineLayerConfiguration.stories.js +1 -1
  6. package/build/cjs/components/Map/components/MapProvider/components/MapLayerProvider/hooks/index.js +8 -1
  7. package/build/cjs/components/Map/components/ThematicLayerConfiguration/ThematicLayerConfigModal.stories.js +1 -1
  8. package/build/cjs/components/Map/components/ThematicLayerConfiguration/ThematicLayerConfiguration.stories.js +1 -1
  9. package/build/cjs/components/PivotTable/PivotTable.stories.js +277 -0
  10. package/build/cjs/components/PivotTable/components/AssignedCategoriesIcon/index.js +32 -0
  11. package/build/cjs/components/PivotTable/components/PivotTable.js +60 -0
  12. package/build/cjs/components/PivotTable/components/PivotTableBody.js +41 -0
  13. package/build/cjs/components/PivotTable/components/PivotTableCell.js +40 -0
  14. package/build/cjs/components/PivotTable/components/PivotTableClippedAxis.js +26 -0
  15. package/build/cjs/components/PivotTable/components/PivotTableColumnHeaderCell.js +71 -0
  16. package/build/cjs/components/PivotTable/components/PivotTableColumnHeaders.js +60 -0
  17. package/build/cjs/components/PivotTable/components/PivotTableContainer.js +33 -0
  18. package/build/cjs/components/PivotTable/components/PivotTableDimensionLabelCell.js +77 -0
  19. package/build/cjs/components/PivotTable/components/PivotTableEmptyCell.js +22 -0
  20. package/build/cjs/components/PivotTable/components/PivotTableEmptyRow.js +33 -0
  21. package/build/cjs/components/PivotTable/components/PivotTableEngineContext.js +25 -0
  22. package/build/cjs/components/PivotTable/components/PivotTableHead.js +35 -0
  23. package/build/cjs/components/PivotTable/components/PivotTableHeaderCell.js +37 -0
  24. package/build/cjs/components/PivotTable/components/PivotTableRow.js +65 -0
  25. package/build/cjs/components/PivotTable/components/PivotTableRowHeaderCell.js +49 -0
  26. package/build/cjs/components/PivotTable/components/PivotTableTitleRow.js +52 -0
  27. package/build/cjs/components/PivotTable/components/PivotTableTitleRows.js +46 -0
  28. package/build/cjs/components/PivotTable/components/PivotTableValueCell.js +69 -0
  29. package/build/cjs/components/PivotTable/constants/dataTypes.js +129 -0
  30. package/build/cjs/components/PivotTable/constants/pivotTable.js +64 -0
  31. package/build/cjs/components/PivotTable/constants/predefinedDimensions.js +62 -0
  32. package/build/cjs/components/PivotTable/constants/valueTypes.js +55 -0
  33. package/build/cjs/components/PivotTable/data/column-data.json +210 -0
  34. package/build/cjs/components/PivotTable/hooks/useParentSize.js +41 -0
  35. package/build/cjs/components/PivotTable/hooks/useScrollPosition.js +38 -0
  36. package/build/cjs/components/PivotTable/hooks/useSortableColumns.js +34 -0
  37. package/build/cjs/components/PivotTable/hooks/useTableClipping.js +53 -0
  38. package/build/cjs/components/PivotTable/index.js +47 -0
  39. package/build/cjs/components/PivotTable/interfaces/index.js +1 -0
  40. package/build/cjs/components/PivotTable/services/adaptiveClippingController.js +197 -0
  41. package/build/cjs/components/PivotTable/services/engine.js +901 -0
  42. package/build/cjs/components/PivotTable/utils/getOuLevelAndGroupText.js +71 -0
  43. package/build/cjs/components/PivotTable/utils/index.js +322 -0
  44. package/build/cjs/components/PivotTable/utils/isColorBright.js +29 -0
  45. package/build/cjs/components/PivotTable/utils/layout/dimension.js +61 -0
  46. package/build/cjs/components/PivotTable/utils/layout/dimensionGetId.js +12 -0
  47. package/build/cjs/components/PivotTable/utils/layout/dimensionGetItems.js +12 -0
  48. package/build/cjs/components/PivotTable/utils/layout/dimensionIs.js +9 -0
  49. package/build/cjs/components/PivotTable/utils/layout/dimensionIsEmpty.js +9 -0
  50. package/build/cjs/components/PivotTable/utils/layout/dimensionIsValid.js +25 -0
  51. package/build/cjs/components/PivotTable/utils/legend.js +40 -0
  52. package/build/cjs/components/PivotTable/utils/ouIdHelper/index.js +27 -0
  53. package/build/cjs/components/SingleValueContainer/SingleValueContainer.stories.js +1 -1
  54. package/build/cjs/index.js +11 -0
  55. package/build/cjs/locales/en/translations.json +33 -0
  56. package/build/es/components/ChartAnalytics/ChartAnalytics.stories.js +1 -1
  57. package/build/es/components/CircularProgressDashboard/CircularProgressIndicator.stories.js +1 -1
  58. package/build/es/components/Map/Map.stories.js +1 -1
  59. package/build/es/components/Map/components/EarthEngineLayerConfiguration/EarthEngineLayerConfigModal.stories.js +1 -1
  60. package/build/es/components/Map/components/EarthEngineLayerConfiguration/EarthEngineLayerConfiguration.stories.js +1 -1
  61. package/build/es/components/Map/components/MapProvider/components/MapLayerProvider/hooks/index.js +8 -1
  62. package/build/es/components/Map/components/ThematicLayerConfiguration/ThematicLayerConfigModal.stories.js +1 -1
  63. package/build/es/components/Map/components/ThematicLayerConfiguration/ThematicLayerConfiguration.stories.js +1 -1
  64. package/build/es/components/PivotTable/PivotTable.stories.js +268 -0
  65. package/build/es/components/PivotTable/components/AssignedCategoriesIcon/index.js +24 -0
  66. package/build/es/components/PivotTable/components/PivotTable.js +51 -0
  67. package/build/es/components/PivotTable/components/PivotTableBody.js +33 -0
  68. package/build/es/components/PivotTable/components/PivotTableCell.js +32 -0
  69. package/build/es/components/PivotTable/components/PivotTableClippedAxis.js +18 -0
  70. package/build/es/components/PivotTable/components/PivotTableColumnHeaderCell.js +62 -0
  71. package/build/es/components/PivotTable/components/PivotTableColumnHeaders.js +52 -0
  72. package/build/es/components/PivotTable/components/PivotTableContainer.js +25 -0
  73. package/build/es/components/PivotTable/components/PivotTableDimensionLabelCell.js +68 -0
  74. package/build/es/components/PivotTable/components/PivotTableEmptyCell.js +14 -0
  75. package/build/es/components/PivotTable/components/PivotTableEmptyRow.js +25 -0
  76. package/build/es/components/PivotTable/components/PivotTableEngineContext.js +14 -0
  77. package/build/es/components/PivotTable/components/PivotTableHead.js +27 -0
  78. package/build/es/components/PivotTable/components/PivotTableHeaderCell.js +29 -0
  79. package/build/es/components/PivotTable/components/PivotTableRow.js +57 -0
  80. package/build/es/components/PivotTable/components/PivotTableRowHeaderCell.js +41 -0
  81. package/build/es/components/PivotTable/components/PivotTableTitleRow.js +42 -0
  82. package/build/es/components/PivotTable/components/PivotTableTitleRows.js +37 -0
  83. package/build/es/components/PivotTable/components/PivotTableValueCell.js +60 -0
  84. package/build/es/components/PivotTable/constants/dataTypes.js +98 -0
  85. package/build/es/components/PivotTable/constants/pivotTable.js +29 -0
  86. package/build/es/components/PivotTable/constants/predefinedDimensions.js +44 -0
  87. package/build/es/components/PivotTable/constants/valueTypes.js +27 -0
  88. package/build/es/components/PivotTable/data/column-data.json +210 -0
  89. package/build/es/components/PivotTable/hooks/useParentSize.js +33 -0
  90. package/build/es/components/PivotTable/hooks/useScrollPosition.js +30 -0
  91. package/build/es/components/PivotTable/hooks/useSortableColumns.js +27 -0
  92. package/build/es/components/PivotTable/hooks/useTableClipping.js +46 -0
  93. package/build/es/components/PivotTable/index.js +26 -0
  94. package/build/es/components/PivotTable/interfaces/index.js +1 -0
  95. package/build/es/components/PivotTable/services/adaptiveClippingController.js +191 -0
  96. package/build/es/components/PivotTable/services/engine.js +894 -0
  97. package/build/es/components/PivotTable/utils/getOuLevelAndGroupText.js +63 -0
  98. package/build/es/components/PivotTable/utils/index.js +309 -0
  99. package/build/es/components/PivotTable/utils/isColorBright.js +22 -0
  100. package/build/es/components/PivotTable/utils/layout/dimension.js +48 -0
  101. package/build/es/components/PivotTable/utils/layout/dimensionGetId.js +5 -0
  102. package/build/es/components/PivotTable/utils/layout/dimensionGetItems.js +5 -0
  103. package/build/es/components/PivotTable/utils/layout/dimensionIs.js +2 -0
  104. package/build/es/components/PivotTable/utils/layout/dimensionIsEmpty.js +2 -0
  105. package/build/es/components/PivotTable/utils/layout/dimensionIsValid.js +18 -0
  106. package/build/es/components/PivotTable/utils/legend.js +27 -0
  107. package/build/es/components/PivotTable/utils/ouIdHelper/index.js +17 -0
  108. package/build/es/components/SingleValueContainer/SingleValueContainer.stories.js +1 -1
  109. package/build/es/index.js +1 -0
  110. package/build/es/locales/en/translations.json +33 -0
  111. package/build/types/components/PivotTable/components/AssignedCategoriesIcon/index.d.ts +3 -0
  112. package/build/types/components/PivotTable/components/PivotTable.d.ts +12 -0
  113. package/build/types/components/PivotTable/components/PivotTableBody.d.ts +7 -0
  114. package/build/types/components/PivotTable/components/PivotTableCell.d.ts +17 -0
  115. package/build/types/components/PivotTable/components/PivotTableClippedAxis.d.ts +7 -0
  116. package/build/types/components/PivotTable/components/PivotTableColumnHeaderCell.d.ts +13 -0
  117. package/build/types/components/PivotTable/components/PivotTableColumnHeaders.d.ts +11 -0
  118. package/build/types/components/PivotTable/components/PivotTableContainer.d.ts +8 -0
  119. package/build/types/components/PivotTable/components/PivotTableDimensionLabelCell.d.ts +13 -0
  120. package/build/types/components/PivotTable/components/PivotTableEmptyCell.d.ts +7 -0
  121. package/build/types/components/PivotTable/components/PivotTableEmptyRow.d.ts +5 -0
  122. package/build/types/components/PivotTable/components/PivotTableEngineContext.d.ts +8 -0
  123. package/build/types/components/PivotTable/components/PivotTableHead.d.ts +21 -0
  124. package/build/types/components/PivotTable/components/PivotTableHeaderCell.d.ts +22 -0
  125. package/build/types/components/PivotTable/components/PivotTableRow.d.ts +19 -0
  126. package/build/types/components/PivotTable/components/PivotTableRowHeaderCell.d.ts +8 -0
  127. package/build/types/components/PivotTable/components/PivotTableTitleRow.d.ts +21 -0
  128. package/build/types/components/PivotTable/components/PivotTableTitleRows.d.ts +12 -0
  129. package/build/types/components/PivotTable/components/PivotTableValueCell.d.ts +8 -0
  130. package/build/types/components/PivotTable/constants/dataTypes.d.ts +90 -0
  131. package/build/types/components/PivotTable/constants/pivotTable.d.ts +29 -0
  132. package/build/types/components/PivotTable/constants/predefinedDimensions.d.ts +80 -0
  133. package/build/types/components/PivotTable/constants/valueTypes.d.ts +22 -0
  134. package/build/types/components/PivotTable/hooks/useParentSize.d.ts +8 -0
  135. package/build/types/components/PivotTable/hooks/useScrollPosition.d.ts +5 -0
  136. package/build/types/components/PivotTable/hooks/useSortableColumns.d.ts +8 -0
  137. package/build/types/components/PivotTable/hooks/useTableClipping.d.ts +24 -0
  138. package/build/types/components/PivotTable/index.d.ts +26 -0
  139. package/build/types/components/PivotTable/interfaces/index.d.ts +72 -0
  140. package/build/types/components/PivotTable/services/adaptiveClippingController.d.ts +44 -0
  141. package/build/types/components/PivotTable/services/engine.d.ts +252 -0
  142. package/build/types/components/PivotTable/utils/getOuLevelAndGroupText.d.ts +1 -0
  143. package/build/types/components/PivotTable/utils/index.d.ts +38 -0
  144. package/build/types/components/PivotTable/utils/isColorBright.d.ts +1 -0
  145. package/build/types/components/PivotTable/utils/layout/dimension.d.ts +35 -0
  146. package/build/types/components/PivotTable/utils/layout/dimensionGetId.d.ts +3 -0
  147. package/build/types/components/PivotTable/utils/layout/dimensionGetItems.d.ts +3 -0
  148. package/build/types/components/PivotTable/utils/layout/dimensionIs.d.ts +1 -0
  149. package/build/types/components/PivotTable/utils/layout/dimensionIsEmpty.d.ts +3 -0
  150. package/build/types/components/PivotTable/utils/layout/dimensionIsValid.d.ts +5 -0
  151. package/build/types/components/PivotTable/utils/legend.d.ts +11 -0
  152. package/build/types/components/PivotTable/utils/ouIdHelper/index.d.ts +10 -0
  153. package/build/types/index.d.ts +1 -0
  154. package/package.json +6 -3
@@ -0,0 +1,63 @@
1
+ import { dimensionGetItems } from './layout/dimensionGetItems.js';
2
+ import { dimensionIs } from './layout/dimensionIs.js';
3
+ import { ouIdHelper } from './ouIdHelper';
4
+ import { DIMENSION_ID_ORGUNIT } from '../constants/predefinedDimensions';
5
+ import i18n from '@dhis2/d2-i18n';
6
+ export const getOuLevelAndGroupText = (filter, metaData) => {
7
+ if (!dimensionIs(DIMENSION_ID_ORGUNIT)) {
8
+ return '';
9
+ }
10
+ const items = dimensionGetItems(filter);
11
+ const hasOuLevel = items.some(item => ouIdHelper.hasLevelPrefix(item.id));
12
+ const hasOuGroup = items.some(item => ouIdHelper.hasGroupPrefix(item.id));
13
+ const filterFragments = [];
14
+ if (hasOuGroup) {
15
+ filterFragments.push(getLevelAndGroupText(items, metaData, false));
16
+ }
17
+ if (hasOuLevel) {
18
+ filterFragments.push(getLevelAndGroupText(items, metaData, true));
19
+ }
20
+ return filterFragments.join(' - ');
21
+ };
22
+ const getLevelAndGroupText = (items, metaData, isLevel) => {
23
+ const getNameFromMetadata = id => metaData.items[id] ? metaData.items[id].name : id;
24
+ const dynamicOuItems = items.filter(item => isLevel ? ouIdHelper.hasLevelPrefix(item.id) : ouIdHelper.hasGroupPrefix(item.id));
25
+ const lastItem = dynamicOuItems.length > 1 ? dynamicOuItems.pop() : null;
26
+ const dynamicOuNames = dynamicOuItems.map(item => getNameFromMetadata(ouIdHelper.removePrefix(item.id))).join(', ');
27
+ let allDynamicOuNames;
28
+ if (lastItem) {
29
+ const lastOuName = getNameFromMetadata(ouIdHelper.removePrefix(lastItem.id));
30
+ allDynamicOuNames = i18n.t('{{dynamicOuNames}} and {{lastOuName}}', {
31
+ dynamicOuNames,
32
+ lastOuName
33
+ });
34
+ } else {
35
+ allDynamicOuNames = dynamicOuNames;
36
+ }
37
+ const staticOuNames = items.filter(item => !ouIdHelper.hasGroupPrefix(item.id) && !ouIdHelper.hasLevelPrefix(item.id)).map(item => getNameFromMetadata(item.id)).join(', ');
38
+ let ouLevelAndGroupText = '';
39
+ if (!staticOuNames) {
40
+ if (isLevel) {
41
+ ouLevelAndGroupText = i18n.t('{{allDynamicOuNames}} levels', {
42
+ allDynamicOuNames
43
+ });
44
+ } else {
45
+ ouLevelAndGroupText = i18n.t('{{allDynamicOuNames}} groups', {
46
+ allDynamicOuNames
47
+ });
48
+ }
49
+ } else {
50
+ if (isLevel) {
51
+ ouLevelAndGroupText = i18n.t('{{allDynamicOuNames}} levels in {{staticOuNames}}', {
52
+ allDynamicOuNames,
53
+ staticOuNames
54
+ });
55
+ } else {
56
+ ouLevelAndGroupText = i18n.t('{{allDynamicOuNames}} groups in {{staticOuNames}}', {
57
+ allDynamicOuNames,
58
+ staticOuNames
59
+ });
60
+ }
61
+ }
62
+ return ouLevelAndGroupText;
63
+ };
@@ -0,0 +1,309 @@
1
+ // @ts-nocheck
2
+
3
+ import { getOuLevelAndGroupText } from './getOuLevelAndGroupText';
4
+ import { dimensionGetItems } from './layout/dimensionGetItems';
5
+ import { dimensionIs } from './layout/dimensionIs';
6
+ import { ouIdHelper } from './ouIdHelper';
7
+ import { DIMENSION_ID_ORGUNIT } from '../constants/predefinedDimensions';
8
+ import { CLIPPED_CELL_MAX_SIZE, NUMBER_TYPE_COLUMN_PERCENTAGE, NUMBER_TYPE_ROW_PERCENTAGE, WRAPPED_TEXT_JUSTIFY_BUFFER, WRAPPED_TEXT_LINE_HEIGHT } from '../constants/pivotTable';
9
+ import { isNumericValueType } from '../constants/valueTypes';
10
+ import { colors } from '@dhis2/ui';
11
+ import { getColorByValueFromLegendSet, LEGEND_DISPLAY_STRATEGY_BY_DATA_ITEM, LEGEND_DISPLAY_STRATEGY_FIXED, LEGEND_DISPLAY_STYLE_FILL, LEGEND_DISPLAY_STYLE_TEXT } from './legend';
12
+ import { isColorBright } from './isColorBright.js';
13
+ export const parseValue = valueString => {
14
+ const parsedValue = parseFloat(valueString);
15
+ if (isNaN(parsedValue)) {
16
+ return valueString;
17
+ }
18
+ return parsedValue;
19
+ };
20
+ let canvas;
21
+ const getContext = fontSize => {
22
+ if (!canvas) {
23
+ canvas = document.createElement('canvas');
24
+ }
25
+ const ctx = canvas.getContext('2d');
26
+ ctx.font = `${fontSize}px Roboto, Arial, sans-serif`;
27
+ return ctx;
28
+ };
29
+ const measureText = function (text) {
30
+ let fontSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 11;
31
+ const ctx = getContext(fontSize);
32
+ const textMetrics = ctx.measureText(text);
33
+ return textMetrics.width;
34
+ };
35
+ export const measureTextWithWrapping = (text, _ref) => {
36
+ let {
37
+ fontSize = 11,
38
+ maxWidth = CLIPPED_CELL_MAX_SIZE,
39
+ justifyBuffer = WRAPPED_TEXT_JUSTIFY_BUFFER,
40
+ lineHeight = WRAPPED_TEXT_LINE_HEIGHT
41
+ } = _ref;
42
+ if (!text) {
43
+ return {
44
+ width: 0,
45
+ height: 0
46
+ };
47
+ }
48
+ // Multiple consecutive linebreaks are combined into one
49
+ const paragraphs = String(text).split(/\n/);
50
+ const lines = [];
51
+ let currentLine = '';
52
+ let currentLineWidth = 0;
53
+ let maxLineWidth = 0;
54
+ while (paragraphs.length) {
55
+ var _paragraphs$shift$spl, _paragraphs$shift;
56
+ // Currently, all different whitespace types are ignored and assumed to be just a space
57
+ const words = (_paragraphs$shift$spl = (_paragraphs$shift = paragraphs.shift()) === null || _paragraphs$shift === void 0 ? void 0 : _paragraphs$shift.split(/\s+/)) !== null && _paragraphs$shift$spl !== void 0 ? _paragraphs$shift$spl : [];
58
+ while (words.length) {
59
+ const nextWord = (currentLineWidth === 0 ? '' : ' ') + words.shift();
60
+ const nextWordWidth = measureText(nextWord, fontSize);
61
+ if (maxWidth && currentLineWidth + nextWordWidth > maxWidth) {
62
+ if (currentLineWidth <= maxWidth - justifyBuffer) {
63
+ // Wrapping this word would cause an unnaturally short line
64
+ // For now we allow the cell to expand to fit this word
65
+ // In the future, we might intelligently hyphenate the word
66
+ // TODO: if splitting words how would we localize hyphens?
67
+ // Do nothing, keep the word on this line
68
+ } else {
69
+ maxLineWidth = Math.max(currentLineWidth, maxLineWidth);
70
+ lines.push(currentLine);
71
+ currentLine = '';
72
+ currentLineWidth = 0;
73
+ words.unshift(nextWord.substring(1)); // Get rid of the extra space
74
+ continue;
75
+ }
76
+ }
77
+ currentLine += nextWord;
78
+ currentLineWidth += nextWordWidth;
79
+ }
80
+ if (currentLineWidth > 0) {
81
+ maxLineWidth = Math.max(currentLineWidth, maxLineWidth);
82
+ lines.push(currentLine);
83
+ }
84
+ }
85
+ return {
86
+ normalizedText: lines.join('\n'),
87
+ width: maxLineWidth,
88
+ height: lines.length * fontSize * lineHeight // TODO: use lineHeight=1 for last line?
89
+ };
90
+ };
91
+
92
+ const trimTrailingZeros = stringValue => stringValue.replace(/\.?0+$/, '');
93
+ const decimalSeparator = '.';
94
+ const separateDigitGroups = (stringValue, decimalSeparator) => {
95
+ const isNegative = stringValue[0] === '-';
96
+ const [integer, remainder] = stringValue.replace(/^-/, '').split('.');
97
+ const groups = [];
98
+ for (let i = integer.length; i > 0; i -= 3) {
99
+ groups.unshift(integer.substring(i - 3, i));
100
+ }
101
+ if (isNegative) {
102
+ groups[0] = '-' + groups[0];
103
+ }
104
+ if (remainder) {
105
+ const trimmedRemainder = trimTrailingZeros(remainder);
106
+ if (trimmedRemainder.length) {
107
+ groups[groups.length - 1] += decimalSeparator + remainder;
108
+ }
109
+ }
110
+ return groups;
111
+ };
112
+ const getSeparator = visualization => {
113
+ switch (visualization.digitGroupSeparator) {
114
+ case 'SPACE':
115
+ return ' ';
116
+ case 'COMMA':
117
+ return ',';
118
+ case 'NONE':
119
+ default:
120
+ return '';
121
+ }
122
+ };
123
+ const toFixedPrecisionString = (value, skipRounding) => {
124
+ if (typeof value !== 'number') {
125
+ // Values returned from the server should keep their string representation
126
+ return value;
127
+ }
128
+ const precision = skipRounding ? 10 : value > -1 && value < 1 ? 2 : 1;
129
+ return value.toFixed(precision);
130
+ };
131
+ export const renderValue = (value, valueType, visualization) => {
132
+ if (!isNumericValueType(valueType) || value === undefined) {
133
+ return String(value).replace(/[^\S\n]+/, ' ');
134
+ }
135
+ if (visualization.numberType === NUMBER_TYPE_ROW_PERCENTAGE || visualization.numberType === NUMBER_TYPE_COLUMN_PERCENTAGE) {
136
+ const stringValue = trimTrailingZeros(toFixedPrecisionString(value * 100, visualization.skipRounding));
137
+ return separateDigitGroups(stringValue, decimalSeparator).join(getSeparator(visualization)) + '%';
138
+ } else {
139
+ const stringValue = toFixedPrecisionString(value, visualization.skipRounding);
140
+ return separateDigitGroups(stringValue, decimalSeparator).join(getSeparator(visualization));
141
+ }
142
+ };
143
+ const getLegendSet = (engine, dxDimension) => {
144
+ var _engine$visualization, _engine$visualization2, _engine$visualization3;
145
+ let legendSetId;
146
+ switch ((_engine$visualization = engine.visualization.legend) === null || _engine$visualization === void 0 ? void 0 : _engine$visualization.strategy) {
147
+ case LEGEND_DISPLAY_STRATEGY_BY_DATA_ITEM:
148
+ if (dxDimension && dxDimension.legendSet) {
149
+ legendSetId = dxDimension.legendSet;
150
+ }
151
+ break;
152
+ case LEGEND_DISPLAY_STRATEGY_FIXED:
153
+ default:
154
+ legendSetId = (_engine$visualization2 = engine.visualization.legend) === null || _engine$visualization2 === void 0 ? void 0 : (_engine$visualization3 = _engine$visualization2.set) === null || _engine$visualization3 === void 0 ? void 0 : _engine$visualization3.id;
155
+ break;
156
+ }
157
+ return engine.legendSets[legendSetId];
158
+ };
159
+ const buildStyleObject = (legendColor, engine) => {
160
+ var _engine$visualization4;
161
+ const style = {};
162
+ switch ((_engine$visualization4 = engine.visualization.legend) === null || _engine$visualization4 === void 0 ? void 0 : _engine$visualization4.style) {
163
+ case LEGEND_DISPLAY_STYLE_TEXT:
164
+ style.color = legendColor;
165
+ break;
166
+ case LEGEND_DISPLAY_STYLE_FILL:
167
+ default:
168
+ style.backgroundColor = legendColor;
169
+ if (isColorBright(legendColor)) {
170
+ style.color = colors.grey900;
171
+ } else {
172
+ style.color = colors.white;
173
+ }
174
+ break;
175
+ }
176
+ return style;
177
+ };
178
+ export const applyLegendSet = (value, dxDimension, engine) => {
179
+ if (isNaN(value) || !engine.legendSets) {
180
+ return {};
181
+ }
182
+ const legendSet = getLegendSet(engine, dxDimension);
183
+ if (!legendSet) {
184
+ return {};
185
+ }
186
+ const legendColor = getColorByValueFromLegendSet(legendSet, value);
187
+ if (!legendColor) {
188
+ return {};
189
+ }
190
+ return buildStyleObject(legendColor, engine);
191
+ };
192
+ export function getFilterText(filters, metaData) {
193
+ if (!Array.isArray(filters) || !filters.length) {
194
+ return '';
195
+ }
196
+ const titleFragments = [];
197
+ let i;
198
+ let l;
199
+ filters.forEach(filter => {
200
+ const items = dimensionGetItems(filter);
201
+ if (dimensionIs(filter, DIMENSION_ID_ORGUNIT) && items.some(_ref2 => {
202
+ let {
203
+ id
204
+ } = _ref2;
205
+ return ouIdHelper.hasGroupPrefix(id) || ouIdHelper.hasLevelPrefix(id);
206
+ })) {
207
+ titleFragments.push(getOuLevelAndGroupText(filter, metaData));
208
+ } else {
209
+ const filterItems = metaData.dimensions[filter.dimension];
210
+ if (Array.isArray(filterItems)) {
211
+ l = filterItems.length;
212
+ let id;
213
+ const sectionParts = [];
214
+ for (i = 0; i < l; i++) {
215
+ id = filterItems[i];
216
+
217
+ // if the value is present in items take the name to show from there
218
+ if (metaData.items[id]) {
219
+ sectionParts.push(metaData.items[id].name);
220
+ }
221
+ // otherwise use the values directly
222
+ // this is a temporary fix to avoid app crashing when using filters with data items in EV
223
+ else {
224
+ sectionParts.push(metaData.items[filter.dimension].name + ': ' + filterItems.join(', '));
225
+ break;
226
+ }
227
+ }
228
+ titleFragments.push(sectionParts.join(', '));
229
+ }
230
+ }
231
+ });
232
+ return titleFragments.join(' - ');
233
+ }
234
+ const headerStacksAreEqual = (a, b, limit) => {
235
+ for (let i = 0; i <= limit; ++i) {
236
+ if (a[i] !== b[i]) {
237
+ return false;
238
+ }
239
+ }
240
+ return true;
241
+ };
242
+ export const getHeaderForDisplay = _ref3 => {
243
+ let {
244
+ start,
245
+ count,
246
+ index,
247
+ dimensionLevel,
248
+ getHeader,
249
+ showHierarchy
250
+ } = _ref3;
251
+ const header = getHeader(index);
252
+ const showHeader = index === start || !headerStacksAreEqual(header, getHeader(index - 1), dimensionLevel);
253
+ if (!showHeader) {
254
+ return null;
255
+ }
256
+ let span = 1;
257
+ for (let i = index + 1; i < start + count; ++i) {
258
+ if (!headerStacksAreEqual(getHeader(i), header, dimensionLevel)) {
259
+ break;
260
+ }
261
+ ++span;
262
+ }
263
+ const currentHeader = header[dimensionLevel];
264
+ const includesHierarchy = showHierarchy && (currentHeader === null || currentHeader === void 0 ? void 0 : currentHeader.hierarchy);
265
+ const label = includesHierarchy ? currentHeader.hierarchy.join(' / ') : currentHeader === null || currentHeader === void 0 ? void 0 : currentHeader.name;
266
+ return {
267
+ span,
268
+ label,
269
+ includesHierarchy
270
+ };
271
+ };
272
+ export const clipPartitionedAxis = _ref4 => {
273
+ let {
274
+ partitionSize,
275
+ partitions,
276
+ axisMap,
277
+ widthMap,
278
+ viewportWidth,
279
+ viewportPosition,
280
+ totalWidth
281
+ } = _ref4;
282
+ const partition = Math.floor(viewportPosition / partitionSize);
283
+ if (partitions[partition] === undefined) {
284
+ return {
285
+ indices: [0],
286
+ pre: 0,
287
+ post: 0
288
+ };
289
+ }
290
+ let start = partitions[partition] - partitions[0];
291
+ while (start < axisMap.length && widthMap[axisMap[start]].pre < viewportPosition) {
292
+ ++start;
293
+ }
294
+ start = start === 0 ? start : start - 1;
295
+ const pre = widthMap[axisMap[start]].pre;
296
+ const indices = [];
297
+ let end = start;
298
+ while (end < axisMap.length && widthMap[axisMap[end]].pre < viewportPosition + viewportWidth) {
299
+ indices.push(end);
300
+ ++end;
301
+ }
302
+ end = end === 0 ? end : end - 1;
303
+ const post = totalWidth - (widthMap[axisMap[end]].pre + widthMap[axisMap[end]].size);
304
+ return {
305
+ indices,
306
+ pre,
307
+ post
308
+ };
309
+ };
@@ -0,0 +1,22 @@
1
+ const calculateColorBrightness = function (rgb) {
2
+ if (!rgb) {
3
+ return 0;
4
+ }
5
+ return Math.round((parseInt(rgb[0]) * 299 + parseInt(rgb[1]) * 587 + parseInt(rgb[2]) * 114) / 1000);
6
+ };
7
+ const isHex = color => {
8
+ return typeof color === 'string' && color.charAt(0) === '#';
9
+ };
10
+ const hexToRgb = hex => {
11
+ let result;
12
+ if (typeof hex === "string") {
13
+ result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
14
+ }
15
+ return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null;
16
+ };
17
+ export const isColorBright = color => {
18
+ if (isHex(color)) {
19
+ color = hexToRgb(color);
20
+ }
21
+ return calculateColorBrightness(color) > 125;
22
+ };
@@ -0,0 +1,48 @@
1
+ import isObject from 'lodash/isObject';
2
+ import isString from 'lodash/isString';
3
+
4
+ // Dimension
5
+
6
+ export const DIMENSION = {
7
+ isValid: dimension => isObject(dimension)
8
+ };
9
+
10
+ // Props
11
+
12
+ export const DIMENSION_PROP_ID = {
13
+ name: 'dimension',
14
+ defaultValue: '',
15
+ required: true,
16
+ isValid: prop => isString(prop)
17
+ };
18
+ export const DIMENSION_PROP_ITEMS = {
19
+ name: 'items',
20
+ defaultValue: [],
21
+ required: false,
22
+ isValid: prop => Array.isArray(prop)
23
+ };
24
+ export const DIMENSION_PROP_FILTER = {
25
+ name: 'filter',
26
+ defaultValue: [],
27
+ required: false,
28
+ isValid: prop => isString(prop)
29
+ };
30
+ export const DIMENSION_PROP_LEGEND_SET = {
31
+ name: 'legendSet',
32
+ defaultValue: [],
33
+ required: false,
34
+ isValid: prop => isString(prop)
35
+ };
36
+ export const DIMENSION_PROP_PROGRAM_STAGE = {
37
+ name: 'programStage',
38
+ defaultValue: {},
39
+ required: false,
40
+ isValid: prop => isObject(prop)
41
+ };
42
+ export const DIMENSION_PROP_REPETITION = {
43
+ name: 'repetition',
44
+ defaultValue: [],
45
+ required: false,
46
+ isValid: prop => Array.isArray(prop)
47
+ };
48
+ export const DIMENSION_PROPS = [DIMENSION_PROP_ID, DIMENSION_PROP_ITEMS, DIMENSION_PROP_FILTER, DIMENSION_PROP_LEGEND_SET, DIMENSION_PROP_PROGRAM_STAGE, DIMENSION_PROP_REPETITION];
@@ -0,0 +1,5 @@
1
+ import { DIMENSION_PROP_ID, DIMENSION_PROP_PROGRAM_STAGE } from './dimension';
2
+ export const dimensionGetId = dimension => {
3
+ var _dimension$DIMENSION_;
4
+ return (_dimension$DIMENSION_ = dimension[DIMENSION_PROP_PROGRAM_STAGE.name]) !== null && _dimension$DIMENSION_ !== void 0 && _dimension$DIMENSION_.id ? `${dimension[DIMENSION_PROP_PROGRAM_STAGE.name].id}.${dimension[DIMENSION_PROP_ID.name]}` : dimension[DIMENSION_PROP_ID.name];
5
+ };
@@ -0,0 +1,5 @@
1
+ import { DIMENSION_PROP_ITEMS } from './dimension';
2
+ import { dimensionIsValid } from './dimensionIsValid';
3
+ export const dimensionGetItems = dimension => dimensionIsValid(dimension, {
4
+ requireItems: true
5
+ }) ? dimension[DIMENSION_PROP_ITEMS.name] : DIMENSION_PROP_ITEMS.defaultValue;
@@ -0,0 +1,2 @@
1
+ import { DIMENSION_PROP_ID } from './dimension';
2
+ export const dimensionIs = (dimension, dimensionId) => dimension[DIMENSION_PROP_ID.name] === dimensionId;
@@ -0,0 +1,2 @@
1
+ import { DIMENSION, DIMENSION_PROP_ITEMS } from './dimension';
2
+ export const dimensionIsEmpty = dimension => !(DIMENSION.isValid(dimension) && DIMENSION_PROP_ITEMS.isValid(dimension[DIMENSION_PROP_ITEMS.name]) && dimension[DIMENSION_PROP_ITEMS.name].length);
@@ -0,0 +1,18 @@
1
+ import { DIMENSION, DIMENSION_PROPS } from './dimension';
2
+ import { dimensionIsEmpty } from './dimensionIsEmpty';
3
+ export const dimensionIsValid = function (dimension) {
4
+ let {
5
+ requireItems
6
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
7
+ if (!DIMENSION.isValid(dimension)) {
8
+ return false;
9
+ }
10
+ const requiredProps = DIMENSION_PROPS.filter(prop => prop.required);
11
+ if (!requiredProps.every(prop => prop.isValid(dimension[prop.name]))) {
12
+ return false;
13
+ }
14
+ if (requireItems === true && dimensionIsEmpty(dimension)) {
15
+ return false;
16
+ }
17
+ return true;
18
+ };
@@ -0,0 +1,27 @@
1
+ export const LEGEND_DISPLAY_STRATEGY_BY_DATA_ITEM = 'BY_DATA_ITEM';
2
+ export const LEGEND_DISPLAY_STRATEGY_FIXED = 'FIXED';
3
+ export const LEGEND_DISPLAY_STYLE_FILL = 'FILL';
4
+ export const LEGEND_DISPLAY_STYLE_TEXT = 'TEXT';
5
+ export const getLegendByValueFromLegendSet = (legendSet, value) => {
6
+ var _legendSet$legends;
7
+ return Number.isInteger(parseInt(value)) ? legendSet === null || legendSet === void 0 ? void 0 : (_legendSet$legends = legendSet.legends) === null || _legendSet$legends === void 0 ? void 0 : _legendSet$legends.find(legend => value >= legend.startValue && value < legend.endValue // TODO: Confirm inclusive/exclusive bounds
8
+ ) : null;
9
+ };
10
+ export const getColorByValueFromLegendSet = (legendSet, value) => {
11
+ const legend = getLegendByValueFromLegendSet(legendSet, value);
12
+ return legend && legend.color;
13
+ };
14
+ export const getLegendSetByDisplayStrategy = _ref => {
15
+ let {
16
+ displayStrategy,
17
+ legendSets,
18
+ legendSetId
19
+ } = _ref;
20
+ if (displayStrategy === LEGEND_DISPLAY_STRATEGY_FIXED && legendSets.length) {
21
+ return legendSets[0];
22
+ } else if (displayStrategy === LEGEND_DISPLAY_STRATEGY_BY_DATA_ITEM) {
23
+ return legendSets.find(legendSet => legendSet.id === legendSetId);
24
+ } else {
25
+ return null;
26
+ }
27
+ };
@@ -0,0 +1,17 @@
1
+ const LEVEL_ID_PREFIX = 'LEVEL';
2
+ const GROUP_ID_PREFIX = 'OU_GROUP';
3
+ export const USER_ORG_UNIT = 'USER_ORGUNIT';
4
+ export const USER_ORG_UNIT_CHILDREN = 'USER_ORGUNIT_CHILDREN';
5
+ export const USER_ORG_UNIT_GRANDCHILDREN = 'USER_ORGUNIT_GRANDCHILDREN';
6
+ const hasGroupPrefix = id => id.substr(0, GROUP_ID_PREFIX.length) === GROUP_ID_PREFIX;
7
+ const hasLevelPrefix = id => id.substr(0, LEVEL_ID_PREFIX.length) === LEVEL_ID_PREFIX;
8
+ const stripLevelPrefix = id => hasLevelPrefix(id) ? id.substr(LEVEL_ID_PREFIX.length + 1) : id;
9
+ const stripGroupPrefix = id => hasGroupPrefix(id) ? id.substr(GROUP_ID_PREFIX.length + 1) : id;
10
+ const removePrefix = id => stripGroupPrefix(stripLevelPrefix(id));
11
+ export const ouIdHelper = Object.freeze({
12
+ addLevelPrefix: id => `${LEVEL_ID_PREFIX}-${removePrefix(id)}`,
13
+ addGroupPrefix: id => `${GROUP_ID_PREFIX}-${removePrefix(id)}`,
14
+ removePrefix,
15
+ hasGroupPrefix,
16
+ hasLevelPrefix
17
+ });
@@ -107,7 +107,7 @@ SingleValuesWithDecimalPlaces.args = {
107
107
  }]
108
108
  };
109
109
  export default {
110
- title: "Components/Single Value Container",
110
+ title: "Analytics/Single Value Container",
111
111
  component: SingleValueContainer,
112
112
  decorators: [SingleValueStory => {
113
113
  return /*#__PURE__*/React.createElement(SingleValueStory, null);
package/build/es/index.js CHANGED
@@ -2,4 +2,5 @@ export * from "./components/Map";
2
2
  export * from "./components/CircularProgressDashboard";
3
3
  export * from "./components/ChartAnalytics";
4
4
  export * from "./components/SingleValueContainer";
5
+ export * from "./components/PivotTable";
5
6
  export {};
@@ -124,5 +124,38 @@
124
124
  "Legend set is required": "Legend set is required",
125
125
  "Radius": "Radius",
126
126
  "Configure Thematic Layer": "Configure Thematic Layer",
127
+ "Program": "Program",
128
+ "Select a program": "Select a program",
129
+ "Indicators": "Indicators",
130
+ "Indicator group": "Indicator group",
131
+ "All groups": "All groups",
132
+ "Indicator": "Indicator",
133
+ "No indicator groups found": "No indicator groups found",
134
+ "Loading indicator groups": "Loading indicator groups",
135
+ "Data elements": "Data elements",
136
+ "Data element group": "Data element group",
137
+ "Data element": "Data element",
138
+ "No data element groups found": "No data element groups found",
139
+ "Loading data element groups": "Loading data element groups",
140
+ "Data sets": "Data sets",
141
+ "Data set": "Data set",
142
+ "All data sets": "All data sets",
143
+ "No data sets found": "No data sets found",
144
+ "Loading data sets": "Loading data sets",
145
+ "Event data items": "Event data items",
146
+ "All programs": "All programs",
147
+ "Event data item": "Event data item",
148
+ "No programs found": "No programs found",
149
+ "Loading programs": "Loading programs",
150
+ "Program indicators": "Program indicators",
151
+ "Program indicator": "Program indicator",
152
+ "Data": "Data",
153
+ "Organisation unit": "Organisation unit",
154
+ "Assigned Categories": "Assigned Categories",
155
+ "{{dynamicOuNames}} and {{lastOuName}}": "{{dynamicOuNames}} and {{lastOuName}}",
156
+ "{{allDynamicOuNames}} levels": "{{allDynamicOuNames}} levels",
157
+ "{{allDynamicOuNames}} groups": "{{allDynamicOuNames}} groups",
158
+ "{{allDynamicOuNames}} levels in {{staticOuNames}}": "{{allDynamicOuNames}} levels in {{staticOuNames}}",
159
+ "{{allDynamicOuNames}} groups in {{staticOuNames}}": "{{allDynamicOuNames}} groups in {{staticOuNames}}",
127
160
  "Loading ...": "Loading ..."
128
161
  }
@@ -0,0 +1,3 @@
1
+ /// <reference types="react" />
2
+ declare const AssignedCategoriesIcon: () => JSX.Element;
3
+ export default AssignedCategoriesIcon;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { LegendSet } from "@hisptz/dhis2-utils";
3
+ import { PivotTableVisualization, ToggleContextualMenuFunction } from "../interfaces";
4
+ export interface PivotTableProps {
5
+ data: Record<string, any>;
6
+ visualization: PivotTableVisualization;
7
+ legendSets?: Array<LegendSet>;
8
+ tableProps?: Record<string, any>;
9
+ renderCounter?: number;
10
+ onToggleContextualMenu?: ToggleContextualMenuFunction;
11
+ }
12
+ export declare const PivotTable: React.FC<PivotTableProps>;
@@ -0,0 +1,7 @@
1
+ /// <reference types="react" />
2
+ import { ClippingResult, ToggleContextualMenuFunction } from "../interfaces";
3
+ export interface PivotTableBodyProps {
4
+ clippingResult: ClippingResult;
5
+ onToggleContextualMenu?: ToggleContextualMenuFunction;
6
+ }
7
+ export declare const PivotTableBody: ({ clippingResult, onToggleContextualMenu }: PivotTableBodyProps) => JSX.Element;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ export interface PivotTableCellProps {
3
+ children: React.ReactNode;
4
+ classes: Array<string> | Record<string, any> | string;
5
+ dataTest: string;
6
+ isHeader: boolean;
7
+ style: Record<string, any>;
8
+ isSortable?: boolean;
9
+ index: number;
10
+ sortBy: {
11
+ order: number;
12
+ column: number;
13
+ };
14
+ onSortClick?: () => void;
15
+ [key: string]: any;
16
+ }
17
+ export declare const PivotTableCell: React.ForwardRefExoticComponent<Pick<PivotTableCellProps, keyof PivotTableCellProps> & React.RefAttributes<HTMLTableCellElement>>;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ export interface PivotTableClippedAxisProps {
3
+ axisClippingResult: Record<string, any>;
4
+ EmptyComponent: React.JSXElementConstructor<any>;
5
+ ItemComponent: React.JSXElementConstructor<any>;
6
+ }
7
+ export declare const PivotTableClippedAxis: ({ axisClippingResult, EmptyComponent, ItemComponent, }: PivotTableClippedAxisProps) => React.ReactElement<any, string | React.JSXElementConstructor<any>>;
@@ -0,0 +1,13 @@
1
+ /// <reference types="react" />
2
+ import { ClippingResult } from "../interfaces";
3
+ export interface PivotTableColumnHeaderCellProps {
4
+ clippingResult: ClippingResult;
5
+ index: number;
6
+ level: number;
7
+ onSortByColumn: (column: number) => void;
8
+ sortBy: {
9
+ order: number;
10
+ column: number;
11
+ } | null;
12
+ }
13
+ export declare const PivotTableColumnHeaderCell: ({ clippingResult, index, level, onSortByColumn, sortBy, }: PivotTableColumnHeaderCellProps) => JSX.Element | null;
@@ -0,0 +1,11 @@
1
+ /// <reference types="react" />
2
+ import { ClippingResult } from "../interfaces";
3
+ export interface PivotTableColumnHeadersProps {
4
+ clippingResult: ClippingResult;
5
+ onSortByColumn: (column: number) => void;
6
+ sortBy: {
7
+ order: number;
8
+ column: number;
9
+ } | null;
10
+ }
11
+ export declare const PivotTableColumnHeaders: ({ clippingResult, onSortByColumn, sortBy, }: PivotTableColumnHeadersProps) => JSX.Element | null;