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:
@@ -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>
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ const Wrapper = styled.div`
|
||||
const Label = styled.label`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export default function Checkbox({
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user