diff --git a/package-lock.json b/package-lock.json
index d896ddc9653..6346a2298ea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -48287,7 +48287,6 @@
"mongodb-collection-model": "^5.35.3",
"mongodb-ns": "^3.0.1",
"mongodb-schema": "^12.6.3",
- "numeral": "^2.0.6",
"react": "^17.0.2",
"react-redux": "^8.1.3",
"redux": "^4.2.1",
@@ -48338,15 +48337,6 @@
"node": ">=0.3.1"
}
},
- "packages/compass-collection/node_modules/numeral": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
- "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
"packages/compass-collection/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -49156,7 +49146,6 @@
"mongodb-data-service": "^22.34.3",
"mongodb-ns": "^3.0.1",
"mongodb-query-parser": "^4.3.0",
- "numeral": "^2.0.6",
"react": "^17.0.2",
"reflux": "^0.4.1",
"semver": "^7.6.3"
@@ -49274,14 +49263,6 @@
"path-to-regexp": "^6.2.1"
}
},
- "packages/compass-crud/node_modules/numeral": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
- "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==",
- "engines": {
- "node": "*"
- }
- },
"packages/compass-crud/node_modules/path-to-regexp": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
@@ -50754,7 +50735,6 @@
"mongodb-mql-engines": "^0.0.4",
"mongodb-ns": "^3.0.1",
"mongodb-query-parser": "^4.3.0",
- "numeral": "^2.0.6",
"react": "^17.0.2",
"react-redux": "^8.1.3",
"redux": "^4.2.1",
@@ -50767,7 +50747,6 @@
"@mongodb-js/prettier-config-compass": "^1.2.9",
"@mongodb-js/testing-library-compass": "^1.3.17",
"@mongodb-js/tsconfig-compass": "^1.2.12",
- "@types/numeral": "^2.0.5",
"chai": "^4.2.0",
"depcheck": "^1.4.1",
"electron": "^37.6.1",
@@ -50819,14 +50798,6 @@
"bson": "^4.6.3 || ^5 || ^6"
}
},
- "packages/compass-indexes/node_modules/numeral": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
- "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==",
- "engines": {
- "node": "*"
- }
- },
"packages/compass-indexes/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -51433,6 +51404,7 @@
"@types/leaflet": "^1.9.8",
"@types/leaflet-draw": "^1.0.11",
"@types/mocha": "^9.0.0",
+ "@types/numeral": "^2.0.5",
"@types/react": "^17.0.5",
"@types/react-dom": "^17.0.10",
"chai": "^4.3.4",
@@ -62331,7 +62303,6 @@
"mongodb-collection-model": "^5.35.3",
"mongodb-ns": "^3.0.1",
"mongodb-schema": "^12.6.3",
- "numeral": "^2.0.6",
"nyc": "^15.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -62357,11 +62328,6 @@
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
- "numeral": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
- "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA=="
- },
"semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -63082,7 +63048,6 @@
"mongodb-instance-model": "^12.51.0",
"mongodb-ns": "^3.0.1",
"mongodb-query-parser": "^4.3.0",
- "numeral": "^2.0.6",
"nyc": "^15.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -63167,11 +63132,6 @@
"path-to-regexp": "^6.2.1"
}
},
- "numeral": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
- "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA=="
- },
"path-to-regexp": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
@@ -64060,7 +64020,6 @@
"@mongodb-js/shell-bson-parser": "^1.2.0",
"@mongodb-js/testing-library-compass": "^1.3.17",
"@mongodb-js/tsconfig-compass": "^1.2.12",
- "@types/numeral": "^2.0.5",
"bson": "^6.10.4",
"chai": "^4.2.0",
"compass-preferences-model": "^2.59.0",
@@ -64075,7 +64034,6 @@
"mongodb-mql-engines": "^0.0.4",
"mongodb-ns": "^3.0.1",
"mongodb-query-parser": "^4.3.0",
- "numeral": "^2.0.6",
"nyc": "^15.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -64115,11 +64073,6 @@
"lodash": "^4.17.21"
}
},
- "numeral": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
- "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA=="
- },
"semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -64534,6 +64487,7 @@
"@types/leaflet": "^1.9.8",
"@types/leaflet-draw": "^1.0.11",
"@types/mocha": "^9.0.0",
+ "@types/numeral": "^2.0.5",
"@types/react": "^17.0.5",
"@types/react-dom": "^17.0.10",
"bson": "^6.10.4",
diff --git a/packages/compass-collection/package.json b/packages/compass-collection/package.json
index ea5a8c795fb..a755c17a9ca 100644
--- a/packages/compass-collection/package.json
+++ b/packages/compass-collection/package.json
@@ -68,7 +68,6 @@
"mongodb-collection-model": "^5.35.3",
"mongodb-ns": "^3.0.1",
"mongodb-schema": "^12.6.3",
- "numeral": "^2.0.6",
"react": "^17.0.2",
"react-redux": "^8.1.3",
"redux": "^4.2.1",
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx
index 1a44fe569eb..f17816f34c8 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/document-count-screen.tsx
@@ -1,5 +1,6 @@
import {
Body,
+ compactBytes,
css,
palette,
spacing,
@@ -9,7 +10,6 @@ import React, { useMemo } from 'react';
import { connect } from 'react-redux';
import type { CollectionState } from '../../modules/collection-tab';
import type { SchemaAnalysisState } from '../../schema-analysis-types';
-import numeral from 'numeral';
import { DEFAULT_DOCUMENT_COUNT, MAX_DOCUMENT_COUNT } from './constants';
const BYTE_PRECISION_THRESHOLD = 1000;
@@ -40,8 +40,8 @@ const boldStyles = css({
});
const formatBytes = (bytes: number) => {
- const precision = bytes <= BYTE_PRECISION_THRESHOLD ? '0' : '0.0';
- return numeral(bytes).format(precision + 'b');
+ const decimals = bytes <= BYTE_PRECISION_THRESHOLD ? 0 : 1;
+ return compactBytes(bytes, true, decimals);
};
type ErrorState =
@@ -101,7 +101,7 @@ const DocumentCountScreen = ({
}
};
- return schemaAnalysisState.status === 'complete' ? (
+ return (
Specify Number of Documents to Generate
@@ -130,9 +130,6 @@ const DocumentCountScreen = ({
- ) : (
- // Not reachable since schema analysis must be finished before the modal can be opened
- We are analyzing your collection.
);
};
diff --git a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx
index 8a786a36197..96492f25e3f 100644
--- a/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx
+++ b/packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx
@@ -811,10 +811,10 @@ describe('MockDataGeneratorModal', () => {
);
userEvent.clear(documentCountInput);
userEvent.type(documentCountInput, '1000');
- expect(screen.getByText('100.0KB')).to.exist;
+ expect(screen.getByText('100.0 kB')).to.exist;
userEvent.clear(documentCountInput);
userEvent.type(documentCountInput, '2000');
- expect(screen.getByText('200.0KB')).to.exist;
+ expect(screen.getByText('200.0 kB')).to.exist;
});
});
diff --git a/packages/compass-components/src/index.ts b/packages/compass-components/src/index.ts
index e37b137ec16..83b5dadba23 100644
--- a/packages/compass-components/src/index.ts
+++ b/packages/compass-components/src/index.ts
@@ -218,6 +218,7 @@ export {
} from './components/links/link';
export { ChevronCollapse } from './components/chevron-collapse-icon';
export { formatDate } from './utils/format-date';
+export { compactBytes, compactNumber } from './utils/format';
export {
VirtualList,
type VirtualListRef,
diff --git a/packages/compass-components/src/utils/format.ts b/packages/compass-components/src/utils/format.ts
new file mode 100644
index 00000000000..77f8caf2ff8
--- /dev/null
+++ b/packages/compass-components/src/utils/format.ts
@@ -0,0 +1,43 @@
+/**
+ * Format bytes into a human-readable string with appropriate units.
+ *
+ * @param bytes - The number of bytes to format
+ * @param si - Use SI units (1000-based) if true, binary units (1024-based) if false
+ * @param decimals - Number of decimal places to show
+ * @returns Formatted string with units (e.g., "1.5 MB", "2.0 KiB", "-1.5 MB")
+ */
+export function compactBytes(bytes: number, si = true, decimals = 2): string {
+ const isNegative = bytes < 0;
+ const absBytes = Math.abs(bytes);
+
+ if (absBytes === 0) {
+ return '0 B';
+ }
+
+ const threshold = si ? 1000 : 1024;
+ const units = si
+ ? ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+ : ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
+ let i = Math.floor(Math.log(absBytes) / Math.log(threshold));
+ if (i >= units.length) {
+ i = units.length - 1;
+ }
+ const num = absBytes / Math.pow(threshold, i);
+ return `${isNegative ? '-' : ''}${num.toFixed(decimals)} ${units[i]}`;
+}
+
+/**
+ * Format a number into a compact notation with appropriate suffix.
+ *
+ * @param number - The number to format
+ * @returns Formatted string with compact notation (e.g., "1.5K", "2M")
+ */
+export function compactNumber(number: number): string {
+ return new Intl.NumberFormat('en', {
+ notation: 'compact',
+ })
+ .formatToParts(number)
+ .reduce((acc, part) => {
+ return `${acc}${part.value}`;
+ }, '');
+}
diff --git a/packages/compass-crud/package.json b/packages/compass-crud/package.json
index a1b3a3d7847..b5e17cfd733 100644
--- a/packages/compass-crud/package.json
+++ b/packages/compass-crud/package.json
@@ -98,7 +98,6 @@
"mongodb-data-service": "^22.34.3",
"mongodb-ns": "^3.0.1",
"mongodb-query-parser": "^4.3.0",
- "numeral": "^2.0.6",
"react": "^17.0.2",
"reflux": "^0.4.1",
"semver": "^7.6.3"
diff --git a/packages/compass-crud/src/plugin-title.tsx b/packages/compass-crud/src/plugin-title.tsx
index 8d5f8665404..91bba381fa0 100644
--- a/packages/compass-crud/src/plugin-title.tsx
+++ b/packages/compass-crud/src/plugin-title.tsx
@@ -1,6 +1,12 @@
import React, { useMemo } from 'react';
-import numeral from 'numeral';
-import { css, Tooltip, Badge, spacing } from '@mongodb-js/compass-components';
+import {
+ css,
+ Tooltip,
+ Badge,
+ spacing,
+ compactBytes,
+ compactNumber,
+} from '@mongodb-js/compass-components';
import type { CrudStore } from './stores/crud-store';
import { usePreference } from 'compass-preferences-model/provider';
@@ -22,12 +28,14 @@ const isNumber = (val: any): val is number => {
return typeof val === 'number' && !isNaN(val);
};
-const format = (value: any, format = 'a') => {
+const format = (value: any, formatType: 'number' | 'bytes' = 'number') => {
if (!isNumber(value)) {
return INVALID;
}
- const precision = value <= 1000 ? '0' : '0.0';
- return numeral(value).format(precision + format);
+ const decimals = value <= 1000 ? 0 : 1;
+ return formatType === 'bytes'
+ ? compactBytes(value, true, decimals)
+ : compactNumber(value);
};
type CollectionStatsProps = {
@@ -84,9 +92,9 @@ export const CrudTabTitle = ({
avg_document_size = NaN,
} = collectionStats ?? {};
return {
- documentCount: format(document_count),
- storageSize: format(storage_size - free_storage_size, 'b'),
- avgDocumentSize: format(avg_document_size, 'b'),
+ documentCount: format(document_count, 'number'),
+ storageSize: format(storage_size - free_storage_size, 'bytes'),
+ avgDocumentSize: format(avg_document_size, 'bytes'),
};
}, [collectionStats]);
const enableDbAndCollStats = usePreference('enableDbAndCollStats');
diff --git a/packages/compass-indexes/package.json b/packages/compass-indexes/package.json
index 868090a363f..c60cf72b5f0 100644
--- a/packages/compass-indexes/package.json
+++ b/packages/compass-indexes/package.json
@@ -53,7 +53,6 @@
"@mongodb-js/prettier-config-compass": "^1.2.9",
"@mongodb-js/testing-library-compass": "^1.3.17",
"@mongodb-js/tsconfig-compass": "^1.2.12",
- "@types/numeral": "^2.0.5",
"chai": "^4.2.0",
"depcheck": "^1.4.1",
"electron": "^37.6.1",
@@ -88,7 +87,6 @@
"mongodb-mql-engines": "^0.0.4",
"mongodb-ns": "^3.0.1",
"mongodb-query-parser": "^4.3.0",
- "numeral": "^2.0.6",
"react": "^17.0.2",
"react-redux": "^8.1.3",
"redux": "^4.2.1",
diff --git a/packages/compass-indexes/src/components/regular-indexes-table/size-field.spec.tsx b/packages/compass-indexes/src/components/regular-indexes-table/size-field.spec.tsx
index 6ed53c69927..7c5d1fd597a 100644
--- a/packages/compass-indexes/src/components/regular-indexes-table/size-field.spec.tsx
+++ b/packages/compass-indexes/src/components/regular-indexes-table/size-field.spec.tsx
@@ -17,8 +17,8 @@ describe('SizeField', function () {
describe('SizeField functions', function () {
it('formats size', function () {
expect(formatSize(908)).to.equal('908 B');
- expect(formatSize(2020)).to.equal('2.0 KB');
- expect(formatSize(202020)).to.equal('202.0 KB');
+ expect(formatSize(2020)).to.equal('2.0 kB');
+ expect(formatSize(202020)).to.equal('202.0 kB');
});
it('returns correct tooltip', function () {
diff --git a/packages/compass-indexes/src/components/regular-indexes-table/size-field.tsx b/packages/compass-indexes/src/components/regular-indexes-table/size-field.tsx
index 4038c9ab3b8..7b8f195810b 100644
--- a/packages/compass-indexes/src/components/regular-indexes-table/size-field.tsx
+++ b/packages/compass-indexes/src/components/regular-indexes-table/size-field.tsx
@@ -1,6 +1,5 @@
-import numeral from 'numeral';
import React from 'react';
-import { Body, Tooltip } from '@mongodb-js/compass-components';
+import { Body, Tooltip, compactBytes } from '@mongodb-js/compass-components';
type SizeFieldProps = {
size: number;
@@ -8,8 +7,8 @@ type SizeFieldProps = {
};
export const formatSize = (size: number) => {
- const precision = size <= 1000 ? '0' : '0.0';
- return numeral(size).format(precision + ' b');
+ const decimals = size <= 1000 ? 0 : 1;
+ return compactBytes(size, true, decimals);
};
export const getSizeTooltip = (relativeSize: number): string => {
diff --git a/packages/compass-indexes/src/plugin-title.tsx b/packages/compass-indexes/src/plugin-title.tsx
index 8cc0cd433d9..83a6e69a304 100644
--- a/packages/compass-indexes/src/plugin-title.tsx
+++ b/packages/compass-indexes/src/plugin-title.tsx
@@ -1,8 +1,14 @@
import React, { useMemo } from 'react';
import { connect } from 'react-redux';
import type { RootState } from './modules';
-import { Badge, css, spacing, Tooltip } from '@mongodb-js/compass-components';
-import numeral from 'numeral';
+import {
+ Badge,
+ css,
+ spacing,
+ Tooltip,
+ compactBytes,
+ compactNumber,
+} from '@mongodb-js/compass-components';
import { usePreference } from 'compass-preferences-model/provider';
const containerStyles = css({
@@ -30,12 +36,14 @@ const isNumber = (val: any): val is number => {
return typeof val === 'number' && !isNaN(val);
};
-const format = (value: any, format = 'a') => {
+const format = (value: any, formatType: 'number' | 'bytes' = 'number') => {
if (!isNumber(value)) {
return INVALID;
}
- const precision = value <= 1000 ? '0' : '0.0';
- return numeral(value).format(precision + format);
+ const decimals = value <= 1000 ? 0 : 1;
+ return formatType === 'bytes'
+ ? compactBytes(value, true, decimals)
+ : compactNumber(value);
};
type CollectionStatsProps = {
@@ -85,9 +93,9 @@ const TabTitle = ({
const { indexCount, totalIndexSize, avgIndexSize } = useMemo(() => {
const { index_count = NaN, index_size = NaN } = collectionStats ?? {};
return {
- indexCount: format(index_count),
- totalIndexSize: format(index_size, 'b'),
- avgIndexSize: format(avg(index_size, index_count), 'b'),
+ indexCount: format(index_count, 'number'),
+ totalIndexSize: format(index_size, 'bytes'),
+ avgIndexSize: format(avg(index_size, index_count), 'bytes'),
};
}, [collectionStats]);
diff --git a/packages/compass-schema/package.json b/packages/compass-schema/package.json
index 723ad718efd..7be3896b51a 100644
--- a/packages/compass-schema/package.json
+++ b/packages/compass-schema/package.json
@@ -58,6 +58,7 @@
"@types/leaflet": "^1.9.8",
"@types/leaflet-draw": "^1.0.11",
"@types/mocha": "^9.0.0",
+ "@types/numeral": "^2.0.5",
"@types/react": "^17.0.5",
"@types/react-dom": "^17.0.10",
"chai": "^4.3.4",
diff --git a/packages/databases-collections-list/src/collections.tsx b/packages/databases-collections-list/src/collections.tsx
index 5c8e0e199fa..4281490f1aa 100644
--- a/packages/databases-collections-list/src/collections.tsx
+++ b/packages/databases-collections-list/src/collections.tsx
@@ -1,6 +1,10 @@
import React from 'react';
-import { css, spacing } from '@mongodb-js/compass-components';
-import { compactBytes, compactNumber } from './format';
+import {
+ css,
+ spacing,
+ compactBytes,
+ compactNumber,
+} from '@mongodb-js/compass-components';
import type { BadgeProp } from './namespace-card';
import { NamespaceItemCard } from './namespace-card';
import { ItemsGrid } from './items-grid';
diff --git a/packages/databases-collections-list/src/databases.tsx b/packages/databases-collections-list/src/databases.tsx
index 8e9bad3cbdb..f9e6c2ebce1 100644
--- a/packages/databases-collections-list/src/databases.tsx
+++ b/packages/databases-collections-list/src/databases.tsx
@@ -1,7 +1,11 @@
/* eslint-disable react/prop-types */
import React from 'react';
-import { PerformanceSignals, spacing } from '@mongodb-js/compass-components';
-import { compactBytes, compactNumber } from './format';
+import {
+ PerformanceSignals,
+ spacing,
+ compactBytes,
+ compactNumber,
+} from '@mongodb-js/compass-components';
import { NamespaceItemCard } from './namespace-card';
import { ItemsGrid } from './items-grid';
import type { DatabaseProps } from 'mongodb-database-model';
diff --git a/packages/databases-collections-list/src/format.ts b/packages/databases-collections-list/src/format.ts
deleted file mode 100644
index c9c8b36ddd4..00000000000
--- a/packages/databases-collections-list/src/format.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-export function compactBytes(bytes: number, si = true, decimals = 2): string {
- const threshold = si ? 1000 : 1024;
- if (bytes === 0) {
- return `${bytes} B`;
- }
- const units = si
- ? ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
- : ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
- const i = Math.floor(Math.log(bytes) / Math.log(threshold));
- const num = bytes / Math.pow(threshold, i);
- return `${num.toFixed(decimals)} ${units[i]}`;
-}
-
-export function compactNumber(number: number): string {
- return new Intl.NumberFormat('en', {
- notation: 'compact',
- })
- .formatToParts(number)
- .reduce((acc, part) => {
- if (part.type === 'compact') {
- return `${acc} ${part.value}`;
- }
- return `${acc}${part.value}`;
- }, '');
-}