@bundle-stats/ui 3.3.0-alpha.1 → 3.3.1-beta.8

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 (205) hide show
  1. package/CHANGELOG.md +65 -13
  2. package/build-storybook.log +4691 -0
  3. package/lib/app/app.js +137 -102
  4. package/lib/app/app.module.css +11 -4
  5. package/lib/app/header/header.js +1 -10
  6. package/lib/components/asset-info/asset-info.module.css +1 -0
  7. package/lib/components/bundle-assets/bundle-assets.js +104 -52
  8. package/lib/components/bundle-assets/bundle-assets.module.css +24 -26
  9. package/lib/components/bundle-assets-totals-table/bundle-assets-totals-table.js +13 -3
  10. package/lib/components/bundle-modules/bundle-modules.js +68 -68
  11. package/lib/components/bundle-modules/bundle-modules.module.css +11 -2
  12. package/lib/components/bundle-modules/bundle-modules.utils.js +14 -10
  13. package/lib/components/bundle-modules/index.js +4 -4
  14. package/lib/components/bundle-packages/bundle-packages.js +100 -44
  15. package/lib/components/bundle-packages/bundle-packages.module.css +17 -21
  16. package/lib/components/delta/delta.module.css +1 -0
  17. package/lib/components/job-header/job-header.js +3 -1
  18. package/lib/components/job-header/job-header.module.css +6 -8
  19. package/lib/components/jobs-header/jobs-header.module.css +5 -0
  20. package/lib/components/metrics-table/metrics-table.js +72 -59
  21. package/lib/components/metrics-table/metrics-table.module.css +18 -7
  22. package/lib/components/metrics-table-search/metrics-table-search.module.css +5 -4
  23. package/lib/components/metrics-table-title/metrics-table-title.js +9 -7
  24. package/lib/components/metrics-table-title/metrics-table-title.module.css +2 -0
  25. package/lib/components/module-info/module-info.js +2 -2
  26. package/lib/components/module-info/module-info.module.css +1 -0
  27. package/lib/components/summary/summary.js +1 -1
  28. package/lib/components/summary-item/summary-item.js +15 -18
  29. package/lib/components/summary-item/summary-item.module.css +1 -23
  30. package/lib/components/total-size-type-title/total-size-type-title.js +7 -3
  31. package/lib/constants.js +1 -1
  32. package/lib/css/default.css +14 -3
  33. package/lib/css/variables.css +17 -17
  34. package/lib/hooks/search-params.js +31 -31
  35. package/lib/layout/box/box.module.css +9 -0
  36. package/lib/layout/footer/footer.module.css +1 -1
  37. package/lib/layout/index.js +8 -0
  38. package/lib/layout/separator.js +48 -0
  39. package/lib/layout/separator.module.css +5 -0
  40. package/lib/ui/dropdown/dropdown.js +21 -26
  41. package/lib/ui/dropdown/dropdown.module.css +11 -12
  42. package/lib/ui/dropdown/dropdown.stories.js +2 -9
  43. package/lib/ui/empty-set/empty-set.js +5 -4
  44. package/lib/ui/empty-set/empty-set.module.css +2 -2
  45. package/lib/ui/filters/filters.container.js +1 -1
  46. package/lib/ui/filters/filters.js +22 -7
  47. package/lib/ui/filters/filters.module.css +3 -3
  48. package/lib/ui/horizontal-bar-chart/horizontal-bar-chart.js +1 -1
  49. package/lib/ui/hover-card/hover-card.js +102 -0
  50. package/lib/ui/hover-card/hover-card.module.css +29 -0
  51. package/lib/ui/hover-card/hover-card.stories.js +56 -0
  52. package/lib/ui/hover-card/index.js +18 -0
  53. package/lib/ui/icon/icon.js +6 -6
  54. package/lib/ui/icon/icon.module.css +1 -0
  55. package/lib/ui/index.js +8 -8
  56. package/lib/ui/table/table.js +5 -74
  57. package/lib/ui/table/table.module.css +41 -25
  58. package/lib/ui/table/table.stories.js +1 -1
  59. package/lib/ui/{popover → tag}/index.js +4 -4
  60. package/lib/ui/tag/tag.js +72 -0
  61. package/lib/ui/tag/tag.module.css +55 -0
  62. package/lib/ui/tag/tag.stories.js +38 -0
  63. package/lib/ui/tooltip/tooltip.js +10 -9
  64. package/lib/ui/tooltip/tooltip.module.css +2 -1
  65. package/lib-esm/app/app.js +132 -102
  66. package/lib-esm/app/app.module.css +11 -4
  67. package/lib-esm/app/header/header.js +1 -9
  68. package/lib-esm/components/asset-info/asset-info.module.css +1 -0
  69. package/lib-esm/components/bundle-assets/bundle-assets.js +97 -56
  70. package/lib-esm/components/bundle-assets/bundle-assets.module.css +24 -26
  71. package/lib-esm/components/bundle-assets-totals-table/bundle-assets-totals-table.js +7 -3
  72. package/lib-esm/components/bundle-modules/bundle-modules.js +50 -51
  73. package/lib-esm/components/bundle-modules/bundle-modules.module.css +11 -2
  74. package/lib-esm/components/bundle-modules/bundle-modules.utils.js +14 -6
  75. package/lib-esm/components/bundle-modules/index.js +5 -3
  76. package/lib-esm/components/bundle-packages/bundle-packages.js +88 -41
  77. package/lib-esm/components/bundle-packages/bundle-packages.module.css +17 -21
  78. package/lib-esm/components/delta/delta.module.css +1 -0
  79. package/lib-esm/components/job-header/job-header.js +2 -1
  80. package/lib-esm/components/job-header/job-header.module.css +6 -8
  81. package/lib-esm/components/jobs-header/jobs-header.module.css +5 -0
  82. package/lib-esm/components/metrics-table/metrics-table.js +61 -53
  83. package/lib-esm/components/metrics-table/metrics-table.module.css +18 -7
  84. package/lib-esm/components/metrics-table-search/metrics-table-search.module.css +5 -4
  85. package/lib-esm/components/metrics-table-title/metrics-table-title.js +8 -7
  86. package/lib-esm/components/metrics-table-title/metrics-table-title.module.css +2 -0
  87. package/lib-esm/components/module-info/module-info.js +2 -2
  88. package/lib-esm/components/module-info/module-info.module.css +1 -0
  89. package/lib-esm/components/summary/summary.js +1 -1
  90. package/lib-esm/components/summary-item/summary-item.js +14 -18
  91. package/lib-esm/components/summary-item/summary-item.module.css +1 -23
  92. package/lib-esm/components/total-size-type-title/total-size-type-title.js +6 -3
  93. package/lib-esm/constants.js +1 -1
  94. package/lib-esm/css/default.css +14 -3
  95. package/lib-esm/css/variables.css +17 -17
  96. package/lib-esm/hooks/search-params.js +27 -28
  97. package/lib-esm/layout/box/box.module.css +9 -0
  98. package/lib-esm/layout/footer/footer.module.css +1 -1
  99. package/lib-esm/layout/index.js +1 -0
  100. package/lib-esm/layout/separator.js +28 -0
  101. package/lib-esm/layout/separator.module.css +5 -0
  102. package/lib-esm/ui/dropdown/dropdown.js +20 -25
  103. package/lib-esm/ui/dropdown/dropdown.module.css +11 -12
  104. package/lib-esm/ui/dropdown/dropdown.stories.js +2 -7
  105. package/lib-esm/ui/empty-set/empty-set.js +4 -4
  106. package/lib-esm/ui/empty-set/empty-set.module.css +2 -2
  107. package/lib-esm/ui/filters/filters.js +20 -5
  108. package/lib-esm/ui/filters/filters.module.css +3 -3
  109. package/lib-esm/ui/hover-card/hover-card.js +83 -0
  110. package/lib-esm/ui/hover-card/hover-card.module.css +29 -0
  111. package/lib-esm/ui/hover-card/hover-card.stories.js +35 -0
  112. package/lib-esm/ui/hover-card/index.js +1 -0
  113. package/lib-esm/ui/icon/icon.js +3 -3
  114. package/lib-esm/ui/icon/icon.module.css +1 -0
  115. package/lib-esm/ui/index.js +1 -1
  116. package/lib-esm/ui/table/table.js +5 -65
  117. package/lib-esm/ui/table/table.module.css +41 -25
  118. package/lib-esm/ui/table/table.stories.js +1 -1
  119. package/lib-esm/ui/tag/index.js +1 -0
  120. package/lib-esm/ui/tag/tag.js +45 -0
  121. package/lib-esm/ui/tag/tag.module.css +55 -0
  122. package/lib-esm/ui/tag/tag.stories.js +23 -0
  123. package/lib-esm/ui/tooltip/tooltip.js +10 -9
  124. package/lib-esm/ui/tooltip/tooltip.module.css +2 -1
  125. package/package.json +33 -31
  126. package/src/app/app.jsx +136 -146
  127. package/src/app/app.module.css +11 -4
  128. package/src/app/header/header.jsx +1 -11
  129. package/src/assets/icons.svg +0 -8
  130. package/src/components/asset-info/asset-info.module.css +1 -0
  131. package/src/components/bundle-assets/bundle-assets.jsx +85 -31
  132. package/src/components/bundle-assets/bundle-assets.module.css +24 -26
  133. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.jsx +13 -25
  134. package/src/components/bundle-assets-totals-table/bundle-assets-totals-table.module.css +2 -2
  135. package/src/components/bundle-modules/bundle-modules.jsx +53 -36
  136. package/src/components/bundle-modules/bundle-modules.module.css +11 -2
  137. package/src/components/bundle-modules/bundle-modules.utils.js +16 -4
  138. package/src/components/bundle-modules/index.jsx +3 -0
  139. package/src/components/bundle-packages/bundle-packages.jsx +89 -39
  140. package/src/components/bundle-packages/bundle-packages.module.css +17 -21
  141. package/src/components/delta/delta.module.css +1 -0
  142. package/src/components/index.js +0 -1
  143. package/src/components/job-header/job-header.jsx +2 -1
  144. package/src/components/job-header/job-header.module.css +6 -8
  145. package/src/components/jobs-header/jobs-header.module.css +5 -0
  146. package/src/components/metrics-table/metrics-table.jsx +53 -44
  147. package/src/components/metrics-table/metrics-table.module.css +18 -7
  148. package/src/components/metrics-table-search/metrics-table-search.module.css +5 -4
  149. package/src/components/metrics-table-title/metrics-table-title.jsx +20 -21
  150. package/src/components/metrics-table-title/metrics-table-title.module.css +2 -0
  151. package/src/components/module-info/module-info.jsx +3 -7
  152. package/src/components/module-info/module-info.module.css +1 -0
  153. package/src/components/summary/summary.jsx +7 -8
  154. package/src/components/summary/summary.module.css +1 -1
  155. package/src/components/summary-item/summary-item.jsx +23 -40
  156. package/src/components/summary-item/summary-item.module.css +1 -34
  157. package/src/components/total-size-type-title/total-size-type-title.jsx +6 -9
  158. package/src/constants.js +11 -7
  159. package/src/css/default.css +14 -3
  160. package/src/css/variables.css +17 -17
  161. package/src/hooks/search-params.js +42 -33
  162. package/src/layout/box/box.module.css +9 -0
  163. package/src/layout/footer/footer.module.css +1 -1
  164. package/src/layout/index.js +1 -0
  165. package/src/layout/separator.jsx +25 -0
  166. package/src/layout/separator.module.css +5 -0
  167. package/src/ui/alert/alert.jsx +2 -7
  168. package/src/ui/dropdown/dropdown.jsx +16 -22
  169. package/src/ui/dropdown/dropdown.module.css +11 -12
  170. package/src/ui/empty-set/empty-set.jsx +25 -21
  171. package/src/ui/empty-set/empty-set.module.css +2 -2
  172. package/src/ui/filters/filters.jsx +64 -53
  173. package/src/ui/filters/filters.module.css +3 -3
  174. package/src/ui/hover-card/hover-card.jsx +73 -0
  175. package/src/ui/hover-card/hover-card.module.css +29 -0
  176. package/src/ui/hover-card/index.js +1 -0
  177. package/src/ui/icon/icon.jsx +11 -9
  178. package/src/ui/icon/icon.module.css +1 -0
  179. package/src/ui/index.js +1 -1
  180. package/src/ui/table/table.jsx +5 -52
  181. package/src/ui/table/table.module.css +41 -25
  182. package/src/ui/tag/index.js +1 -0
  183. package/src/ui/tag/tag.jsx +49 -0
  184. package/src/ui/tag/tag.module.css +55 -0
  185. package/src/ui/tooltip/tooltip.jsx +10 -14
  186. package/src/ui/tooltip/tooltip.module.css +2 -1
  187. package/storybook-static/favicon.ico +0 -0
  188. package/lib/ui/popover/popover.js +0 -92
  189. package/lib/ui/popover/popover.module.css +0 -66
  190. package/lib/ui/popover/popover.stories.js +0 -26
  191. package/lib-esm/ui/popover/index.js +0 -1
  192. package/lib-esm/ui/popover/popover.js +0 -71
  193. package/lib-esm/ui/popover/popover.module.css +0 -66
  194. package/lib-esm/ui/popover/popover.stories.js +0 -14
  195. package/src/assets/icons/alert-circle.svg +0 -1
  196. package/src/assets/icons/check-circle.svg +0 -1
  197. package/src/components/budget-info/budget-info.jsx +0 -65
  198. package/src/components/budget-info/budget-info.module.css +0 -40
  199. package/src/components/budget-info/index.js +0 -1
  200. package/src/components/budget-insights/budget-insights.jsx +0 -157
  201. package/src/components/budget-insights/budget-insights.module.css +0 -53
  202. package/src/components/budget-insights/index.js +0 -1
  203. package/src/ui/popover/index.js +0 -1
  204. package/src/ui/popover/popover.jsx +0 -79
  205. package/src/ui/popover/popover.module.css +0 -66
