import { cx } from '@emotion/css'; import { Checkbox, Icon, IconButton, LoadingPlaceholder, useStyles2, useTheme2, FadeTransition } from '@grafana/ui'; import React, { useCallback, useEffect, useState } from 'react'; import { Space } from '../Space'; import getStyles from './styles'; import { ResourceRowType, ResourceRow, ResourceRowGroup } from './types'; import { findRow } from './utils'; interface NestedRowsProps { rows: ResourceRowGroup; level: number; selectedRows: ResourceRowGroup; requestNestedRows: (row: ResourceRow) => Promise; onRowSelectedChange: (row: ResourceRow, selected: boolean) => void; } const NestedRows: React.FC = ({ rows, selectedRows, level, requestNestedRows, onRowSelectedChange, }) => ( <> {rows.map((row) => ( ))} ); interface NestedRowProps { row: ResourceRow; level: number; selectedRows: ResourceRowGroup; requestNestedRows: (row: ResourceRow) => Promise; onRowSelectedChange: (row: ResourceRow, selected: boolean) => void; } const NestedRow: React.FC = ({ row, selectedRows, level, requestNestedRows, onRowSelectedChange }) => { const styles = useStyles2(getStyles); const initialOpenStatus = row.type === ResourceRowType.Subscription ? 'open' : 'closed'; const [rowStatus, setRowStatus] = useState<'open' | 'closed' | 'loading'>(initialOpenStatus); const isSelected = !!selectedRows.find((v) => v.id === row.id); const isDisabled = selectedRows.length > 0 && !isSelected; const isOpen = rowStatus === 'open'; const onRowToggleCollapse = async () => { if (rowStatus === 'open') { setRowStatus('closed'); return; } setRowStatus('loading'); await requestNestedRows(row); setRowStatus('open'); }; // opens the resource group on load of component if there was a previously saved selection useEffect(() => { // Assuming we don't have multi-select yet const selectedRow = selectedRows[0]; const containsChild = selectedRow && !!findRow(row.children ?? [], selectedRow.id); if (containsChild) { setRowStatus('open'); } }, [selectedRows, row]); return ( <> {row.typeLabel} {row.location ?? '-'} {isOpen && row.children && Object.keys(row.children).length > 0 && ( )} ); }; interface EntryIconProps { entry: ResourceRow; isOpen: boolean; } const EntryIcon: React.FC = ({ isOpen, entry: { type } }) => { switch (type) { case ResourceRowType.Subscription: return ; case ResourceRowType.ResourceGroup: return ; case ResourceRowType.Resource: return ; case ResourceRowType.VariableGroup: return ; case ResourceRowType.Variable: return ; default: return null; } }; interface NestedEntryProps { level: number; entry: ResourceRow; isSelected: boolean; isOpen: boolean; isDisabled: boolean; onToggleCollapse: (row: ResourceRow) => void; onSelectedChange: (row: ResourceRow, selected: boolean) => void; } const NestedEntry: React.FC = ({ entry, isSelected, isDisabled, isOpen, level, onToggleCollapse, onSelectedChange, }) => { const theme = useTheme2(); const styles = useStyles2(getStyles); const hasChildren = !!entry.children; // Subscriptions, resource groups, resources, and variables are all selectable, so // the top-level variable group is the only thing that cannot be selected. const isSelectable = entry.type !== ResourceRowType.VariableGroup; const handleToggleCollapse = useCallback(() => { onToggleCollapse(entry); }, [onToggleCollapse, entry]); const handleSelectedChanged = useCallback( (ev: React.ChangeEvent) => { const isSelected = ev.target.checked; onSelectedChange(entry, isSelected); }, [entry, onSelectedChange] ); const checkboxId = `checkbox_${entry.id}`; return (
{/* When groups are selectable, I *think* we will want to show a 2-wide space instead of the collapse button for leaf rows that have no children to get them to align */} {hasChildren ? ( ) : ( )} {isSelectable && ( <> )}
); }; export default NestedRows;