Skip to content

Commit

Permalink
Sidebar enhancements for summary fields (#4833)
Browse files Browse the repository at this point in the history
* check for top-level presence, color cleanup

* text bubbles cleanup, formatting

* enhance reload dataset

* filter tweaks

* object rendering

* embedded doc list filter tests

* group video fix

* fix keypoints

* text bubble tests

* font and spacing tweaks

* lightning fixes, better small number formatting

* fix full path regression

* move tags tests

* bugfix, update screenshots

* linux screenshots

* sidebar tests

* linting
  • Loading branch information
benjaminpkane authored Sep 25, 2024
1 parent e94cde0 commit 1b14d08
Show file tree
Hide file tree
Showing 45 changed files with 1,015 additions and 695 deletions.
19 changes: 7 additions & 12 deletions app/packages/core/src/components/Common/RangeSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { Slider as SliderUnstyled } from "@mui/material";
import React, { ChangeEvent, useLayoutEffect, useState } from "react";
import {
RecoilState,
RecoilValueReadOnly,
useRecoilState,
useRecoilValue,
} from "recoil";
import styled from "styled-components";

import { DATE_FIELD, DATE_TIME_FIELD } from "@fiftyone/utilities";

import { useTheme } from "@fiftyone/components";
import * as fos from "@fiftyone/state";
import { DATE_FIELD, DATE_TIME_FIELD } from "@fiftyone/utilities";
import { Slider as SliderUnstyled } from "@mui/material";
import type { ChangeEvent } from "react";
import React, { useLayoutEffect, useState } from "react";
import type { RecoilState, RecoilValueReadOnly } from "recoil";
import { useRecoilState, useRecoilValue } from "recoil";
import styled from "styled-components";
import { getDateTimeRangeFormattersWithPrecision } from "../../utils/generic";
import { getFormatter, getStep } from "./utils";

Expand Down
7 changes: 4 additions & 3 deletions app/packages/core/src/components/Common/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import numeral from "numeral";
import React from "react";

import {
DATE_FIELD,
DATE_TIME_FIELD,
FRAME_NUMBER_FIELD,
FRAME_SUPPORT_FIELD,
INT_FIELD,
} from "@fiftyone/utilities";
import numeral from "numeral";
import React from "react";

import { getDateTimeRangeFormattersWithPrecision } from "../../utils/generic";

Expand Down Expand Up @@ -72,6 +71,8 @@ export const getFormatter = (fieldType: string, timeZone: string, bounds) => {
return numeral(v).format(
[INT_FIELD, FRAME_NUMBER_FIELD, FRAME_SUPPORT_FIELD].includes(fieldType)
? "0a"
: bounds[1] - bounds[0] < 0.1
? "0.0000a"
: "0.00a"
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import ImageIcon from "@mui/icons-material/Image";
import VisibilityIcon from "@mui/icons-material/Visibility";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import IconButton from "@mui/material/IconButton";
import React, { ForwardedRef } from "react";
import type { ForwardedRef } from "react";
import React from "react";
import styled from "styled-components";

type ItemProp = {
Expand All @@ -30,7 +31,6 @@ const StyledPanelItem = styled.div<{ color?: string }>`
const Text = styled.div`
font-size: 1rem;
margin: auto auto auto 5px;
${({ theme }) => theme.text.secondary};
`;

export const getIcon = (icon: string) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Tooltip } from "@fiftyone/components";
import * as fos from "@fiftyone/state";
import { IconButton } from "@mui/material";
import Color from "color";
import React from "react";
import { RecoilState, useRecoilValue } from "recoil";
import type { RecoilState } from "recoil";
import styled from "styled-components";
import Item from "./FilterItem";
import Popout from "./Popout";
Expand Down Expand Up @@ -34,6 +33,7 @@ const FilterMode = styled.div`
`;

interface Props {
color: string;
excludeAtom: RecoilState<boolean>;
isMatchingAtom: RecoilState<boolean>;
valueName: string;
Expand All @@ -42,14 +42,13 @@ interface Props {
}

const FilterOption: React.FC<Props> = ({
path,
modal,
color,
excludeAtom,
isMatchingAtom,
modal,
path,
}) => {
const [open, setOpen] = React.useState(false);

const color = useRecoilValue(fos.pathColor(path));
const highlightedBGColor = Color(color).alpha(0.25).string();

const options = useOptions(modal, path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { useRecoilValue } from "recoil";
import Option from "../FilterOption";
import * as state from "./state";

function FilterOption({ modal, path }: { modal: boolean; path: string }) {
function FilterOption({
color,
modal,
path,
}: {
color: string;
modal: boolean;
path: string;
}) {
const isFiltered = useRecoilValue(fos.fieldIsFiltered({ modal, path }));
const hasBounds = useRecoilValue(state.hasBounds({ modal, path }));
const field = fos.useAssertedRecoilValue(fos.field(path));
Expand All @@ -15,6 +23,7 @@ function FilterOption({ modal, path }: { modal: boolean; path: string }) {

return (
<Option
color={color}
excludeAtom={fos.numericExcludeAtom({ modal, path })}
isMatchingAtom={fos.numericIsMatchingAtom({
modal,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as fos from "@fiftyone/state";
import { formatPrimitive } from "@fiftyone/utilities";
import React from "react";
import { useRecoilValue } from "recoil";
import styled from "styled-components";
Expand Down Expand Up @@ -29,17 +30,16 @@ const RangeSliderContainer = styled.div`
`;

type Props = {
color: string;
modal: boolean;
path: string;
named?: boolean;
color: string;
onFocus?: () => void;
onBlur?: () => void;
path: string;
};

const NumericFieldFilter = ({ modal, path, named = true }: Props) => {
const NumericFieldFilter = ({ color, modal, named = true, path }: Props) => {
const name = path.split(".").slice(-1)[0];
const color = useRecoilValue(fos.pathColor(path));
const ftype = useRecoilValue(fos.fieldType({ path }));
const field = fos.useAssertedRecoilValue(fos.field(path));
const hasBounds = useRecoilValue(state.hasBounds({ path, modal }));
Expand All @@ -49,6 +49,7 @@ const NumericFieldFilter = ({ modal, path, named = true }: Props) => {
const excluded = useRecoilValue(fos.numericExcludeAtom({ modal, path }));
const defaultRange = useRecoilValue(state.hasDefaultRange({ modal, path }));
const one = useRecoilValue(state.oneBound({ path, modal }));
const timeZone = useRecoilValue(fos.timeZone);

if (named && !lightning && !hasBounds) {
return null;
Expand Down Expand Up @@ -76,7 +77,7 @@ const NumericFieldFilter = ({ modal, path, named = true }: Props) => {
{hasBounds &&
!(excluded && defaultRange) &&
(one !== null ? (
one
formatPrimitive({ ftype, timeZone, value: one })?.toString()
) : (
<RangeSlider
showBounds={false}
Expand All @@ -94,8 +95,8 @@ const NumericFieldFilter = ({ modal, path, named = true }: Props) => {
/>
))}
{defaultRange && <Nonfinites modal={modal} path={path} />}
<FilterOption modal={modal} path={path} />
<Reset modal={modal} path={path} />
<FilterOption color={color} modal={modal} path={path} />
<Reset color={color} modal={modal} path={path} />
{!lightning && !hasBounds && <>No results</>}
</RangeSliderContainer>
</NamedRangeSliderContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,15 @@ const useReset = (options: { modal: boolean; path: string }) => {
);
};

function Reset({ modal, path }: { modal: boolean; path: string }) {
const color = useRecoilValue(fos.pathColor(path));
function Reset({
color,
modal,
path,
}: {
color: string;
modal: boolean;
path: string;
}) {
const hasVisibilitySetting = useRecoilValue(
fos.fieldHasVisibilitySetting({ modal, path })
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { LoadingDots } from "@fiftyone/components";
import * as fos from "@fiftyone/state";
import React from "react";
import type { RecoilState } from "recoil";
import {
RecoilState,
selectorFamily,
useRecoilState,
useRecoilValue,
Expand All @@ -13,17 +13,18 @@ import FilterOption from "../FilterOption/FilterOption";
import { isBooleanField, isInKeypointsField } from "../state";
import { CHECKBOX_LIMIT, nullSort } from "../utils";
import Reset from "./Reset";
import { Result } from "./Result";
import type { Result } from "./Result";
import { pathSearchCount } from "./state";
import { showSearchSelector } from "./useSelected";

interface CheckboxesProps {
results: Result[] | null;
selectedAtom: RecoilState<(string | null)[]>;
color: string;
excludeAtom: RecoilState<boolean>;
isMatchingAtom: RecoilState<boolean>;
modal: boolean;
path: string;
results: Result[] | null;
selectedAtom: RecoilState<(string | null)[]>;
}

const isSkeleton = selectorFamily({
Expand Down Expand Up @@ -72,11 +73,17 @@ const useCounts = (modal: boolean, path: string, results: Result[] | null) => {
: new Map<string | null, number | null>();

const loading = loadable.state === "loading";
results?.forEach(({ value, count }) => {
if (!data.has(value)) {
data.set(value, loading || (lightning && !unlocked) ? count : count ?? 0);

if (results) {
for (const { count, value } of results) {
if (!data.has(value)) {
data.set(
value,
loading || (lightning && !unlocked) ? count : count ?? 0
);
}
}
});
}

return { counts: data, loading };
};
Expand Down Expand Up @@ -160,15 +167,15 @@ const useGetCount = (modal: boolean, path: string) => {
};

const Checkboxes = ({
results,
selectedAtom,
color,
excludeAtom,
isMatchingAtom,
modal,
path,
results,
selectedAtom,
}: CheckboxesProps) => {
const [selected, setSelected] = useRecoilState(selectedAtom);
const color = useRecoilValue(fos.pathColor(path));

const { loading, name, selectedSet, sorting, values } = useValues({
modal,
Expand Down Expand Up @@ -225,13 +232,14 @@ const Checkboxes = ({
{!!selectedSet.size && (
<>
<FilterOption
color={color}
excludeAtom={excludeAtom}
isMatchingAtom={isMatchingAtom}
valueName={name}
modal={modal}
path={path}
valueName={name}
/>
<Reset modal={modal} path={path} />
<Reset color={color} modal={modal} path={path} />
</>
)}
</>
Expand Down
13 changes: 8 additions & 5 deletions app/packages/core/src/components/Filters/StringFilter/Reset.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import * as fos from "@fiftyone/state";
import { isSidebarFilterMode, pathColor } from "@fiftyone/state";
import { isSidebarFilterMode } from "@fiftyone/state";
import React from "react";
import { useRecoilCallback, useRecoilValue } from "recoil";
import { useRecoilCallback } from "recoil";
import { Button } from "../../utils";

export default function (params: { modal: boolean; path: string }) {
const color = useRecoilValue(pathColor(params.path));
export default function (params: {
color: string;
modal: boolean;
path: string;
}) {
const handleReset = useRecoilCallback(
({ snapshot, reset }) =>
async () => {
Expand All @@ -19,7 +22,7 @@ export default function (params: { modal: boolean; path: string }) {
return (
<Button
text={"Reset"}
color={color}
color={params.color}
onClick={handleReset}
style={{
margin: "0.25rem -0.5rem",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Selector, useTheme } from "@fiftyone/components";
import * as fos from "@fiftyone/state";
import React from "react";
import { RecoilState, useRecoilValue } from "recoil";
import type { RecoilState } from "recoil";
import { useRecoilValue } from "recoil";
import styled from "styled-components";
import FieldLabelAndInfo from "../../FieldLabelAndInfo";
import { isInKeypointsField } from "../state";
import Checkboxes from "./Checkboxes";
import ResultComponent from "./Result";
import useOnSelect from "./useOnSelect";
import useSelected, { ResultsAtom } from "./useSelected";
import type { ResultsAtom } from "./useSelected";
import useSelected from "./useSelected";

const StringFilterContainer = styled.div`
background: ${({ theme }) => theme.background.level2};
Expand All @@ -32,13 +34,14 @@ const NamedStringFilterHeader = styled.div`
`;

interface Props {
selectedAtom: RecoilState<(string | null)[]>;
color: string;
excludeAtom: RecoilState<boolean>; // toggles select or exclude
isMatchingAtom: RecoilState<boolean>; // toggles match or filter
resultsAtom: ResultsAtom;
modal: boolean;
path: string;
named?: boolean;
resultsAtom: ResultsAtom;
selectedAtom: RecoilState<(string | null)[]>;
}

const useName = (path: string) => {
Expand All @@ -53,13 +56,14 @@ const useName = (path: string) => {
};

const StringFilter = ({
resultsAtom,
selectedAtom,
color,
excludeAtom,
isMatchingAtom,
path,
modal,
named = true,
path,
resultsAtom,
selectedAtom,
}: Props) => {
const name = useName(path);
const isFilterMode = useRecoilValue(fos.isSidebarFilterMode);
Expand All @@ -73,7 +77,6 @@ const StringFilter = ({
const skeleton =
useRecoilValue(isInKeypointsField(path)) && name === "keypoints";
const theme = useTheme();
const color = useRecoilValue(fos.pathColor(path));
const lightningOn = useRecoilValue(fos.lightning);
const lightningPath = useRecoilValue(fos.isLightningPath(path));
const lightning = lightningOn && lightningPath && !modal;
Expand Down Expand Up @@ -121,12 +124,13 @@ const StringFilter = ({
/>
)}
<Checkboxes
color={color}
excludeAtom={excludeAtom}
modal={modal}
isMatchingAtom={isMatchingAtom}
path={path}
results={results?.results || null}
selectedAtom={selectedAtom}
excludeAtom={excludeAtom}
isMatchingAtom={isMatchingAtom}
modal={modal}
/>
</StringFilterContainer>
</NamedStringFilterContainer>
Expand Down
Loading

0 comments on commit 1b14d08

Please sign in to comment.