@@ -8,6 +8,8 @@
8
8
  margin-right: -1px; /** collapse border */
9
9
  }
10
10
 
11
+ .assetName {}
12
+
11
13
  .notPredictive {
12
14
  margin-right: var(--space-xxsmall);
13
15
  display: inline-block;
@@ -21,50 +23,46 @@
21
23
  vertical-align: top;
22
24
  }
23
25
 
24
- .assetInfoFlags {
25
- display: inline-block;
26
+ .assetNameTags {
27
+ display: inline;
26
28
  margin-right: var(--space-xxxsmall);
27
29
  }
28
30
 
29
- .assetInfoFlags:empty {
31
+ .assetNameTags:empty {
30
32
  display: none;
31
33
  }
32
34
 
33
- .flag {
34
- display: inline-block;
35
- width: var(--space-xsmall);
36
- height: var(--space-xsmall);
37
- font-family: var(--font-family);
38
- font-size: 8px;
39
- font-weight: bold;
40
- background: var(--color-highlight);
41
- color: var(--color-text-light);
42
- line-height: var(--space-xsmall);
43
- text-align: center;
44
- text-transform: uppercase;
35
+ .assetNameTag {
45
36
  vertical-align: middle;
46
37
  }
47
38
 
48
- .flag + .flag {
39
+ .assetNameTag + .assetNameTag {
49
40
  margin-left: 2px;
50
41
  }
51
42
 
52
- .flagEntry {
53
- composes: flag;
43
+ .assetNameTagEntry {
54
44
  background: var(--color-info-light);
55
- color: var(--color-background);
56
45
  }
57
46
 
58
- .flagInitial {
59
- composes: flag;
47
+ .assetNameTagEntry::before {
48
+ content: 'e';
49
+ }
50
+
51
+ .assetNameTagInitial {
60
52
  background: var(--color-info-light);
61
- color: var(--color-background);
62
53
  }
63
54
 
64
- .flagChunk {
65
- composes: flag;
66
- background: var(--color-info);
67
- color: var(--color-background);
55
+ .assetNameTagInitial::before {
56
+ content: 'i';
57
+ }
58
+
59
+ .assetNameTagChunk::before {
60
+ content: 'c';
61
+ }
62
+
63
+ .assetNameText {
64
+ display: inline;
65
+ vertical-align: middle;
68
66
  }
69
67
 
70
68
  /* Tooltip not predictive */
@@ -1,4 +1,7 @@
1
1
  import _get from "lodash/get";
2
+
3
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
4
+
2
5
  import React, { useCallback, useMemo } from 'react';
3
6
  import PropTypes from 'prop-types';
4
7
  import { getBundleAssetsFileTypeComponentLink } from '@bundle-stats/utils';
@@ -15,7 +18,8 @@ export const BundleAssetsTotalsTable = _ref => {
15
18
  let {
16
19
  className,
17
20
  jobs,
18
- customComponentLink: CustomComponentLink
21
+ customComponentLink: CustomComponentLink,
22
+ ...restProps
19
23
  } = _ref;
20
24
  const items = useMemo(() => webpack.compareBySection.sizes(jobs), [jobs]);
21
25
  const budgets = (_jobs$0$insights = jobs[0].insights) == null ? void 0 : (_jobs$0$insights$webp = _jobs$0$insights.webpack) == null ? void 0 : _jobs$0$insights$webp.budgets;
@@ -43,13 +47,13 @@ export const BundleAssetsTotalsTable = _ref => {
43
47
  budgetInsight: budget
44
48
  })));
45
49
  }, [CustomComponentLink]);
46
- return /*#__PURE__*/React.createElement(MetricsTable, {
50
+ return /*#__PURE__*/React.createElement(MetricsTable, _extends({
47
51
  className: className,
48
52
  runs: jobs,
49
53
  items: items,
50
54
  renderRowHeader: renderRowHeader,
51
55
  showHeaderSum: true
52
- });
56
+ }, restProps));
53
57
  };
54
58
  BundleAssetsTotalsTable.defaultProps = {
55
59
  className: '',
@@ -15,8 +15,9 @@ import { FlexStack } from '../../layout/flex-stack';
15
15
  import { EmptySet } from '../../ui/empty-set';
16
16
  import { FileName } from '../../ui/file-name';
17
17
  import { Filters } from '../../ui/filters';
18
- import { Popover } from '../../ui/popover';
18
+ import { HoverCard } from '../../ui/hover-card';
19
19
  import { SortDropdown } from '../../ui/sort-dropdown';
20
+ import { Tag } from '../../ui/tag';
20
21
  import { Toolbar } from '../../ui/toolbar';
21
22
  import { MetricsTable } from '../metrics-table';
22
23
  import { MetricsTableSearch } from '../metrics-table-search';
@@ -35,35 +36,41 @@ const RowHeader = _ref => {
35
36
 
36
37
  const chunkIds = _map(chunks, 'id');
37
38
 
38
- const [showPopopver, setPopover] = useState(false);
39
- const handleOnMouseEnter = useCallback(() => setPopover(true), [showPopopver]);
40
- const content = /*#__PURE__*/React.createElement(FileName, {
39
+ const [showHoverCard, setHoverCard] = useState(false);
40
+ const handleOnMouseEnter = useCallback(() => setHoverCard(true), [showHoverCard]);
41
+ const content = useMemo(() => /*#__PURE__*/React.createElement("span", {
42
+ className: css.name
43
+ }, row.duplicated && /*#__PURE__*/React.createElement(Tag, {
44
+ className: css.nameTagDuplicated,
45
+ size: "small",
46
+ kind: Tag.KINDS.DANGER
47
+ }), /*#__PURE__*/React.createElement(FileName, {
48
+ className: css.nameText,
41
49
  name: row.label
42
- });
43
- return /*#__PURE__*/React.createElement("div", {
44
- onMouseEnter: handleOnMouseEnter
45
- }, !showPopopver ? content : /*#__PURE__*/React.createElement(Popover, {
46
- ariaLabel: "View module info",
50
+ })), [row]);
51
+
52
+ if (!showHoverCard) {
53
+ return /*#__PURE__*/React.createElement("div", {
54
+ onMouseEnter: handleOnMouseEnter
55
+ }, content);
56
+ }
57
+
58
+ return /*#__PURE__*/React.createElement(HoverCard, {
47
59
  label: content
48
- }, _ref2 => {
49
- let {
50
- popoverToggle
51
- } = _ref2;
52
- return /*#__PURE__*/React.createElement(ModuleInfo, {
53
- className: css.namePopover,
54
- item: row,
55
- chunks: chunks,
56
- chunkIds: chunkIds,
57
- labels: labels,
58
- customComponentLink: CustomComponentLink,
59
- onClick: popoverToggle
60
- });
60
+ }, /*#__PURE__*/React.createElement(ModuleInfo, {
61
+ className: css.namePopover,
62
+ item: row,
63
+ chunks: chunks,
64
+ chunkIds: chunkIds,
65
+ labels: labels,
66
+ customComponentLink: CustomComponentLink
61
67
  }));
62
68
  };
63
69
 
64
70
  RowHeader.propTypes = {
65
71
  row: PropTypes.shape({
66
- label: PropTypes.string
72
+ label: PropTypes.string,
73
+ duplicated: PropTypes.bool
67
74
  }).isRequired,
68
75
  chunks: PropTypes.arrayOf(PropTypes.shape({
69
76
  id: PropTypes.string,
@@ -75,22 +82,7 @@ RowHeader.propTypes = {
75
82
  RowHeader.defaultProps = {
76
83
  chunks: []
77
84
  };
78
-
79
- const getRenderRowHeader = _ref3 => {
80
- let {
81
- labels,
82
- chunks,
83
- CustomComponentLink
84
- } = _ref3;
85
- return row => /*#__PURE__*/React.createElement(RowHeader, {
86
- row: row,
87
- chunks: chunks,
88
- labels: labels,
89
- CustomComponentLink: CustomComponentLink
90
- });
91
- };
92
-
93
- export const BundleModules = _ref4 => {
85
+ export const BundleModules = _ref2 => {
94
86
  let {
95
87
  className,
96
88
  jobs,
@@ -108,14 +100,14 @@ export const BundleModules = _ref4 => {
108
100
  updateSearch,
109
101
  hasActiveFilters,
110
102
  customComponentLink: CustomComponentLink
111
- } = _ref4;
103
+ } = _ref2;
112
104
  const rootClassName = cx(css.root, className);
113
- const labels = useMemo(() => _map(jobs, 'label'), [jobs]);
114
- const renderRowHeader = useMemo(() => getRenderRowHeader({
115
- labels,
116
- chunks,
117
- CustomComponentLink
118
- }), [labels, chunks]);
105
+ const renderRowHeader = useCallback(row => /*#__PURE__*/React.createElement(RowHeader, {
106
+ row: row,
107
+ chunks: chunks,
108
+ labels: _map(jobs, 'label'),
109
+ CustomComponentLink: CustomComponentLink
110
+ }), [jobs, chunks, CustomComponentLink]);
119
111
  const emptyMessage = /*#__PURE__*/React.createElement(EmptySet, {
120
112
  resources: "modules",
121
113
  filtered: totalRowCount !== 0,
@@ -128,6 +120,11 @@ export const BundleModules = _ref4 => {
128
120
  defaultValue: filters.changed,
129
121
  disabled: jobs.length <= 1
130
122
  },
123
+ [MODULE_FILTERS.DUPLICATED]: {
124
+ label: 'Duplicated',
125
+ defaultValue: filters[MODULE_FILTERS.DUPLICATED],
126
+ disabled: jobs.length <= 1
127
+ },
131
128
  [MODULE_SOURCE_TYPE]: {
132
129
  label: 'Source',
133
130
  [MODULE_FILTERS.FIRST_PARTY]: {
@@ -143,11 +140,11 @@ export const BundleModules = _ref4 => {
143
140
  ...(!_isEmpty(chunks) && {
144
141
  [MODULE_CHUNK]: {
145
142
  label: 'Chunk',
146
- ...chunks.reduce((chunkFilters, _ref5) => {
143
+ ...chunks.reduce((chunkFilters, _ref3) => {
147
144
  let {
148
145
  id,
149
146
  name
150
- } = _ref5;
147
+ } = _ref3;
151
148
  return { ...chunkFilters,
152
149
  [id]: {
153
150
  label: name,
@@ -172,10 +169,10 @@ export const BundleModules = _ref4 => {
172
169
  className: rootClassName
173
170
  }, /*#__PURE__*/React.createElement(Toolbar, {
174
171
  className: css.toolbar,
175
- renderActions: _ref6 => {
172
+ renderActions: _ref4 => {
176
173
  let {
177
174
  actionClassName
178
- } = _ref6;
175
+ } = _ref4;
179
176
  return /*#__PURE__*/React.createElement(FlexStack, {
180
177
  space: "xxsmall",
181
178
  className: cx(css.dropdown, actionClassName)
@@ -188,7 +185,9 @@ export const BundleModules = _ref4 => {
188
185
  handleResetFilters: resetFilters
189
186
  }));
190
187
  }
191
- }, /*#__PURE__*/React.createElement(FlexStack, null, /*#__PURE__*/React.createElement(MetricsTableSearch, {
188
+ }, /*#__PURE__*/React.createElement(FlexStack, {
189
+ space: "xxsmall"
190
+ }, /*#__PURE__*/React.createElement(MetricsTableSearch, {
192
191
  className: css.toolbarSearch,
193
192
  search: search,
194
193
  updateSearch: updateSearch,
@@ -12,6 +12,15 @@
12
12
  margin-right: -1px; /** collapse border */
13
13
  }
14
14
 
15
- .namePopover {
16
- min-width: 280px;
15
+ .nameTagDuplicated {
16
+ margin-right: var(--space-xxxsmall);
17
+ vertical-align: baseline;
18
+ }
19
+
20
+ .nameTagDuplicated::before {
21
+ content: 'D';
22
+ }
23
+
24
+ .nameText {
25
+ vertical-align: middle;
17
26
  }
@@ -7,12 +7,15 @@ import { MODULE_CHUNK, MODULE_FILTERS, MODULE_FILE_TYPE, MODULE_SOURCE_TYPE, get
7
7
  import { SORT_BY_NAME, SORT_BY_SIZE, SORT_BY_DELTA } from './bundle-modules.constants';
8
8
  export const addRowSourceFlag = row => {
9
9
  const {
10
- key
11
- } = row;
12
- const thirdParty = Boolean(key.match(MODULE_PATH_PACKAGES));
13
- return { ...row,
14
- thirdParty
15
- };
10
+ key,
11
+ runs
12
+ } = row; // @NOTE Assign instead destructuring for perf reasons
13
+ // eslint-disable-next-line no-param-reassign
14
+
15
+ row.thirdParty = Boolean(key.match(MODULE_PATH_PACKAGES)); // eslint-disable-next-line no-param-reassign
16
+
17
+ row.duplicated = Boolean(runs.find(run => (run == null ? void 0 : run.duplicated) === true));
18
+ return row;
16
19
  };
17
20
  export const getCustomSort = sortBy => item => {
18
21
  if (sortBy === SORT_BY_NAME) {
@@ -33,6 +36,11 @@ export const getRowFilter = filters => row => {
33
36
  // Skip not changed rows
34
37
  if (filters[MODULE_FILTERS.CHANGED] && !row.changed) {
35
38
  return false;
39
+ } // Skip not duplicated rows
40
+
41
+
42
+ if (filters[MODULE_FILTERS.DUPLICATED] && !row.duplicated) {
43
+ return false;
36
44
  } // Skip not matching source type
37
45
 
38
46
 
@@ -5,7 +5,7 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
5
5
 
6
6
  import React, { useMemo } from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { getModuleChunkFilters, getModuleSourceTypeFilters, getModuleFileTypeFilters } from '@bundle-stats/utils';
8
+ import { MODULE_FILTERS, getModuleChunkFilters, getModuleSourceTypeFilters, getModuleFileTypeFilters } from '@bundle-stats/utils';
9
9
  import * as webpack from '@bundle-stats/utils/lib-esm/webpack';
10
10
  import { useRowsFilter } from '../../hooks/rows-filter';
11
11
  import { useRowsSort } from '../../hooks/rows-sort';
@@ -45,13 +45,15 @@ export const BundleModules = props => {
45
45
  changed: (jobs == null ? void 0 : jobs.length) > 1,
46
46
  ...getModuleSourceTypeFilters(true),
47
47
  ...getModuleChunkFilters(chunkIds, true),
48
- ...getModuleFileTypeFilters(true)
48
+ ...getModuleFileTypeFilters(true),
49
+ [MODULE_FILTERS.DUPLICATED]: false
49
50
  },
50
51
  allEntriesFilters: {
51
52
  changed: false,
52
53
  ...getModuleSourceTypeFilters(true),
53
54
  ...getModuleChunkFilters(chunkIds, true),
54
- ...getModuleFileTypeFilters(true)
55
+ ...getModuleFileTypeFilters(true),
56
+ [MODULE_FILTERS.DUPLICATED]: false
55
57
  }
56
58
  }), [jobs]);
57
59
  const searchParams = useSearchParams({
@@ -1,6 +1,6 @@
1
1
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
2
 
3
- import React, { useCallback } from 'react';
3
+ import React, { useCallback, useMemo, useState } from 'react';
4
4
  import PropTypes from 'prop-types';
5
5
  import cx from 'classnames';
6
6
  import { PACKAGE_FILTERS, PACKAGES_SEPARATOR, PACKAGE_ID_SEPARATOR, getBundleModulesBySearch, getBundlePackagesByNameComponentLink } from '@bundle-stats/utils';
@@ -10,8 +10,9 @@ import { Stack } from '../../layout/stack';
10
10
  import { FlexStack } from '../../layout/flex-stack';
11
11
  import { EmptySet } from '../../ui/empty-set';
12
12
  import { Filters } from '../../ui/filters';
13
- import { Popover } from '../../ui/popover';
13
+ import { HoverCard } from '../../ui/hover-card';
14
14
  import { SortDropdown } from '../../ui/sort-dropdown';
15
+ import { Tag } from '../../ui/tag';
15
16
  import { Toolbar } from '../../ui/toolbar';
16
17
  import { FileName } from '../../ui/file-name';
17
18
  import { ComponentLink } from '../component-link';
@@ -32,36 +33,36 @@ const PackagePopoverContent = _ref => {
32
33
  const normalizedPackagePath = path || `node_modules/${fullName.split(PACKAGES_SEPARATOR).join('/node_modules/')}/`;
33
34
  const [normalizedName, packageId] = name.split(PACKAGE_ID_SEPARATOR);
34
35
  return /*#__PURE__*/React.createElement(Stack, {
35
- space: "xxsmall",
36
- className: css.packagePopover
36
+ space: "xsmall",
37
+ className: css.packageHoverCard
37
38
  }, /*#__PURE__*/React.createElement(Stack, {
38
39
  space: "xxxsmall"
39
40
  }, /*#__PURE__*/React.createElement("h3", {
40
- className: css.packagePopoverTitle
41
+ className: css.packageHoverCardTitle
41
42
  }, normalizedName, packageId && /*#__PURE__*/React.createElement("span", {
42
- className: css.packagePopoverTitleIndex
43
+ className: css.packageHoverCardTitleIndex
43
44
  }, `${PACKAGE_ID_SEPARATOR}${packageId}`)), /*#__PURE__*/React.createElement("p", {
44
- className: css.packagePopoverPath
45
+ className: css.packageHoverCardPath
45
46
  }, /*#__PURE__*/React.createElement(FileName, {
46
- className: css.packagePopoverPathValue,
47
+ className: css.packageHoverCardPathValue,
47
48
  name: normalizedPackagePath
48
49
  }))), /*#__PURE__*/React.createElement("ul", {
49
- className: css.packagePopoverList
50
+ className: css.packageHoverCardList
50
51
  }, /*#__PURE__*/React.createElement("li", {
51
- className: css.packagePopoverItem
52
+ className: css.packageHoverCardItem
52
53
  }, /*#__PURE__*/React.createElement("a", {
53
54
  href: `https://www.npmjs.com/package/${normalizedName}`,
54
55
  target: "_blank",
55
56
  rel: "noreferrer"
56
57
  }, "npmjs.com")), /*#__PURE__*/React.createElement("li", {
57
- className: css.packagePopoverItem
58
+ className: css.packageHoverCardItem
58
59
  }, /*#__PURE__*/React.createElement("a", {
59
60
  href: `https://bundlephobia.com/result?p=${normalizedName}`,
60
61
  target: "_blank",
61
62
  rel: "noreferrer"
62
63
  }, "bundlephobia.com"))), /*#__PURE__*/React.createElement(Stack, {
63
64
  space: "xxxsmall",
64
- className: css.packagePopover.actions
65
+ className: css.packageHoverCardActions
65
66
  }, duplicate && /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(CustomComponentLink, getBundlePackagesByNameComponentLink(normalizedName), "View all duplicate instances")), /*#__PURE__*/React.createElement(CustomComponentLink, getBundleModulesBySearch(normalizedPackagePath), "Search modules by package path")));
66
67
  };
67
68
 
@@ -76,39 +77,83 @@ PackagePopoverContent.defaultProps = {
76
77
  path: ''
77
78
  };
78
79
 
79
- const PackageRowHeader = _ref2 => {
80
+ const PackageName = _ref2 => {
80
81
  let {
81
- item,
82
+ packageName,
83
+ path,
84
+ showDuplicateFlag,
85
+ row,
82
86
  CustomComponentLink
83
87
  } = _ref2;
84
- const packageNames = item.label.split(PACKAGES_SEPARATOR);
88
+ const [showHoverCard, setHoverCard] = useState(false);
89
+ const handleOnMouseEnter = useCallback(() => setHoverCard(true), [showHoverCard]);
90
+ const label = useMemo(() => /*#__PURE__*/React.createElement(React.Fragment, null, showDuplicateFlag && row.duplicate && /*#__PURE__*/React.createElement(Tag, {
91
+ className: css.packageNameTagDuplicate,
92
+ title: "Duplicate package",
93
+ kind: Tag.KINDS.DANGER,
94
+ size: Tag.SIZES.SMALL
95
+ }), /*#__PURE__*/React.createElement("span", {
96
+ className: css.packageNameLabel
97
+ }, packageName)), [packageName, showDuplicateFlag, row]);
98
+
99
+ if (!showHoverCard) {
100
+ return /*#__PURE__*/React.createElement("span", {
101
+ className: css.packageName,
102
+ onMouseEnter: handleOnMouseEnter
103
+ }, label);
104
+ }
105
+
106
+ return /*#__PURE__*/React.createElement(HoverCard, {
107
+ className: css.packageName,
108
+ label: /*#__PURE__*/React.createElement("span", {
109
+ className: css.packageName
110
+ }, label)
111
+ }, /*#__PURE__*/React.createElement(PackagePopoverContent, {
112
+ name: packageName,
113
+ path: path,
114
+ fullName: row.label,
115
+ duplicate: row.duplicate,
116
+ CustomComponentLink: CustomComponentLink
117
+ }));
118
+ };
119
+
120
+ PackageName.propTypes = {
121
+ packageName: PropTypes.string.isRequired,
122
+ path: PropTypes.string.isRequired,
123
+ showDuplicateFlag: PropTypes.bool.isRequired,
124
+ row: PropTypes.shape({
125
+ label: PropTypes.string,
126
+ duplicate: PropTypes.bool,
127
+ runs: PropTypes.arrayOf(PropTypes.shape({
128
+ path: PropTypes.string
129
+ })).isRequired
130
+ }).isRequired,
131
+ CustomComponentLink: PropTypes.elementType.isRequired
132
+ };
133
+
134
+ const RowHeader = _ref3 => {
135
+ let {
136
+ row,
137
+ CustomComponentLink
138
+ } = _ref3;
139
+ const packageNames = row.label.split(PACKAGES_SEPARATOR);
85
140
  const {
86
141
  path
87
- } = item.runs[0] || {};
142
+ } = row.runs[0] || {};
88
143
  return /*#__PURE__*/React.createElement("span", {
89
144
  className: css.packageNames
90
- }, packageNames.map((packageName, index) => {
91
- // Render duplicate flag only for the last entry
92
- const duplicateFlag = index === packageNames.length - 1 && item.duplicate && /*#__PURE__*/React.createElement("span", {
93
- className: css.duplicate,
94
- title: "Duplicate package"
95
- }, "D");
96
- return /*#__PURE__*/React.createElement(Popover, {
97
- className: css.packageName,
98
- icon: duplicateFlag,
99
- label: packageName
100
- }, /*#__PURE__*/React.createElement(PackagePopoverContent, {
101
- name: packageName,
102
- path: path,
103
- fullName: item.label,
104
- duplicate: item.duplicate,
105
- CustomComponentLink: CustomComponentLink
106
- }));
107
- }));
145
+ }, packageNames.map((packageName, index) => /*#__PURE__*/React.createElement(PackageName, {
146
+ packageName: packageName,
147
+ path: path // Render duplicate flag only for the last entry
148
+ ,
149
+ showDuplicateFlag: index === packageNames.length - 1,
150
+ row: row,
151
+ CustomComponentLink: CustomComponentLink
152
+ })));
108
153
  };
109
154
 
110
- PackageRowHeader.propTypes = {
111
- item: PropTypes.shape({
155
+ RowHeader.propTypes = {
156
+ row: PropTypes.shape({
112
157
  label: PropTypes.string,
113
158
  duplicate: PropTypes.bool,
114
159
  runs: PropTypes.arrayOf(PropTypes.shape({
@@ -141,18 +186,18 @@ export const BundlePackages = props => {
141
186
  handleResetFilters: resetFilters,
142
187
  handleViewAll: resetAllFilters
143
188
  });
144
- const renderRowHeader = useCallback(item => /*#__PURE__*/React.createElement(PackageRowHeader, {
145
- item: item,
189
+ const renderRowHeader = useCallback(row => /*#__PURE__*/React.createElement(RowHeader, {
190
+ row: row,
146
191
  CustomComponentLink: CustomComponentLink
147
192
  }), [CustomComponentLink]);
148
193
  return /*#__PURE__*/React.createElement("section", {
149
194
  className: cx(css.root, className)
150
195
  }, /*#__PURE__*/React.createElement(Toolbar, {
151
196
  className: css.toolbar,
152
- renderActions: _ref3 => {
197
+ renderActions: _ref4 => {
153
198
  let {
154
199
  actionClassName
155
- } = _ref3;
200
+ } = _ref4;
156
201
  return /*#__PURE__*/React.createElement(FlexStack, {
157
202
  space: "xxsmall",
158
203
  className: cx(css.dropdown, actionClassName)
@@ -165,7 +210,9 @@ export const BundlePackages = props => {
165
210
  handleResetFilters: resetFilters
166
211
  }));
167
212
  }
168
- }, /*#__PURE__*/React.createElement(FlexStack, null, /*#__PURE__*/React.createElement(MetricsTableSearch, {
213
+ }, /*#__PURE__*/React.createElement(FlexStack, {
214
+ space: "xxsmall"
215
+ }, /*#__PURE__*/React.createElement(MetricsTableSearch, {
169
216
  className: css.toolbarSearch,
170
217
  placeholder: "Search by name",
171
218
  search: search,
@@ -12,23 +12,8 @@
12
12
  margin-right: -1px; /** collapse border */
13
13
  }
14
14
 
15
- .duplicate {
16
- margin-right: var(--space-xxxsmall);
17
- display: inline-block;
18
- width: var(--space-small);
19
- height: var(--space-small);
20
- font-size: 10px;
21
- font-weight: bold;
22
- line-height: var(--space-small);
23
- text-align: center;
24
- text-transform: uppercase;
25
- vertical-align: middle;
26
-
27
- background: var(--color-danger);
28
- color: var(--color-light);
29
- }
30
-
31
15
  .packageName {
16
+ display: inline-block;
32
17
  color: var(--color-text-light);
33
18
  }
34
19
 
@@ -43,22 +28,33 @@
43
28
  color: var(--color-text-ultra-light);
44
29
  }
45
30
 
46
- .packagePopover {
47
- min-width: 320px;
31
+ .packageNameTagDuplicate {
32
+ margin-right: var(--space-xxxsmall);
33
+ vertical-align: middle;
34
+ }
35
+
36
+ .packageNameLabel {
37
+ vertical-align: middle;
38
+ }
39
+
40
+ .packageNameTagDuplicate::before {
41
+ content: 'd';
48
42
  }
49
43
 
50
- .packagePopoverTitleIndex {
44
+
45
+ .packageHoverCardTitleIndex {
51
46
  color: var(--color-text-light);
52
47
  font-weight: normal;
53
48
  }
54
49
 
55
- .packagePopoverPathValue {
50
+ .packageHoverCardPathValue {
51
+ width: 100%;
56
52
  padding: var(--space-xxxsmall);
57
53
  background: var(--color-highlight-warning);
58
54
  font-family: var(--font-family-monospace);
59
55
  }
60
56
 
61
- .packagePopoverList {
57
+ .packageHoverCardList {
62
58
  padding: 0;
63
59
  list-style: inside;
64
60
  }
@@ -28,6 +28,7 @@
28
28
  }
29
29
 
30
30
  .inverted {
31
+ border-radius: var(--radius-small);
31
32
  background-color: var(--color-gray-ultra-light);
32
33
  color: var(--color-light);
33
34
  }
@@ -7,6 +7,7 @@ import { SOURCE_PATH_WEBPACK_STATS, formatDate, formatTime } from '@bundle-stats
7
7
  import { FlexStack } from '../../layout/flex-stack';
8
8
  import { Stack } from '../../layout/stack';
9
9
  import { Icon } from '../../ui/icon';
10
+ import { Tag } from '../../ui/tag';
10
11
  import css from './job-header.module.css';
11
12
  export const JobHeader = props => {
12
13
  const {
@@ -27,7 +28,7 @@ export const JobHeader = props => {
27
28
  space: "xxsmall"
28
29
  }, /*#__PURE__*/React.createElement("h1", {
29
30
  className: css.title
30
- }, /*#__PURE__*/React.createElement("span", null, `#${job.internalBuildNumber}`), tag && /*#__PURE__*/React.createElement("span", {
31
+ }, /*#__PURE__*/React.createElement("span", null, `#${job.internalBuildNumber}`), tag && /*#__PURE__*/React.createElement(Tag, {
31
32
  className: css.tag
32
33
  }, tag)), /*#__PURE__*/React.createElement(FlexStack, {
33
34
  className: css.meta,