diff --git a/src/components/ActionDataSamplesCard.tsx b/src/components/ActionDataSamplesCard.tsx index fb6d74e65..d92ec41a2 100644 --- a/src/components/ActionDataSamplesCard.tsx +++ b/src/components/ActionDataSamplesCard.tsx @@ -21,7 +21,7 @@ import { Text, VStack, } from "@chakra-ui/react"; -import { ReactNode, useCallback } from "react"; +import { forwardRef, ReactNode, useCallback, useRef } from "react"; import { RiHashtag, RiTimerLine } from "react-icons/ri"; import { FormattedMessage, useIntl } from "react-intl"; import { DataSamplesView, ActionData, RecordingData } from "../model"; @@ -81,32 +81,33 @@ const ActionDataSamplesCard = ({ )} {value.recordings.map((recording, idx) => ( - deleteActionRecording(value.id, recording.id)} + /> + } > - deleteActionRecording(value.id, recording.id)} - /> - + ))} ); @@ -163,37 +164,58 @@ const ActionDataSamplesCard = ({ ); }; -interface DataSamplesRowCardProps extends CardProps { - selected: boolean; - onSelectRow?: () => void; +interface GraphAndDataFeaturesDataSampleCardProps + extends DataSamplesRowCardProps { children: ReactNode; + closeButton: ReactNode; } -const DataSamplesRowCard = ({ - selected, - onSelectRow, +const GraphAndDataFeaturesDataSampleCard = ({ children, - ...rest -}: DataSamplesRowCardProps) => { + closeButton, + ...props +}: GraphAndDataFeaturesDataSampleCardProps) => { + const ref = useRef(null); return ( - - - {children} - - + + {closeButton} + {children} + ); }; +interface DataSamplesRowCardProps extends CardProps { + selected: boolean; + onSelectRow?: () => void; + children: ReactNode; +} + +const DataSamplesRowCard = forwardRef( + function DataSamplesRowCard( + { selected, onSelectRow, children, ...rest }: DataSamplesRowCardProps, + ref + ) { + return ( + + + {children} + + + ); + } +); + interface RecordingAreaProps extends BoxProps { action: ActionData; selected: boolean; @@ -328,6 +350,7 @@ const DataSample = ({ view: DataSamplesView; hasClose?: boolean; }) => { + const ref = useRef(null); const hasGraph = view === DataSamplesView.Graph || view === DataSamplesView.GraphAndDataFeatures; @@ -339,26 +362,27 @@ const DataSample = ({ onDelete(actionId, recording.id); }, [actionId, onDelete, recording.id]); return ( - + {hasClose && ( - + + + )} {hasGraph && ( ; } -const CodeViewCard = ({ project, parentRef }: CodeViewCardProps) => { +const CodeViewCard = ({ project, parentRef, ...props }: CodeViewCardProps) => { // This is used to set the tour cutout as the card can be taller than // the parent in a scrollable area. const [observableHeight, setObservableHeight] = useState( @@ -54,7 +54,6 @@ const CodeViewCard = ({ project, parentRef }: CodeViewCardProps) => { alignSelf="start" display="flex" flexDirection="column" - py={2} h="full" w="full" borderColor="brand.500" @@ -67,6 +66,7 @@ const CodeViewCard = ({ project, parentRef }: CodeViewCardProps) => { p={5} objectFit="contain" position="relative" + {...props} > { return ( diff --git a/src/components/DataSamplesTable.tsx b/src/components/DataSamplesTable.tsx index 2fc95fc40..8eee04edb 100644 --- a/src/components/DataSamplesTable.tsx +++ b/src/components/DataSamplesTable.tsx @@ -36,15 +36,12 @@ const gridCommonProps: Partial = { }; const headings: GridColumnHeadingItemProps[] = [ - { - titleId: "action-label", - descriptionId: "action-tooltip", - }, + { titleId: "action-label", descriptionId: "action-tooltip" }, { titleId: "data-samples-label", descriptionId: "data-samples-tooltip", itemsRight: ( - + @@ -245,10 +242,9 @@ const DataSamplesTable = ({ )} 0 ? `${gridPadding}px` : 2} alignItems="start" autoRows="max-content" - overflow="auto" flexGrow={1} h={0} ref={(node) => { diff --git a/src/components/HeadingGrid.tsx b/src/components/HeadingGrid.tsx index 8e5d6d092..e348b0600 100644 --- a/src/components/HeadingGrid.tsx +++ b/src/components/HeadingGrid.tsx @@ -11,15 +11,18 @@ import { ReactNode } from "react"; interface HeadingGridProps extends GridProps { headings: GridColumnHeadingItemProps[]; } - const HeadingGrid = ({ headings, ...props }: HeadingGridProps) => { return ( {headings.map((props, idx) => ( diff --git a/src/components/TestingModelTable.tsx b/src/components/TestingModelTable.tsx index e8bc39838..a96981df4 100644 --- a/src/components/TestingModelTable.tsx +++ b/src/components/TestingModelTable.tsx @@ -6,15 +6,15 @@ */ import { Box, + Flex, Grid, GridItem, GridProps, - HStack, Icon, VStack, } from "@chakra-ui/react"; import { MakeCodeRenderBlocksProvider } from "@microbit/makecode-embed/react"; -import { useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { RiArrowRightLine } from "react-icons/ri"; import { useIntl } from "react-intl"; import { useDataConnected } from "../data-connection-flow"; @@ -28,9 +28,12 @@ import CodeViewCard from "./CodeViewCard"; import CodeViewDefaultBlockCard from "./CodeViewDefaultBlockCard"; import HeadingGrid from "./HeadingGrid"; +const blockCardMinWidth = "400px"; +const gap = 3; + const gridCommonProps: Partial = { - gridTemplateColumns: "290px 360px 40px auto", - gap: 3, + gridTemplateColumns: `290px 360px 40px minmax(${blockCardMinWidth}, 1fr)`, + gap, w: "100%", }; @@ -58,82 +61,129 @@ const TestingModelTable = () => { const isConnected = useDataConnected(); const [{ languageId }] = useSettings(); const makeCodeLang = getMakeCodeLang(languageId); - const scrollableAreaRef = useRef(null); + const gridAreaRef = useRef(null); const intl = useIntl(); + + const scrollableAreaRef = useRef(null); + const [scrollbarWidth, setScrollbarWidth] = useState(0); + useEffect(() => { + const scrollEl = scrollableAreaRef.current; + if (!scrollEl) { + return; + } + const measure = () => { + const hasScrollbar = scrollEl.scrollHeight > scrollEl.clientHeight; + const scrollbarWidth = scrollEl.offsetWidth - scrollEl.clientWidth; + setScrollbarWidth(hasScrollbar ? scrollbarWidth : 0); + }; + + measure(); // run on mount. + window.addEventListener("resize", measure); + return () => window.removeEventListener("resize", measure); + }, []); + return ( - - - - + + - {actions.map((action) => { - const { requiredConfidence: threshold } = action; - return ( - - - - - - - setRequiredConfidence(action.id, val) - } - requiredConfidence={ - threshold ?? mlSettings.defaultRequiredConfidence - } - disabled={!isConnected} - /> - - - - - + + {actions.map((action, actionIdx) => { + const { requiredConfidence: threshold } = action; + return ( + + + + + + + setRequiredConfidence(action.id, val) + } + requiredConfidence={ + threshold ?? mlSettings.defaultRequiredConfidence + } + disabled={!isConnected} + /> + + + + {!projectEdited && ( - + + + + )} + + {projectEdited && actionIdx === 0 && ( + + + )} - - - ); - })} - - {projectEdited && ( - - )} - - + + ); + })} + + + + ); }; diff --git a/src/pages/DataSamplesPage.tsx b/src/pages/DataSamplesPage.tsx index ec9de390f..482bf75dd 100644 --- a/src/pages/DataSamplesPage.tsx +++ b/src/pages/DataSamplesPage.tsx @@ -288,7 +288,13 @@ const DataSamplesPage = () => { } backLabelId="home-action" > - +