Skip to content
Snippets Groups Projects
Unverified Commit da88cc8a authored by Ryan Laurie's avatar Ryan Laurie Committed by GitHub
Browse files

Bulk filter Modal: Make search visible (#24526)

* make search visible

* update Modal title
parent f32550c6
Branches
Tags
No related merge requests found
import React, { useEffect, useState, useRef } from "react";
import { t } from "ttag";
import { useOnMount } from "metabase/hooks/use-on-mount";
import {
SearchContainer,
SearchInput,
SearchIcon,
} from "./BulkFilterModal.styled";
export const FieldSearch = ({
value,
onChange,
}: {
value: string;
onChange: (value: string) => void;
}): JSX.Element => {
const [showSearch, setShowSearch] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);
useOnMount(() => {
const searchToggleListener = (e: KeyboardEvent) => {
if ((e.ctrlKey || e.metaKey) && e.key === "k") {
setShowSearch(true);
}
};
window.addEventListener("keydown", searchToggleListener);
return () => window.removeEventListener("keydown", searchToggleListener);
});
const shouldClose = () => {
const input = inputRef.current;
const isFocused = document.activeElement === input;
if (input && !input.value && !isFocused) {
return true;
}
return false;
};
useEffect(() => {
if (showSearch) {
inputRef.current?.focus();
}
}, [showSearch]);
return (
<SearchContainer
isActive={showSearch}
onMouseEnter={() => setShowSearch(true)}
onMouseLeave={e => shouldClose() && setShowSearch(false)}
>
<SearchIcon
name="search"
onClick={() => setShowSearch(lastShowSearch => !lastShowSearch)}
/>
<SearchInput
ref={inputRef}
isActive={showSearch}
type="search"
placeholder={t`Search for a column...`}
value={value}
onChange={e => onChange(e.target.value)}
onBlur={() => shouldClose() && setShowSearch(false)}
/>
</SearchContainer>
);
};
......@@ -6,6 +6,7 @@ import TabList from "metabase/core/components/TabList";
import TabPanel from "metabase/core/components/TabPanel";
import Ellipsified from "metabase/core/components/Ellipsified";
import IconButtonWrapper from "metabase/components/IconButtonWrapper";
import Icon from "metabase/components/Icon";
interface ModalRootProps {
hasSideNav?: boolean;
......@@ -84,11 +85,55 @@ export const ModalCloseButton = styled(IconButtonWrapper)`
color: ${color("text-light")};
`;
export const SearchContainer = styled.div`
margin-right: ${space(2)};
export const SearchIcon = styled(Icon)`
margin: 0 ${space(1)};
color: ${color("text-light")};
`;
export const SearchContainer = styled.div<{ isActive: boolean }>`
display: flex;
flex: 1 1 auto;
align-items: center;
position: relative;
border: ${props =>
props.isActive ? `1px solid ${color("border")}` : "none"};
overflow: hidden;
transition: max-width 0.2s;
margin-right: ${space(3)};
@media (prefers-reduced-motion) {
transition: none;
}
justify-content: center;
margin-left: auto;
input {
font-weight: bold;
padding: 12px ${space(3)};
max-width: ${props => (props.isActive ? "20rem" : "2rem")};
height: 2rem;
border-radius: 0.5rem;
${breakpointMaxSmall} {
display: none;
}
`;
export const SearchInput = styled.input<{ isActive: boolean }>`
background-color: transparent;
border: none;
color: ${color("text-medium")};
font-weight: 700;
margin-right: ${space(1)};
width: 100%;
&:focus {
outline: none;
}
&::placeholder {
color: ${color("text-light")};
}
width: ${props => (props.isActive ? "100%" : 0)};
`;
......@@ -2,9 +2,10 @@ import React, { useCallback, useMemo, useState } from "react";
import { t } from "ttag";
import { useDebouncedEffect } from "metabase/hooks/use-debounced-effect";
import { useOnMount } from "metabase/hooks/use-on-mount";
import Filter from "metabase-lib/lib/queries/structured/Filter";
import { pluralize } from "metabase/lib/formatting";
import StructuredQuery, {
FilterSection,
DimensionOption,
......@@ -17,7 +18,6 @@ import Tab from "metabase/core/components/Tab";
import TabContent from "metabase/core/components/TabContent";
import Icon from "metabase/components/Icon";
import BulkFilterList from "../BulkFilterList";
import TextInput from "metabase/components/TextInput";
import {
ModalBody,
ModalCloseButton,
......@@ -29,9 +29,10 @@ import {
ModalTabList,
ModalTabPanel,
ModalTitle,
SearchContainer,
} from "./BulkFilterModal.styled";
import { FieldSearch } from "./BulkFilterFieldSearch";
import { fixBetweens, getSearchHits } from "./utils";
export interface BulkFilterModalProps {
......@@ -45,21 +46,9 @@ const BulkFilterModal = ({
}: BulkFilterModalProps): JSX.Element | null => {
const [query, setQuery] = useState(getQuery(question));
const [isChanged, setIsChanged] = useState(false);
const [showSearch, setShowSearch] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
useOnMount(() => {
const searchToggleListener = (e: KeyboardEvent) => {
if ((e.ctrlKey || e.metaKey) && e.key === "k") {
setSearchQuery("");
setShowSearch(showSearch => !showSearch);
}
};
window.addEventListener("keydown", searchToggleListener);
return () => window.removeEventListener("keydown", searchToggleListener);
});
const filters = useMemo(() => {
return query.topLevelFilters();
}, [query]);
......@@ -114,13 +103,12 @@ const BulkFilterModal = ({
<ModalRoot hasSideNav={hasSideNav}>
<ModalHeader>
<ModalTitle>{getTitle(query, sections.length === 1)}</ModalTitle>
{showSearch ? (
<FieldSearch value={searchQuery} onChange={setSearchQuery} />
) : (
<ModalCloseButton onClick={onClose}>
<Icon name="close" />
</ModalCloseButton>
)}
<FieldSearch value={searchQuery} onChange={setSearchQuery} />
<ModalCloseButton onClick={onClose}>
<Icon name="close" />
</ModalCloseButton>
</ModalHeader>
<ModalMain>
{!hasSideNav || searchItems ? (
......@@ -265,33 +253,10 @@ const getTitle = (query: StructuredQuery, singleTable: boolean) => {
const table = query.table();
if (singleTable) {
return t`Filter by ${table.displayName()}`;
return t`Filter ${pluralize(table.displayName())} by`;
} else {
return t`Filter by`;
return t`Filter by}`;
}
};
const FieldSearch = ({
value,
onChange,
}: {
value: string;
onChange: (value: string) => void;
}): JSX.Element => {
return (
<SearchContainer>
<TextInput
hasClearButton
placeholder={t`Search for a column...`}
value={value}
onChange={onChange}
padding="sm"
borderRadius="md"
autoFocus
icon={<Icon name="search" size={13} style={{ marginTop: 2 }} />}
/>
</SearchContainer>
);
};
export default BulkFilterModal;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment