Skip to content

Commit

Permalink
QP sidebar enhancements (#5098)
Browse files Browse the repository at this point in the history
* fix frames field on non-video datasets

* add support for computing real bounds to lightning queries when nonfinites are present

* ux cleanup

* lightning test fixes

* sample count copy fix

* unwind fix

* numeric cleanup

* string cleanup

* details

* modal and keypoints bugs

* fix top lightning bolt
  • Loading branch information
benjaminpkane authored Nov 14, 2024
1 parent a07b12c commit 7300c9f
Show file tree
Hide file tree
Showing 28 changed files with 716 additions and 332 deletions.
4 changes: 2 additions & 2 deletions app/packages/core/src/components/FieldLabelAndInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ function FieldInfoExpanded({

useEffect(updatePosition, [field, isCollapsed]);
const timeZone = useRecoilValue(fos.timeZone);
const disabled = useRecoilValue(fos.disabledFilterPaths);
const disabled = useRecoilValue(fos.isDisabledFilterPath(path));

return ReactDOM.createPortal(
<FieldInfoHoverTarget
Expand All @@ -283,7 +283,7 @@ function FieldInfoExpanded({
onClick={(e) => e.stopPropagation()}
>
<FieldInfoExpandedContainer color={color}>
{!disabled.has(field) && (
{!disabled && (
<CustomizeColor
onClick={onClickCustomizeColor}
color={color}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { LoadingDots } from "@fiftyone/components";
import React from "react";
import styled from "styled-components";

const BoxDiv = styled.div`
background: ${({ theme }) => theme.background.level2};
border: 1px solid var(--fo-palette-divider);
border-radius: 2px;
color: ${({ theme }) => theme.text.secondary};
margin-top: 0.25rem;
padding: 0.25rem 0.5rem;
`;

export default function Box({
children,
text,
}: React.PropsWithChildren<{ text?: string }>) {
const value = text === "Loading" ? <LoadingDots text="Loading" /> : text;
return (
<BoxDiv
style={{
height: 71,
display: "flex",
justifyContent: "center",
flexDirection: "column",
textAlign: "center",
}}
>
{children ? children : value}
</BoxDiv>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,17 @@ const useNonfiniteSettings = (params: { modal: boolean; path: string }) => {
const useNonfinites = (options: { modal: boolean; path: string }) => {
const get = useNonfiniteSettings(options);
const list = [get("none")];
const { ftype } = fos.useAssertedRecoilValue(fos.field(options.path));
const { ftype, subfield } = fos.useAssertedRecoilValue(
fos.field(options.path)
);
const data = useRecoilValue(
fos.nonfiniteData({
extended: false,
path: options.path,
modal: options.modal,
})
);
if (ftype === FLOAT_FIELD) {
if (ftype === FLOAT_FIELD || subfield === FLOAT_FIELD) {
for (const key of state.FLOAT_NONFINITES) {
list.push(get(key));
}
Expand All @@ -78,16 +80,6 @@ function Nonfinites({ modal, path }: { modal: boolean; path: string }) {
});
const hasBounds = useRecoilValue(state.hasBounds({ modal, path }));
const one = useRecoilValue(state.oneBound({ modal, path }));
const queryPerformance = useRecoilValue(fos.queryPerformance);
const indexed = useRecoilValue(fos.pathHasIndexes(path));

if (queryPerformance && indexed && nonfinites.length) {
return (
<span style={{ color: "var(--fo-palette-danger-plainColor)" }}>
{nonfinites.map(({ key }) => key).join(", ")} present
</span>
);
}

if (nonfinites.length === 1 && nonfinites[0].key === "none") {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { LoadingDots, useTheme } from "@fiftyone/components";
import * as fos from "@fiftyone/state";
import * as schemaAtoms from "@fiftyone/state/src/recoil/schema";
import React, { Suspense } from "react";
import { useRecoilValue } from "recoil";
import styled from "styled-components";
import FieldLabelAndInfo from "../../FieldLabelAndInfo";
import { LightningBolt } from "../../Sidebar/Entries/FilterablePathEntry/Icon";
import { Button } from "../../utils";
import Box from "./Box";
import RangeSlider from "./RangeSlider";
import * as state from "./state";
import Bolt from "@mui/icons-material/Bolt";
import useShow from "./use-show";

const Container = styled.div`
margin: 3px;
Expand All @@ -21,15 +19,6 @@ const Header = styled.div`
align-items: center;
`;

const Box = styled.div`
background: ${({ theme }) => theme.background.level2};
border: 1px solid var(--fo-palette-divider);
border-radius: 2px;
color: ${({ theme }) => theme.text.secondary};
margin-top: 0.25rem;
padding: 0.25rem 0.5rem;
`;

type Props = {
color: string;
modal: boolean;
Expand All @@ -41,28 +30,24 @@ type Props = {

const NumericFieldFilter = ({ color, modal, named = true, path }: Props) => {
const name = path.split(".").slice(-1)[0];
const fieldType = useRecoilValue(schemaAtoms.filterFields(path));
const isGroup = fieldType.length > 1;
const [showRange, setShowRange] = React.useState(!isGroup);
const [showRange, setShowRange] = React.useState(!named);
const field = fos.useAssertedRecoilValue(fos.field(path));
const queryPerformance = useRecoilValue(fos.queryPerformance);
const indexed = useRecoilValue(fos.pathHasIndexes(path));
const hasBounds = useRecoilValue(
state.hasBounds({ path, modal, shouldCalculate: !queryPerformance })

const { show, showLoadButton, showQueryPerformanceIcon } = useShow(
modal,
named,
path,
showRange
);
const theme = useTheme();

if (!queryPerformance && named && !hasBounds) {
if (!show) {
return null;
}

const handleShowRange = () => {
setShowRange(true);
};

const showButton = isGroup && queryPerformance && !showRange && !modal;
const showQueryPerformanceIcon =
isGroup && queryPerformance && indexed && !modal;
return (
<Container onClick={(e) => e.stopPropagation()}>
{named && name && (
Expand All @@ -73,29 +58,21 @@ const NumericFieldFilter = ({ color, modal, named = true, path }: Props) => {
template={({ label, hoverTarget }) => (
<Header>
<span ref={hoverTarget}>{label}</span>
{showQueryPerformanceIcon && (
<Bolt fontSize={"small"} sx={{ color: theme.action.active }} />
)}
{showQueryPerformanceIcon && <LightningBolt />}
</Header>
)}
/>
)}
<Suspense
fallback={
<Box>
<LoadingDots text="Loading" />
</Box>
}
>
{showButton ? (
<Suspense fallback={<Box text="Loading" />}>
{showLoadButton ? (
<Box>
<Button
text={`Filter by ${name}`}
color={color}
onClick={handleShowRange}
style={{
margin: "0.25rem -0.5rem",
height: "2rem",
margin: "0 -0.5rem",
borderRadius: 0,
textAlign: "center",
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React from "react";
import { useRecoilValue } from "recoil";
import styled from "styled-components";
import CommonRangeSlider from "../../Common/RangeSlider";
import Box from "./Box";
import FilterOption from "./FilterOption";
import Nonfinites from "./Nonfinites";
import Reset from "./Reset";
Expand Down Expand Up @@ -35,36 +36,44 @@ const RangeSlider = ({
const timeZone = useRecoilValue(fos.timeZone);
const hasBounds = useRecoilValue(state.hasBounds({ path, modal }));

if (!hasBounds) {
return <Box text="No results" />;
}

const showSlider = hasBounds && !(excluded && defaultRange);
if (showSlider && one !== null) {
return (
<Box
text={formatPrimitive({ ftype, timeZone, value: one })?.toString()}
/>
);
}

return (
<Container
onMouseDown={(e) => e.stopPropagation()}
style={{ cursor: "default" }}
data-cy={`numeric-slider-container-${key}`}
>
{hasBounds &&
!(excluded && defaultRange) &&
(one !== null ? (
formatPrimitive({ ftype, timeZone, value: one })?.toString()
) : (
<CommonRangeSlider
showBounds={false}
fieldType={ftype}
valueAtom={fos.rangeAtom({
modal,
path,
withBounds: true,
})}
boundsAtom={fos.boundsAtom({
path,
modal,
})}
color={color}
/>
))}
{showSlider && (
<CommonRangeSlider
showBounds={false}
fieldType={ftype}
valueAtom={fos.rangeAtom({
modal,
path,
withBounds: true,
})}
boundsAtom={fos.boundsAtom({
path,
modal,
})}
color={color}
/>
)}
{defaultRange && <Nonfinites modal={modal} path={path} />}
<FilterOption color={color} modal={modal} path={path} />
<Reset color={color} modal={modal} path={path} />
{!hasBounds && "No results"}
</Container>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const FLOAT_NONFINITES: Nonfinite[] = ["inf", "ninf", "nan"];
export const hasBounds = selectorFamily({
key: "hasBounds",
get:
(params: { path: string; modal: boolean; shouldCalculate: boolean }) =>
(params: { path: string; modal: boolean; shouldCalculate?: boolean }) =>
({ get }) => {
const shouldCalculate = params.shouldCalculate ?? true;
return shouldCalculate
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as fos from "@fiftyone/state";
import { useRecoilValue } from "recoil";
import * as state from "./state";

export default function useShow(
modal: boolean,
named: boolean,
path: string,
showRange: boolean
) {
const queryPerformance = useRecoilValue(fos.queryPerformance);
const hasBounds = useRecoilValue(
state.hasBounds({
path,
modal,
shouldCalculate: !queryPerformance || modal,
})
);
const indexed = useRecoilValue(fos.pathHasIndexes(path));
const frameField = useRecoilValue(fos.isFrameField(path));

return {
show: hasBounds || (queryPerformance && !modal) || !named,
showLoadButton: named && queryPerformance && !showRange && !modal,
showQueryPerformanceIcon:
named && queryPerformance && indexed && !frameField && !modal,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ interface CheckboxesProps {
color: string;
excludeAtom: RecoilState<boolean>;
isMatchingAtom: RecoilState<boolean>;

modal: boolean;
path: string;
results: Result[] | null;
selectedAtom: RecoilState<(string | null)[]>;
skeleton?: boolean;
}

const isSkeleton = selectorFamily({
Expand All @@ -32,7 +34,7 @@ const isSkeleton = selectorFamily({
(path: string) =>
({ get }) =>
get(isInKeypointsField(path)) &&
path.split(".").slice(-1)[0] === "keypoints",
path.split(".").slice(-1)[0] === "points",
});

const checkboxCounts = selectorFamily({
Expand Down Expand Up @@ -162,6 +164,7 @@ const Checkboxes = ({
path,
results,
selectedAtom,
skeleton,
}: CheckboxesProps) => {
const [selected, setSelected] = useRecoilState(selectedAtom);
const queryPerformance = useRecoilValue(fos.queryPerformance);
Expand All @@ -173,10 +176,10 @@ const Checkboxes = ({
selected,
});

const show = useRecoilValue(isBooleanField(path));
const show = useRecoilValue(fos.isObjectIdField(path));
const getCount = useGetCount(modal, path);

if (!modal && queryPerformance && values.length === 0) {
if (!modal && queryPerformance && !skeleton && values.length === 0) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Selector, useTheme } from "@fiftyone/components";
import * as fos from "@fiftyone/state";
import Bolt from "@mui/icons-material/Bolt";
import React from "react";
import type { RecoilState } from "recoil";
import { useRecoilValue } from "recoil";
Expand All @@ -11,8 +12,6 @@ import ResultComponent from "./Result";
import useOnSelect from "./useOnSelect";
import type { ResultsAtom } from "./useSelected";
import useSelected from "./useSelected";
import * as schemaAtoms from "@fiftyone/state/src/recoil/schema";
import Bolt from "@mui/icons-material/Bolt";

const StringFilterContainer = styled.div`
background: ${({ theme }) => theme.background.level2};
Expand Down Expand Up @@ -76,20 +75,19 @@ const StringFilter = ({
path,
resultsAtom
);
const fieldType = useRecoilValue(schemaAtoms.filterFields(path));
const isGroup = fieldType.length > 1;
const onSelect = useOnSelect(modal, path, selectedAtom);
const skeleton =
useRecoilValue(isInKeypointsField(path)) && name === "keypoints";
useRecoilValue(isInKeypointsField(path)) && name === "points";
const indexed = useRecoilValue(fos.pathHasIndexes(path));
const theme = useTheme();
const queryPerformance = useRecoilValue(fos.queryPerformance);
if (named && !queryPerformance && !results?.count) {
const frameField = useRecoilValue(fos.isFrameField(path));
if (named && (!queryPerformance || modal) && !results?.count) {
return null;
}

const showQueryPerformanceIcon =
isGroup && queryPerformance && indexed && !modal;
named && queryPerformance && indexed && !modal && !frameField;

return (
<NamedStringFilterContainer
Expand Down Expand Up @@ -140,6 +138,7 @@ const StringFilter = ({
path={path}
results={results?.results || null}
selectedAtom={selectedAtom}
skeleton={skeleton}
/>
</StringFilterContainer>
</NamedStringFilterContainer>
Expand Down
Loading

0 comments on commit 7300c9f

Please sign in to comment.