diff --git a/packages/compass-components/src/components/leafygreen.tsx b/packages/compass-components/src/components/leafygreen.tsx
index 96f8df17870..7cbf9d75001 100644
--- a/packages/compass-components/src/components/leafygreen.tsx
+++ b/packages/compass-components/src/components/leafygreen.tsx
@@ -53,8 +53,11 @@ import {
TableBody,
flexRender,
useLeafyGreenTable,
+ useLeafyGreenVirtualTable,
+ type LeafyGreenVirtualItem,
getExpandedRowModel,
getFilteredRowModel,
+ type TableProps,
} from '@leafygreen-ui/table';
import type { Row as LgTableRowType } from '@tanstack/table-core'; // TODO(COMPASS-8437): import from LG
@@ -197,6 +200,9 @@ export {
InfoSprinkle,
flexRender,
useLeafyGreenTable,
+ useLeafyGreenVirtualTable,
+ type LeafyGreenVirtualItem,
+ type TableProps,
getExpandedRowModel,
getFilteredRowModel,
type LgTableRowType,
diff --git a/packages/databases-collections-list/src/collections.tsx b/packages/databases-collections-list/src/collections.tsx
index 5c8e0e199fa..8e7ccdd7378 100644
--- a/packages/databases-collections-list/src/collections.tsx
+++ b/packages/databases-collections-list/src/collections.tsx
@@ -1,28 +1,102 @@
-import React from 'react';
-import { css, spacing } from '@mongodb-js/compass-components';
+import React, { useCallback } from 'react';
+import {
+ Badge,
+ type BadgeVariant,
+ cx,
+ css,
+ type GlyphName,
+ Icon,
+ spacing,
+ type LGColumnDef,
+ Tooltip,
+ palette,
+ useDarkMode,
+} from '@mongodb-js/compass-components';
import { compactBytes, compactNumber } from './format';
-import type { BadgeProp } from './namespace-card';
-import { NamespaceItemCard } from './namespace-card';
-import { ItemsGrid } from './items-grid';
+import { ItemsTable } from './items-table';
import type { CollectionProps } from 'mongodb-collection-model';
import { usePreference } from 'compass-preferences-model/provider';
-const COLLECTION_CARD_WIDTH = spacing[1600] * 4;
+type BadgeProp = {
+ id: string;
+ name: string;
+ variant?: BadgeVariant;
+ icon?: GlyphName;
+ hint?: React.ReactNode;
+};
+
+const collectionBadgesStyles = css({
+ display: 'flex',
+ gap: spacing[200],
+ // Preserving space for when cards with and without badges are mixed in a
+ // single row
+ minHeight: 20,
+});
+
+const CollectionBadges: React.FunctionComponent = ({ children }) => {
+ return
{children}
;
+};
+
+const collectionBadgeStyles = css({
+ gap: spacing[100],
+});
-const COLLECTION_CARD_HEIGHT = 238;
-const COLLECTION_CARD_WITHOUT_STATS_HEIGHT = COLLECTION_CARD_HEIGHT - 150;
+const viewOnStyles = css({
+ fontWeight: 'bold',
+});
+
+const viewOnLightStyles = css({
+ color: palette.white,
+});
-const COLLECTION_CARD_LIST_HEIGHT = 118;
-const COLLECTION_CARD_LIST_WITHOUT_STATS_HEIGHT =
- COLLECTION_CARD_LIST_HEIGHT - 50;
+const viewOnDarkStyles = css({
+ color: palette.black,
+});
-function collectionPropertyToBadge({
+const CollectionBadge: React.FunctionComponent = ({
id,
- options,
-}: {
- id: string;
- options?: Record;
-}): BadgeProp {
+ name,
+ icon,
+ variant,
+ hint,
+}) => {
+ const badge = useCallback(
+ ({ className, children, ...props } = {}) => {
+ return (
+
+ {icon && }
+ {name}
+ {/* Tooltip will be rendered here */}
+ {children}
+
+ );
+ },
+ [id, icon, name, variant]
+ );
+
+ if (hint) {
+ return {hint};
+ }
+
+ return badge();
+};
+
+function collectionPropertyToBadge(
+ collection: CollectionProps,
+ darkMode: boolean | undefined,
+ {
+ id,
+ options,
+ }: {
+ id: string;
+ options?: Record;
+ }
+): BadgeProp {
switch (id) {
case 'collation':
return {
@@ -43,7 +117,25 @@ function collectionPropertyToBadge({
),
};
case 'view':
- return { id, name: id, variant: 'darkgray', icon: 'Visibility' };
+ return {
+ id,
+ name: id,
+ variant: 'darkgray',
+ icon: 'Visibility',
+ hint: (
+ <>
+ Derived from{' '}
+
+ {collection.view_on}
+
+ >
+ ),
+ };
case 'capped':
return { id, name: id, variant: 'darkgray' };
case 'timeseries':
@@ -62,18 +154,118 @@ function collectionPropertyToBadge({
}
}
-const pageContainerStyles = css({
- height: 'auto',
- width: '100%',
+const collectionNameStyles = css({
display: 'flex',
- flexDirection: 'column',
+ gap: spacing[100],
+ flexWrap: 'wrap',
+ alignItems: 'anchor-center',
+ wordBreak: 'break-word',
});
+function collectionColumns(
+ darkMode: boolean | undefined,
+ enableDbAndCollStats: boolean
+): LGColumnDef[] {
+ return [
+ {
+ accessorKey: 'name',
+ header: 'Collection name',
+ enableSorting: true,
+ size: 300,
+ cell: (info) => {
+ const name = info.getValue() as string;
+
+ const badges = info.row.original.properties
+ .filter((prop) => prop.id !== 'read-only')
+ .map((prop) => {
+ return collectionPropertyToBadge(info.row.original, darkMode, prop);
+ });
+
+ return (
+
+ {name}
+
+ {badges.map((badge) => {
+ return (
+
+ );
+ })}
+
+
+ );
+ },
+ },
+ {
+ accessorKey: 'calculated_storage_size',
+ header: 'Storage size',
+ enableSorting: true,
+ cell: (info) => {
+ const type = info.row.original.type as string;
+ if (type === 'view') {
+ return '-';
+ }
+ const size = info.getValue() as number | undefined;
+ return enableDbAndCollStats && size !== undefined
+ ? compactBytes(size)
+ : '-';
+ },
+ },
+ {
+ accessorKey: 'avg_document_size',
+ header: 'Avg. document size',
+ enableSorting: true,
+ cell: (info) => {
+ const type = info.row.original.type as string;
+ if (type === 'view' || type === 'timeseries') {
+ return '-';
+ }
+
+ const size = info.getValue() as number | undefined;
+ return enableDbAndCollStats && size !== undefined
+ ? compactBytes(size)
+ : '-';
+ },
+ },
+ {
+ accessorKey: 'Indexes',
+ header: 'Indexes',
+ enableSorting: true,
+ cell: (info) => {
+ const type = info.row.original.type as string;
+ if (type === 'view' || type === 'timeseries') {
+ return '-';
+ }
+
+ const index_count = info.getValue() as number | undefined;
+ return enableDbAndCollStats && index_count !== undefined
+ ? compactNumber(index_count)
+ : '-';
+ },
+ },
+ {
+ accessorKey: 'index_size',
+ header: 'Total index size',
+ enableSorting: true,
+ cell: (info) => {
+ const type = info.row.original.type as string;
+ if (type === 'view' || type === 'timeseries') {
+ return '-';
+ }
+
+ const size = info.getValue() as number | undefined;
+ return enableDbAndCollStats && size !== undefined
+ ? compactBytes(size)
+ : '-';
+ },
+ },
+ ];
+}
+
+// TODO: we removed delete click functionality, we removed the header hint functionality
const CollectionsList: React.FunctionComponent<{
namespace: string;
collections: CollectionProps[];
onCollectionClick: (id: string) => void;
- onDeleteCollectionClick?: (id: string) => void;
onCreateCollectionClick?: () => void;
onRefreshClick?: () => void;
}> = ({
@@ -81,128 +273,24 @@ const CollectionsList: React.FunctionComponent<{
collections,
onCollectionClick,
onCreateCollectionClick,
- onDeleteCollectionClick,
onRefreshClick,
}) => {
const enableDbAndCollStats = usePreference('enableDbAndCollStats');
+ const darkMode = useDarkMode();
+ const columns = React.useMemo(
+ () => collectionColumns(darkMode, enableDbAndCollStats),
+ [darkMode, enableDbAndCollStats]
+ );
return (
-
- {
- const data =
- coll.type === 'view'
- ? [{ label: 'View on', value: coll.source?.name }]
- : coll.type === 'timeseries'
- ? [
- {
- label: 'Storage size',
- value:
- coll.calculated_storage_size !== undefined
- ? compactBytes(coll.calculated_storage_size)
- : 'N/A',
- hint:
- coll.document_size !== undefined &&
- `Uncompressed data size: ${compactBytes(
- coll.document_size
- )}`,
- },
- ]
- : [
- {
- label: 'Storage size',
- value:
- coll.calculated_storage_size !== undefined
- ? compactBytes(coll.calculated_storage_size)
- : 'N/A',
- hint:
- coll.document_size !== undefined &&
- `Uncompressed data size: ${compactBytes(
- coll.document_size
- )}`,
- },
- {
- label: 'Documents',
- value:
- coll.document_count !== undefined
- ? compactNumber(coll.document_count)
- : 'N/A',
- },
- {
- label: 'Avg. document size',
- value:
- coll.avg_document_size !== undefined
- ? compactBytes(coll.avg_document_size)
- : 'N/A',
- },
- {
- label: 'Indexes',
- value:
- coll.index_count !== undefined
- ? compactNumber(coll.index_count)
- : 'N/A',
- },
- {
- label: 'Total index size',
- value:
- coll.index_size !== undefined
- ? compactBytes(coll.index_size)
- : 'N/A',
- },
- ];
-
- const badges = coll.properties.map((prop) => {
- return collectionPropertyToBadge(prop);
- });
-
- return (
-
- );
- }}
- >
-
+
);
};
diff --git a/packages/databases-collections-list/src/databases.tsx b/packages/databases-collections-list/src/databases.tsx
index 8e9bad3cbdb..69df736f198 100644
--- a/packages/databases-collections-list/src/databases.tsx
+++ b/packages/databases-collections-list/src/databases.tsx
@@ -1,24 +1,70 @@
/* eslint-disable react/prop-types */
import React from 'react';
-import { PerformanceSignals, spacing } from '@mongodb-js/compass-components';
+// TODO: don't forget about performance insights?
+//import { PerformanceSignals, spacing } from '@mongodb-js/compass-components';
import { compactBytes, compactNumber } from './format';
-import { NamespaceItemCard } from './namespace-card';
-import { ItemsGrid } from './items-grid';
+import { ItemsTable } from './items-table';
import type { DatabaseProps } from 'mongodb-database-model';
import { usePreference } from 'compass-preferences-model/provider';
+import { css, type LGColumnDef } from '@mongodb-js/compass-components';
-const DATABASE_CARD_WIDTH = spacing[1600] * 4;
+const databaseNameStyles = css({
+ wordBreak: 'break-word',
+});
-const DATABASE_CARD_HEIGHT = 154;
-const DATABASE_CARD_WITHOUT_STATS_HEIGHT = DATABASE_CARD_HEIGHT - 85;
-
-const DATABASE_CARD_LIST_HEIGHT = 118;
-const DATABASE_CARD_LIST_WITHOUT_STATS_HEIGHT = DATABASE_CARD_LIST_HEIGHT - 50;
+function databaseColumns(
+ enableDbAndCollStats: boolean
+): LGColumnDef[] {
+ return [
+ {
+ accessorKey: 'name',
+ header: 'Database name',
+ enableSorting: true,
+ cell: (info) => {
+ const name = info.getValue() as string;
+ return {name};
+ },
+ },
+ {
+ accessorKey: 'calculated_storage_size',
+ header: 'Storage size',
+ enableSorting: true,
+ cell: (info) => {
+ // TODO: shouldn't this just have the right type rather than unknown?
+ const size = info.getValue() as number | undefined;
+ return enableDbAndCollStats && size !== undefined
+ ? compactBytes(size)
+ : '-';
+ },
+ },
+ {
+ accessorKey: 'collectionsLength',
+ header: 'Collections',
+ enableSorting: true,
+ cell: (info) => {
+ return enableDbAndCollStats
+ ? compactNumber(info.getValue() as number)
+ : '-';
+ },
+ },
+ {
+ accessorKey: 'index_count',
+ header: 'Indexes',
+ enableSorting: true,
+ cell: (info) => {
+ const index_count = info.getValue() as number | undefined;
+ return enableDbAndCollStats && index_count !== undefined
+ ? compactNumber(index_count)
+ : '-';
+ },
+ },
+ ];
+}
+// TODO: we removed delete click functionality, we removed the header hint functionality
const DatabasesList: React.FunctionComponent<{
databases: DatabaseProps[];
onDatabaseClick: (id: string) => void;
- onDeleteDatabaseClick?: (id: string) => void;
onCreateDatabaseClick?: () => void;
onRefreshClick?: () => void;
renderLoadSampleDataBanner?: () => React.ReactNode;
@@ -26,90 +72,24 @@ const DatabasesList: React.FunctionComponent<{
databases,
onDatabaseClick,
onCreateDatabaseClick,
- onDeleteDatabaseClick,
onRefreshClick,
renderLoadSampleDataBanner,
}) => {
const enableDbAndCollStats = usePreference('enableDbAndCollStats');
+ const columns = React.useMemo(
+ () => databaseColumns(enableDbAndCollStats),
+ [enableDbAndCollStats]
+ );
return (
- {
- return (
- = 10_000
- ? PerformanceSignals.get('too-many-collections')
- : undefined,
- },
- {
- label: 'Indexes',
- value:
- enableDbAndCollStats && db.index_count !== undefined
- ? compactNumber(db.index_count)
- : 'N/A',
- },
- ]}
- onItemClick={onItemClick}
- onItemDeleteClick={onDeleteItemClick}
- {...props}
- >
- );
- }}
renderLoadSampleDataBanner={renderLoadSampleDataBanner}
- >
+ >
);
};
diff --git a/packages/databases-collections-list/src/items-grid.tsx b/packages/databases-collections-list/src/items-table.tsx
similarity index 58%
rename from packages/databases-collections-list/src/items-grid.tsx
rename to packages/databases-collections-list/src/items-table.tsx
index fa1300cfed1..62d0263895d 100644
--- a/packages/databases-collections-list/src/items-grid.tsx
+++ b/packages/databases-collections-list/src/items-table.tsx
@@ -1,92 +1,48 @@
-import React, { useCallback, useMemo } from 'react';
+import React, { Fragment, useMemo } from 'react';
import {
css,
cx,
spacing,
- VirtualGrid,
- useSortControls,
- useSortedItems,
WorkspaceContainer,
Button,
Icon,
Breadcrumbs,
+ Table,
+ TableHead,
+ TableBody,
+ useLeafyGreenVirtualTable,
+ type LGColumnDef,
+ type HeaderGroup,
+ HeaderRow,
+ HeaderCell,
+ flexRender,
+ ExpandedContent,
+ Row,
+ Cell,
+ //type LeafyGreenTableRow,
+ type LeafyGreenVirtualItem,
} from '@mongodb-js/compass-components';
import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';
-import type { NamespaceItemCardProps } from './namespace-card';
-import { useViewTypeControls } from './use-view-type';
-import type { ViewType } from './use-view-type';
import { useConnectionInfo } from '@mongodb-js/compass-connections/provider';
import toNS from 'mongodb-ns';
import { getConnectionTitle } from '@mongodb-js/connection-info';
import { useOpenWorkspace } from '@mongodb-js/compass-workspaces/provider';
-import { useConnectionInfoRef } from '@mongodb-js/compass-connections/provider';
import { usePreferences } from 'compass-preferences-model/provider';
type Item = { _id: string } & Record;
-const rowStyles = css({
- paddingLeft: spacing[400],
- paddingRight: spacing[400],
- paddingBottom: spacing[100],
- paddingTop: spacing[100],
- columnGap: spacing[200],
-});
-
-const containerStyles = css({
- width: '100%',
- height: '100%',
- overflow: 'hidden',
- display: 'grid',
- gridTemplateRows: 'auto 1fr',
- gridTemplateColumns: '100%',
- // This element is focusable only to handle virtual list and will immediately
- // pass focus to its children. This can take a frame though so to avoid
- // outline on the container showing up, we are completely disabling it
- outline: 'none',
-});
-
-const gridStyles = {
- container: containerStyles,
- row: rowStyles,
-};
-
export const createButtonStyles = css({
whiteSpace: 'nowrap',
});
-type CallbackProps = {
- onItemClick: (id: string) => void;
- onCreateItemClick?: () => void;
- onDeleteItemClick?: (id: string) => void;
-};
-
-interface RenderItem {
- (
- props: {
- item: T;
- viewType: ViewType;
- } & Omit &
- Omit<
- React.HTMLProps,
- Extract
- >
- ): React.ReactElement;
-}
-
type ItemsGridProps = {
namespace?: string;
itemType: 'collection' | 'database';
- itemGridWidth: number;
- itemGridHeight: number;
- itemListWidth?: number;
- itemListHeight?: number;
+ columns: LGColumnDef[];
items: T[];
- sortBy?: { name: Extract; label: string }[];
onItemClick: (id: string) => void;
- onDeleteItemClick?: (id: string) => void;
onCreateItemClick?: () => void;
onRefreshClick?: () => void;
- renderItem: RenderItem;
renderLoadSampleDataBanner?: () => React.ReactNode;
};
@@ -141,19 +97,15 @@ function buildChartsUrl(
return url.toString();
}
-const GridControls: React.FunctionComponent<{
+const TableControls: React.FunctionComponent<{
namespace?: string;
itemType: string;
- sortControls?: React.ReactNode;
- viewTypeControls?: React.ReactNode;
onCreateItemClick?: () => void;
onRefreshClick?: () => void;
renderLoadSampleDataBanner?: () => React.ReactNode;
}> = ({
namespace,
itemType,
- sortControls,
- viewTypeControls,
onCreateItemClick,
onRefreshClick,
renderLoadSampleDataBanner,
@@ -274,14 +226,6 @@ const GridControls: React.FunctionComponent<{
)}
- {sortControls && viewTypeControls && (
-
-
{sortControls}
-
- {viewTypeControls}
-
-
- )}
{banner && {banner}
}
);
@@ -292,95 +236,106 @@ const itemsGridContainerStyles = css({
height: '100%',
});
-export const ItemsGrid = ({
+const virtualScrollingContainerHeight = css({
+ height: 'calc(100vh - 100px)',
+ padding: `0 ${spacing[400]}px`,
+});
+
+export const ItemsTable = ({
namespace,
itemType,
- itemGridWidth,
- itemGridHeight,
- itemListWidth = itemGridWidth,
- itemListHeight = itemGridHeight,
+ columns,
items,
- sortBy = [],
onItemClick,
- onDeleteItemClick,
onCreateItemClick,
onRefreshClick,
- renderItem: _renderItem,
renderLoadSampleDataBanner,
}: ItemsGridProps): React.ReactElement => {
- const track = useTelemetry();
- const connectionInfoRef = useConnectionInfoRef();
- const onViewTypeChange = useCallback(
- (newType: ViewType) => {
- track(
- 'Switch View Type',
- { view_type: newType, item_type: itemType },
- connectionInfoRef.current
- );
+ const tableContainerRef = React.useRef(null);
+
+ const table = useLeafyGreenVirtualTable({
+ containerRef: tableContainerRef,
+ data: items,
+ columns,
+ virtualizerOptions: {
+ estimateSize: () => 50,
+ overscan: 10,
},
- [itemType, track, connectionInfoRef]
- );
-
- const [sortControls, sortState] = useSortControls(sortBy);
- const [viewTypeControls, viewType] = useViewTypeControls({
- onChange: onViewTypeChange,
});
- const sortedItems = useSortedItems(items, sortState);
-
- const itemWidth = viewType === 'grid' ? itemGridWidth : itemListWidth;
- const itemHeight = viewType === 'grid' ? itemGridHeight : itemListHeight;
-
- const shouldShowControls = items.length > 0;
-
- const renderItem: React.ComponentProps['renderItem'] =
- useCallback(
- ({ index, ...props }) => {
- const item = sortedItems[index];
- return _renderItem({
- item,
- viewType,
- onItemClick,
- onDeleteItemClick,
- ...props,
- });
- },
- [_renderItem, onDeleteItemClick, onItemClick, sortedItems, viewType]
- );
return (
+ >
}
>
- {(scrollTriggerRef) => {
- return (
- {
- return ;
- }}
- headerHeight={0}
- itemKey={(index: number) => sortedItems[index]._id}
- classNames={gridStyles}
- resetActiveItemOnBlur={false}
- data-testid={`${itemType}-grid`}
- >
- );
- }}
+
+
+ {table.getHeaderGroups().map((headerGroup: HeaderGroup) => (
+
+ {headerGroup.headers.map((header) => {
+ return (
+
+ {flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ );
+ })}
+
+ ))}
+
+
+ {table.virtual.getVirtualItems() &&
+ table.virtual
+ .getVirtualItems()
+ .map((virtualRow: LeafyGreenVirtualItem) => {
+ const row = virtualRow.row;
+ const isExpandedContent = row.isExpandedContent ?? false;
+
+ return (
+
+ {!isExpandedContent && (
+ // row is required
+
+ onItemClick(row.original._id as string)
+ }
+ >
+ {row.getVisibleCells().map((cell: any) => {
+ return (
+ // cell is required
+
+ {flexRender(
+ cell.column.columnDef.cell,
+ cell.getContext()
+ )}
+ |
+ );
+ })}
+
+ )}
+ {isExpandedContent && }
+
+ );
+ })}
+
+
);
diff --git a/packages/databases-collections-list/src/namespace-card.tsx b/packages/databases-collections-list/src/namespace-card.tsx
deleted file mode 100644
index f2be5888adc..00000000000
--- a/packages/databases-collections-list/src/namespace-card.tsx
+++ /dev/null
@@ -1,359 +0,0 @@
-/* eslint-disable react/prop-types */
-import React, { useCallback, useMemo } from 'react';
-import {
- Card,
- css,
- Icon,
- spacing,
- Subtitle,
- useHoverState,
- Badge,
- Tooltip,
- cx,
- useFocusState,
- FocusState,
- palette,
- mergeProps,
- useDefaultAction,
- ItemActionControls,
- useDarkMode,
-} from '@mongodb-js/compass-components';
-import type {
- BadgeVariant,
- GlyphName,
- ItemAction,
- SignalPopover,
-} from '@mongodb-js/compass-components';
-import { NamespaceParam } from './namespace-param';
-import type { ViewType } from './use-view-type';
-import { usePreferences } from 'compass-preferences-model/provider';
-
-const cardTitleGroup = css({
- display: 'flex',
- alignItems: 'center',
- gap: spacing[400],
-});
-
-const CardTitleGroup: React.FunctionComponent = ({ children }) => {
- return {children}
;
-};
-
-const inferredFromPrivilegesLightStyles = css({
- color: palette.gray.dark1,
-});
-
-const inferredFromPrivilegesDarkStyles = css({
- color: palette.gray.base,
-});
-
-const inactiveCardStyles = css({
- borderStyle: 'dashed',
- borderWidth: spacing[50],
- '&:hover': {
- borderStyle: 'dashed',
- borderWidth: spacing[50],
- },
-});
-
-const tooltipTriggerStyles = css({
- display: 'flex',
-});
-
-const cardNameWrapper = css({
- // Workaround for uncollapsible text in flex children
- minWidth: 0,
-});
-
-const cardNameDark = css({
- color: palette.green.light2,
-});
-
-const cardNameLight = css({
- color: palette.green.dark2,
-});
-
-const cardName = css({
- overflow: 'hidden',
- whiteSpace: 'nowrap',
- textOverflow: 'ellipsis',
- // To make container 28px to match leafygreen buttons
- paddingTop: 2,
- paddingBottom: 2,
- // TS is very confused if fontWeight is not a number even though it's a valid
- // CSS value
- fontWeight: '600 !important' as unknown as number,
-});
-
-const CardName: React.FunctionComponent<{
- children: string;
- inferredFromPrivileges: boolean;
-}> = ({ children, inferredFromPrivileges }) => {
- const darkMode = useDarkMode();
- return (
-
-
- {children}
-
-
- );
-};
-
-const cardActionContainer = css({
- marginLeft: 'auto',
- flex: 'none',
-});
-
-const cardBadges = css({
- display: 'flex',
- gap: spacing[200],
- // Preserving space for when cards with and without badges are mixed in a
- // single row
- minHeight: 20,
-});
-
-const CardBadges: React.FunctionComponent = ({ children }) => {
- return {children}
;
-};
-
-const cardBadge = css({
- gap: spacing[100],
-});
-
-const cardBadgeLabel = css({});
-
-export type BadgeProp = {
- id: string;
- name: string;
- variant?: BadgeVariant;
- icon?: GlyphName;
- hint?: React.ReactNode;
-};
-
-const CardBadge: React.FunctionComponent = ({
- id,
- name,
- icon,
- variant,
- hint,
-}) => {
- const badge = useCallback(
- ({ className, children, ...props } = {}) => {
- return (
-
- {icon && }
- {name}
- {/* Tooltip will be rendered here */}
- {children}
-
- );
- },
- [id, icon, name, variant]
- );
-
- if (hint) {
- return {hint};
- }
-
- return badge();
-};
-
-const card = css({
- padding: spacing[400],
-});
-
-export type DataProp = {
- label: React.ReactNode;
- value: React.ReactNode;
- hint?: React.ReactNode;
- insights?: React.ComponentProps['signals'];
-};
-
-export type NamespaceItemCardProps = {
- id: string;
- type: 'database' | 'collection';
- viewType: ViewType;
- name: string;
- status: 'initial' | 'fetching' | 'refreshing' | 'ready' | 'error';
- data: DataProp[];
- badges?: BadgeProp[] | null;
- inferredFromPrivileges: boolean;
- onItemClick: (id: string) => void;
- onItemDeleteClick?: (id: string) => void;
-};
-
-const namespaceDataGroup = css({
- display: 'flex',
- gap: spacing[200],
- marginTop: spacing[400],
-});
-
-const column = css({
- flexDirection: 'column',
-});
-
-type NamespaceAction = 'delete';
-
-export const NamespaceItemCard: React.FunctionComponent<
- NamespaceItemCardProps &
- Omit<
- React.HTMLProps,
- Extract
- >
-> = ({
- id,
- type,
- name,
- status,
- data,
- onItemClick,
- onItemDeleteClick,
- badges = null,
- viewType,
- inferredFromPrivileges,
- ...props
-}) => {
- const { readOnly, enableDbAndCollStats } = usePreferences([
- 'readOnly',
- 'enableDbAndCollStats',
- ]);
- const darkMode = useDarkMode();
- const [hoverProps, isHovered] = useHoverState();
- const [focusProps, focusState] = useFocusState();
-
- const onDefaultAction = useCallback(() => {
- onItemClick(id);
- }, [onItemClick, id]);
-
- const hasDeleteHandler = !!onItemDeleteClick;
- const cardActions: ItemAction[] = useMemo(() => {
- return readOnly || !hasDeleteHandler || inferredFromPrivileges
- ? []
- : [
- {
- action: 'delete',
- label: `Delete ${type}`,
- icon: 'Trash',
- },
- ];
- }, [type, readOnly, inferredFromPrivileges, hasDeleteHandler]);
-
- const defaultActionProps = useDefaultAction(onDefaultAction);
-
- const onAction = useCallback(
- (action: NamespaceAction) => {
- if (action === 'delete') {
- onItemDeleteClick?.(id);
- }
- },
- [onItemDeleteClick, id]
- );
-
- const badgesGroup = badges && (
-
- {badges.map((badge) => {
- return ;
- })}
-
- );
-
- const cardProps = mergeProps(
- {
- className: cx(
- card,
- inferredFromPrivileges && [
- !darkMode && inferredFromPrivilegesLightStyles,
- darkMode && inferredFromPrivilegesDarkStyles,
- inactiveCardStyles,
- ]
- ),
- },
- defaultActionProps,
- hoverProps,
- focusProps,
- props
- );
-
- const isButtonVisible =
- [FocusState.FocusVisible, FocusState.FocusWithinVisible].includes(
- focusState
- ) || isHovered;
-
- return (
- // @ts-expect-error the error here is caused by passing children to Card
- // component, even though it's allowed on the implementation level the types
- // are super confused and don't allow that
-
-
-
- {name}
-
-
- {inferredFromPrivileges && (
-
-
-
- }
- >
- Your privileges grant you access to this namespace, but it might not
- currently exist
-
- )}
-
- {viewType === 'list' && badgesGroup}
-
- 0}
- actions={cardActions}
- onAction={onAction}
- className={cardActionContainer}
- >
-
-
- {viewType === 'grid' && badgesGroup}
-
- {enableDbAndCollStats && (
-
- {data.map(({ label, value, hint, insights }, idx) => {
- return (
-
- );
- })}
-
- )}
-
- );
-};
diff --git a/packages/databases-collections-list/src/namespace-param.tsx b/packages/databases-collections-list/src/namespace-param.tsx
deleted file mode 100644
index fbbeaf188f6..00000000000
--- a/packages/databases-collections-list/src/namespace-param.tsx
+++ /dev/null
@@ -1,175 +0,0 @@
-/* eslint-disable react/prop-types */
-import React, { useMemo } from 'react';
-import {
- InlineDefinition,
- spacing,
- css,
- cx,
- ContentWithFallback,
- Placeholder,
- keyframes,
- SignalPopover,
-} from '@mongodb-js/compass-components';
-import type { ViewType } from './use-view-type';
-import { usePreference } from 'compass-preferences-model/provider';
-
-const namespaceParam = css({
- display: 'flex',
- gap: '1ch',
- flex: 1,
- overflow: 'hidden',
- whiteSpace: 'nowrap',
- textOverflow: 'ellipsis',
- minWidth: 0,
- maxWidth: spacing[1600] * 4,
-});
-
-const multiline = css({
- display: 'flex',
- flexDirection: 'column',
- gap: 0,
-});
-
-const namespaceParamLabel = css({
- fontWeight: 'bold',
-});
-
-const namespaceParamValueContainer = css({
- position: 'relative',
- width: '100%',
- // Keeping container height for the placeholder to appear
- minHeight: 20,
-});
-
-const namespaceParamValueContainerWithInsights = css({
- display: 'flex',
- alignItems: 'center',
- gap: spacing[200],
-});
-
-const namespaceParamValue = css({
- opacity: 1,
- transition: 'opacity .16s linear',
-});
-
-const namespaceParamValueRefreshing = css({
- opacity: 0.3,
-});
-
-const namespaceParamValueMissing = css({
- opacity: 0.3,
-});
-
-const namespaceParamValuePlaceholder = css({
- position: 'absolute',
- top: 0,
- left: 0,
- bottom: 0,
- right: 0,
- display: 'flex',
- opacity: 0,
- transition: 'opacity .16s ease-out',
-});
-
-const visible = css({
- opacity: 1,
- transitionTimingFunction: 'ease-in',
-});
-
-const fadeInAnimation = keyframes({
- from: {
- opacity: 0,
- },
- to: {
- opacity: 1,
- },
-});
-
-const fadeIn = css({
- animation: `${fadeInAnimation} .16s ease-out`,
-});
-
-export const NamespaceParam: React.FunctionComponent<{
- label: React.ReactNode;
- value: React.ReactNode;
- status: 'initial' | 'fetching' | 'refreshing' | 'ready' | 'error';
- hint?: React.ReactNode;
- viewType: ViewType;
- insights?: React.ComponentProps['signals'];
-}> = ({ label, value, status, hint, viewType, insights }) => {
- const showInsights = usePreference('showInsights');
-
- const renderedValue = useMemo(() => {
- const isReady = status !== 'initial' && status !== 'fetching';
- return (
- {
- if (!shouldRender) {
- return null;
- }
-
- // eslint-disable-next-line eqeqeq
- const missingValue = value == null || status === 'error';
-
- return (
-
- {missingValue ? '—' : value}
-
- );
- }}
- fallback={(shouldRender) => (
-
-
-
- )}
- >
- );
- }, [value, status]);
-
- return (
-
-
- {hint ? (
-
- {label}:
-
- ) : (
- <>{label}:>
- )}
-
-
- {renderedValue}
- {showInsights && insights && (
-
- )}
-
-
- );
-};
diff --git a/packages/databases-collections-list/src/use-view-type.tsx b/packages/databases-collections-list/src/use-view-type.tsx
deleted file mode 100644
index 765e9c7e9af..00000000000
--- a/packages/databases-collections-list/src/use-view-type.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
-import {
- SegmentedControl,
- SegmentedControlOption,
- Icon,
- Label,
- css,
- spacing,
- useId,
-} from '@mongodb-js/compass-components';
-
-export type ViewType = 'grid' | 'list';
-
-const VIEW_TYPE_SETTINGS_KEY = 'compass_items_grid_view_type';
-
-function getViewTypeSettingsFromSessionStorage(
- defaultType: ViewType = 'grid'
-): ViewType {
- try {
- return (
- (window.sessionStorage.getItem(VIEW_TYPE_SETTINGS_KEY) as ViewType) ??
- defaultType
- );
- } catch {
- return defaultType;
- }
-}
-
-function setViewTypeSettingsFromSessionStorage(val: ViewType) {
- try {
- window.sessionStorage.setItem(VIEW_TYPE_SETTINGS_KEY, val);
- } catch {
- // noop
- }
-}
-
-const controlsContainer = css({
- display: 'flex',
- alignItems: 'center',
- gap: spacing[200],
-});
-
-const label = css({
- // Because leafygreen
- margin: '0 !important',
- padding: '0 !important',
-});
-
-export function useViewTypeControls({
- defaultViewType = 'list',
- onChange = () => {
- // noop
- },
-}: {
- defaultViewType?: ViewType;
- onChange?: (newType: ViewType) => void;
-}): [React.ReactElement, ViewType] {
- const [viewType, setViewType] = useState(() =>
- getViewTypeSettingsFromSessionStorage(defaultViewType)
- );
- useEffect(() => {
- setViewTypeSettingsFromSessionStorage(viewType);
- }, [viewType]);
- const onViewTypeChange = useCallback(
- (val: ViewType) => {
- onChange(val);
- setViewType(val);
- },
- [onChange]
- );
- const labelId = useId();
- const controlId = useId();
- const viewControls = useMemo(() => {
- return (
-
-
- void}
- >
- }
- />
- }
- />
-
-
- );
- }, [labelId, controlId, viewType, onViewTypeChange]);
- return [viewControls, viewType];
-}