Improved search filtering (#940)

* Filter search by collectionId

* Improve spec, remove recursive import

* Add userId filter for documents.search

* 💚

* Search filter UI

* WIP UI

* Date filtering
Prevent dupe menu

* Refactor

* button

* Added year option, improved hover states

* Add new indexes

* Remove manual string interpolation in SQL construction

* Move dateFilter validation to controller

* Fixes: Double query when changing filter
Fixes: Visual jump between filters in dropdown

* Add option to clear filters

* More clearly define dropdowns in dark mode

* Checkbox -> Checkmark
This commit is contained in:
Tom Moor
2019-04-23 07:31:20 -07:00
committed by GitHub
parent a256eba856
commit da7fdfef0a
23 changed files with 679 additions and 76 deletions

View File

@@ -2,6 +2,7 @@
import * as React from 'react';
import styled from 'styled-components';
import { darken } from 'polished';
import { ExpandedIcon } from 'outline-icons';
const RealButton = styled.button`
display: inline-block;
@@ -22,6 +23,10 @@ const RealButton = styled.button`
cursor: pointer;
user-select: none;
svg {
fill: ${props => props.theme.buttonText};
}
&::-moz-focus-inner {
padding: 0;
border: 0;
@@ -45,6 +50,10 @@ const RealButton = styled.button`
box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 2px;
border: 1px solid ${darken(0.1, props.theme.buttonNeutralBackground)};
svg {
fill: ${props.theme.buttonNeutralText};
}
&:hover {
background: ${darken(0.05, props.theme.buttonNeutralBackground)};
border: 1px solid ${darken(0.15, props.theme.buttonNeutralBackground)};
@@ -70,8 +79,9 @@ const Label = styled.span`
`;
const Inner = styled.span`
padding: 0 ${props => (props.small ? 8 : 12)}px;
display: flex;
padding: 0 ${props => (props.small ? 8 : 12)}px;
padding-right: ${props => (props.disclosure ? 2 : props.small ? 8 : 12)}px;
line-height: ${props => (props.small ? 24 : 28)}px;
justify-content: center;
align-items: center;
@@ -88,6 +98,7 @@ export type Props = {
className?: string,
children?: React.Node,
small?: boolean,
disclosure?: boolean,
};
export default function Button({
@@ -95,6 +106,7 @@ export default function Button({
icon,
children,
value,
disclosure,
small,
...rest
}: Props) {
@@ -103,9 +115,10 @@ export default function Button({
return (
<RealButton small={small} {...rest}>
<Inner hasIcon={hasIcon} small={small}>
<Inner hasIcon={hasIcon} small={small} disclosure={disclosure}>
{hasIcon && icon}
{hasText && <Label hasIcon={hasIcon}>{children || value}</Label>}
{disclosure && <ExpandedIcon />}
</Inner>
</RealButton>
);

View File

@@ -25,6 +25,7 @@ const Wrapper = styled.div`
const Label = styled.label`
display: flex;
align-items: center;
user-select: none;
`;
export default function Checkbox({

View File

@@ -8,21 +8,32 @@ import styled from 'styled-components';
import Flex from 'shared/components/Flex';
import { fadeAndScaleIn } from 'shared/styles/animations';
let previousClosePortal;
type Children =
| React.Node
| ((options: { closePortal: () => void }) => React.Node);
type Props = {
label: React.Node,
onOpen?: () => void,
onClose?: () => void,
children?: React.Node,
children?: Children,
className?: string,
style?: Object,
leftAlign?: boolean,
};
@observer
class DropdownMenu extends React.Component<Props> {
@observable top: number;
@observable right: number;
@observable left: number;
handleOpen = (openPortal: (SyntheticEvent<*>) => *) => {
handleOpen = (
openPortal: (SyntheticEvent<*>) => void,
closePortal: () => void
) => {
return (ev: SyntheticMouseEvent<*>) => {
ev.preventDefault();
const currentTarget = ev.currentTarget;
@@ -32,7 +43,18 @@ class DropdownMenu extends React.Component<Props> {
const bodyRect = document.body.getBoundingClientRect();
const targetRect = currentTarget.getBoundingClientRect();
this.top = targetRect.bottom - bodyRect.top;
this.right = bodyRect.width - targetRect.left - targetRect.width;
if (this.props.leftAlign) {
this.left = targetRect.left;
} else {
this.right = bodyRect.width - targetRect.left - targetRect.width;
}
// attempt to keep only one flyout menu open at once
if (previousClosePortal) {
previousClosePortal();
}
previousClosePortal = closePortal;
openPortal(ev);
}
};
@@ -51,18 +73,27 @@ class DropdownMenu extends React.Component<Props> {
>
{({ closePortal, openPortal, portal }) => (
<React.Fragment>
<Label onClick={this.handleOpen(openPortal)}>{label}</Label>
<Label onClick={this.handleOpen(openPortal, closePortal)}>
{label}
</Label>
{portal(
<Menu
onClick={ev => {
ev.stopPropagation();
closePortal();
}}
onClick={
typeof children === 'function'
? undefined
: ev => {
ev.stopPropagation();
closePortal();
}
}
style={this.props.style}
top={this.top}
left={this.left}
right={this.right}
>
{children}
{typeof children === 'function'
? children({ closePortal })
: children}
</Menu>
)}
</React.Fragment>
@@ -83,10 +114,11 @@ const Label = styled(Flex).attrs({
const Menu = styled.div`
animation: ${fadeAndScaleIn} 200ms ease;
transform-origin: 75% 0;
transform-origin: ${({ left }) => (left !== undefined ? '25%' : '75%')} 0;
position: absolute;
right: ${({ right }) => right}px;
${({ left }) => (left !== undefined ? `left: ${left}px` : '')};
${({ right }) => (right !== undefined ? `right: ${right}px` : '')};
top: ${({ top }) => top}px;
z-index: 1000;

View File

@@ -3,7 +3,7 @@ import * as React from 'react';
import styled from 'styled-components';
type Props = {
children: string,
children: React.Node,
};
const Empty = (props: Props) => {