Search: Improving search look and feel (#23854)

* Search: Improving search look and feel

* Fixed issue with tag filter beeing cramped and wrapping tags

* Minor tag polish

* fixed type
This commit is contained in:
Torkel Ödegaard 2020-04-24 19:23:45 +02:00 committed by GitHub
parent 76b184b93c
commit 6f02b51561
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 107 additions and 128 deletions

View File

@ -236,7 +236,6 @@ export interface GrafanaTheme extends GrafanaThemeCommons {
formSwitchBgHover: string; formSwitchBgHover: string;
formSwitchBgDisabled: string; formSwitchBgDisabled: string;
formSwitchDot: string; formSwitchDot: string;
formCheckboxBg: string;
formCheckboxBgChecked: string; formCheckboxBgChecked: string;
formCheckboxBgCheckedHover: string; formCheckboxBgCheckedHover: string;
formCheckboxCheckmark: string; formCheckboxCheckmark: string;

View File

@ -50,7 +50,7 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme) => {
* */ * */
&:checked + span { &:checked + span {
background: blue; background: blue;
background: ${theme.colors.formCheckboxBgChecked}; background: ${theme.colors.formInputBg};
border: none; border: none;
&:hover { &:hover {
background: ${theme.colors.formCheckboxBgCheckedHover}; background: ${theme.colors.formCheckboxBgCheckedHover};
@ -74,7 +74,7 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme) => {
height: ${checkboxSize}; height: ${checkboxSize};
border-radius: ${theme.border.radius.sm}; border-radius: ${theme.border.radius.sm};
margin-right: ${theme.spacing.formSpacingBase}px; margin-right: ${theme.spacing.formSpacingBase}px;
background: ${theme.colors.formCheckboxBg}; background: ${theme.colors.formInputBg};
border: 1px solid ${theme.colors.formInputBorder}; border: 1px solid ${theme.colors.formInputBorder};
position: absolute; position: absolute;
top: 1px; top: 1px;

View File

@ -17,7 +17,7 @@ export interface RadioButtonProps {
} }
const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme, size: RadioButtonSize, fullWidth?: boolean) => { const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme, size: RadioButtonSize, fullWidth?: boolean) => {
const { fontSize, height } = getPropertiesForButtonSize({ const { fontSize, height, padding } = getPropertiesForButtonSize({
theme, theme,
size, size,
hasIcon: false, hasIcon: false,
@ -25,7 +25,6 @@ const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme, size: RadioButt
variant: 'secondary', variant: 'secondary',
}); });
const horizontalPadding = theme.spacing[size] ?? theme.spacing.md;
const c = theme.palette; const c = theme.palette;
const textColor = theme.colors.textSemiWeak; const textColor = theme.colors.textSemiWeak;
const textColorHover = theme.colors.text; const textColorHover = theme.colors.text;
@ -75,7 +74,7 @@ const getRadioButtonStyles = stylesFactory((theme: GrafanaTheme, size: RadioButt
// Deduct border from line-height for perfect vertical centering on windows and linux // Deduct border from line-height for perfect vertical centering on windows and linux
line-height: ${height - 2}px; line-height: ${height - 2}px;
color: ${textColor}; color: ${textColor};
padding: 0 ${horizontalPadding}; padding: ${padding};
margin-left: -1px; margin-left: -1px;
border-radius: ${theme.border.radius.sm}; border-radius: ${theme.border.radius.sm};
border: ${border}; border: ${border};

View File

@ -45,11 +45,10 @@ const getTagStyles = (theme: GrafanaTheme, name: string, colorIndex?: number) =>
line-height: ${theme.typography.lineHeight.xs}; line-height: ${theme.typography.lineHeight.xs};
vertical-align: baseline; vertical-align: baseline;
background-color: ${colors.color}; background-color: ${colors.color};
color: ${theme.palette.white}; color: ${theme.colors.textStrong};
white-space: nowrap; white-space: nowrap;
text-shadow: none; text-shadow: none;
padding: 3px 6px; padding: 3px 6px;
border: 1px solid ${colors.borderColor};
border-radius: ${theme.border.radius.md}; border-radius: ${theme.border.radius.md};
:hover { :hover {

View File

@ -27,12 +27,9 @@ const getStyles = () => {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
flex-wrap: wrap; flex-wrap: wrap;
padding: 10px;
`, `,
tag: css` tag: css`
margin-left: 6px; margin-left: 6px;
font-size: 11px;
padding: 2px 6px;
`, `,
}; };
}; };

View File

@ -81,7 +81,6 @@ const form = {
formSwitchBgActiveHover: basicColors.blue80, formSwitchBgActiveHover: basicColors.blue80,
formSwitchBgDisabled: basicColors.gray25, formSwitchBgDisabled: basicColors.gray25,
formSwitchDot: basicColors.gray15, formSwitchDot: basicColors.gray15,
formCheckboxBg: basicColors.dark5,
formCheckboxBgChecked: basicColors.blue95, formCheckboxBgChecked: basicColors.blue95,
formCheckboxBgCheckedHover: basicColors.blue80, formCheckboxBgCheckedHover: basicColors.blue80,
formCheckboxCheckmark: basicColors.gray25, formCheckboxCheckmark: basicColors.gray25,

View File

@ -80,7 +80,6 @@ const form = {
formSwitchBgActiveHover: basicColors.blue80, formSwitchBgActiveHover: basicColors.blue80,
formSwitchBgDisabled: basicColors.gray4, formSwitchBgDisabled: basicColors.gray4,
formSwitchDot: basicColors.white, formSwitchDot: basicColors.white,
formCheckboxBg: basicColors.white,
formCheckboxBgChecked: basicColors.blue77, formCheckboxBgChecked: basicColors.blue77,
formCheckboxBgCheckedHover: basicColors.blue80, formCheckboxBgCheckedHover: basicColors.blue80,
formCheckboxCheckmark: basicColors.white, formCheckboxCheckmark: basicColors.white,

View File

@ -15,11 +15,12 @@ export class TagBadge extends React.Component<Props, any> {
render() { render() {
const { label, removeIcon, count } = this.props; const { label, removeIcon, count } = this.props;
const { color, borderColor } = getTagColorsFromName(label); const { color } = getTagColorsFromName(label);
const tagStyle = { const tagStyle = {
backgroundColor: color, backgroundColor: color,
borderColor: borderColor,
}; };
const countLabel = count !== 0 && <span className="tag-count-label">{`(${count})`}</span>; const countLabel = count !== 0 && <span className="tag-count-label">{`(${count})`}</span>;
return ( return (

View File

@ -107,17 +107,11 @@ const getStyles = stylesFactory(() => {
return { return {
tagFilter: css` tagFilter: css`
min-width: 180px; min-width: 180px;
line-height: 22px;
flex-grow: 1; flex-grow: 1;
.label-tag { .label-tag {
margin-left: 6px; margin-left: 6px;
font-size: 11px;
cursor: pointer; cursor: pointer;
.fa.fa-remove {
margin-right: 3px;
}
} }
`, `,
}; };

View File

@ -14,7 +14,7 @@ export const TagOption = (props: ExtendedOptionProps) => {
const { data, className, label } = props; const { data, className, label } = props;
return ( return (
<components.Option {...props}> <components.Option {...props}>
<div className={`tag-filter-option btn btn-link ${className || ''}`}> <div className={`tag-filter-option ${className || ''}`}>
<TagBadge label={label} removeIcon={false} count={data.count} /> <TagBadge label={label} removeIcon={false} count={data.count} />
</div> </div>
</components.Option> </components.Option>

View File

@ -44,7 +44,7 @@ export const ActionRow: FC<Props> = ({
return ( return (
<div className={styles.actionRow}> <div className={styles.actionRow}>
<HorizontalGroup spacing="md" width="100%"> <HorizontalGroup spacing="md">
{!hideLayout ? <RadioButtonGroup options={layoutOptions} onChange={onLayoutChange} value={layout} /> : null} {!hideLayout ? <RadioButtonGroup options={layoutOptions} onChange={onLayoutChange} value={layout} /> : null}
<SortPicker onChange={onSortChange} value={query.sort} /> <SortPicker onChange={onSortChange} value={query.sort} />
</HorizontalGroup> </HorizontalGroup>

View File

@ -192,10 +192,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
return { return {
container: css` container: css`
height: 100%; height: 100%;
.results-container {
padding: 5px 0 0;
}
`, `,
searchField: css` searchField: css`
height: auto; height: auto;

View File

@ -22,6 +22,7 @@ const getStyles = stylesFactory(() => ({
// Vertically align absolutely positioned checkbox element // Vertically align absolutely positioned checkbox element
wrapper: css` wrapper: css`
height: 21px; height: 21px;
margin-right: 12px;
& > label { & > label {
height: 100%; height: 100%;
} }

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { shallow, mount } from 'enzyme'; import { mount } from 'enzyme';
import { Tag } from '@grafana/ui'; import { Tag } from '@grafana/ui';
import { SearchItem, Props } from './SearchItem'; import { SearchItem, Props } from './SearchItem';
import { DashboardSearchItemType } from '../types'; import { DashboardSearchItemType } from '../types';
@ -17,7 +17,7 @@ const data = {
checked: false, checked: false,
}; };
const setup = (propOverrides?: Partial<Props>, renderMethod = shallow) => { const setup = (propOverrides?: Partial<Props>) => {
const props: Props = { const props: Props = {
item: data, item: data,
onTagSelected: jest.fn(), onTagSelected: jest.fn(),
@ -28,7 +28,7 @@ const setup = (propOverrides?: Partial<Props>, renderMethod = shallow) => {
Object.assign(props, propOverrides); Object.assign(props, propOverrides);
const wrapper = renderMethod(<SearchItem {...props} />); const wrapper = mount(<SearchItem {...props} />);
const instance = wrapper.instance(); const instance = wrapper.instance();
return { return {
@ -39,14 +39,14 @@ const setup = (propOverrides?: Partial<Props>, renderMethod = shallow) => {
describe('SearchItem', () => { describe('SearchItem', () => {
it('should render the item', () => { it('should render the item', () => {
const { wrapper } = setup(); const { wrapper } = setup({});
expect(wrapper.find({ 'aria-label': 'Dashboard search item Test 1' })).toHaveLength(1); expect(wrapper.find({ 'aria-label': 'Dashboard search item Test 1' })).toHaveLength(1);
expect(wrapper.findWhere(comp => comp.type() === 'div' && comp.text() === 'Test 1')).toHaveLength(1); expect(wrapper.findWhere(comp => comp.type() === 'div' && comp.text() === 'Test 1')).toHaveLength(1);
}); });
it("should render item's tags", () => { it("should render item's tags", () => {
// @ts-ignore // @ts-ignore
const { wrapper } = setup({}, mount); const { wrapper } = setup({});
expect(wrapper.find(Tag)).toHaveLength(2); expect(wrapper.find(Tag)).toHaveLength(2);
}); });
}); });

View File

@ -1,11 +1,11 @@
import React, { FC, useCallback, useRef, useEffect, CSSProperties } from 'react'; import React, { FC, useCallback, CSSProperties } from 'react';
import { css, cx } from 'emotion'; import { css, cx } from 'emotion';
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme } from '@grafana/data';
import { e2e } from '@grafana/e2e'; import { e2e } from '@grafana/e2e';
import { Icon, useTheme, TagList, styleMixins, stylesFactory } from '@grafana/ui'; import { useTheme, TagList, styleMixins, stylesFactory } from '@grafana/ui';
import { updateLocation } from 'app/core/reducers/location';
import { DashboardSectionItem, OnToggleChecked } from '../types'; import { DashboardSectionItem, OnToggleChecked } from '../types';
import { SearchCheckbox } from './SearchCheckbox'; import { SearchCheckbox } from './SearchCheckbox';
import { SEARCH_ITEM_HEIGHT, SEARCH_ITEM_MARGIN } from '../constants';
export interface Props { export interface Props {
item: DashboardSectionItem; item: DashboardSectionItem;
@ -20,30 +20,6 @@ const { selectors } = e2e.pages.Dashboards;
export const SearchItem: FC<Props> = ({ item, editable, onToggleChecked, onTagSelected, style }) => { export const SearchItem: FC<Props> = ({ item, editable, onToggleChecked, onTagSelected, style }) => {
const theme = useTheme(); const theme = useTheme();
const styles = getResultsItemStyles(theme); const styles = getResultsItemStyles(theme);
const inputEl = useRef<HTMLInputElement>(null);
useEffect(() => {
const preventDef = (event: MouseEvent) => {
// manually prevent default on TagList click, as doing it via normal onClick doesn't work inside angular
event.preventDefault();
};
if (inputEl.current) {
inputEl.current.addEventListener('click', preventDef);
}
return () => {
inputEl.current!.removeEventListener('click', preventDef);
};
}, []);
const onItemClick = () => {
//Check if one string can be found in the other
if (window.location.pathname.includes(item.url) || item.url.includes(window.location.pathname)) {
updateLocation({
query: { search: null },
partial: true,
});
}
};
const tagSelected = useCallback((tag: string, event: React.MouseEvent<HTMLElement>) => { const tagSelected = useCallback((tag: string, event: React.MouseEvent<HTMLElement>) => {
onTagSelected(tag); onTagSelected(tag);
@ -60,23 +36,21 @@ export const SearchItem: FC<Props> = ({ item, editable, onToggleChecked, onTagSe
); );
return ( return (
<li <div
style={style} style={style}
aria-label={selectors.dashboards(item.title)} aria-label={selectors.dashboards(item.title)}
className={cx(styles.wrapper, { [styles.selected]: item.selected })} className={cx(styles.wrapper, { [styles.selected]: item.selected })}
> >
<SearchCheckbox editable={editable} checked={item.checked} onClick={toggleItem} /> <SearchCheckbox editable={editable} checked={item.checked} onClick={toggleItem} />
<a href={item.url} className={styles.link}> <a href={item.url} className={styles.link}>
<Icon className={styles.icon} name="apps" size="lg" /> <div className={styles.body}>
<div className={styles.body} onClick={onItemClick}>
<span>{item.title}</span> <span>{item.title}</span>
<span className={styles.folderTitle}>{item.folderTitle}</span> <span className={styles.folderTitle}>{item.folderTitle}</span>
</div> </div>
<span ref={inputEl}>
<TagList tags={item.tags} onClick={tagSelected} className={styles.tags} />
</span>
</a> </a>
</li> <TagList tags={item.tags} onClick={tagSelected} className={styles.tags} />
</div>
); );
}; };
@ -85,8 +59,9 @@ const getResultsItemStyles = stylesFactory((theme: GrafanaTheme) => ({
${styleMixins.listItem(theme)}; ${styleMixins.listItem(theme)};
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 ${theme.spacing.sm}; height: ${SEARCH_ITEM_HEIGHT}px;
min-height: 37px; margin-bottom: ${SEARCH_ITEM_MARGIN}px;
padding: 0 ${theme.spacing.md};
:hover { :hover {
cursor: pointer; cursor: pointer;
@ -101,7 +76,6 @@ const getResultsItemStyles = stylesFactory((theme: GrafanaTheme) => ({
justify-content: center; justify-content: center;
flex: 1 1 auto; flex: 1 1 auto;
overflow: hidden; overflow: hidden;
padding: 0 10px;
`, `,
folderTitle: css` folderTitle: css`
color: ${theme.colors.textWeak}; color: ${theme.colors.textWeak};
@ -114,6 +88,7 @@ const getResultsItemStyles = stylesFactory((theme: GrafanaTheme) => ({
margin-left: 10px; margin-left: 10px;
`, `,
tags: css` tags: css`
flex-grow: 0;
justify-content: flex-end; justify-content: flex-end;
@media only screen and (max-width: ${theme.breakpoints.md}) { @media only screen and (max-width: ${theme.breakpoints.md}) {
display: none; display: none;
@ -122,6 +97,7 @@ const getResultsItemStyles = stylesFactory((theme: GrafanaTheme) => ({
link: css` link: css`
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%; flex-shrink: 0;
flex-grow: 1;
`, `,
})); }));

View File

@ -1,12 +1,12 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { css, cx } from 'emotion'; import { css } from 'emotion';
import { FixedSizeList } from 'react-window'; import { FixedSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme } from '@grafana/data';
import { stylesFactory, useTheme, Spinner } from '@grafana/ui'; import { stylesFactory, useTheme, Spinner } from '@grafana/ui';
import { DashboardSection, OnToggleChecked, SearchLayout } from '../types'; import { DashboardSection, OnToggleChecked, SearchLayout } from '../types';
import { getVisibleItems } from '../utils'; import { getVisibleItems } from '../utils';
import { ITEM_HEIGHT } from '../constants'; import { SEARCH_ITEM_HEIGHT, SEARCH_ITEM_MARGIN } from '../constants';
import { SearchItem } from './SearchItem'; import { SearchItem } from './SearchItem';
import { SectionHeader } from './SectionHeader'; import { SectionHeader } from './SectionHeader';
@ -35,18 +35,18 @@ export const SearchResults: FC<Props> = ({
const renderFolders = () => { const renderFolders = () => {
return ( return (
<ul className={styles.wrapper}> <div className={styles.wrapper}>
{results.map(section => { {results.map(section => {
return ( return (
<li aria-label="Search section" className={styles.section} key={section.title}> <div aria-label="Search section" className={styles.section} key={section.title}>
<SectionHeader onSectionClick={onToggleSection} {...{ onToggleChecked, editable, section }} /> <SectionHeader onSectionClick={onToggleSection} {...{ onToggleChecked, editable, section }} />
<ul aria-label="Search items"> <div aria-label="Search items" className={styles.sectionItems}>
{section.expanded && section.items.map(item => <SearchItem key={item.id} {...itemProps} item={item} />)} {section.expanded && section.items.map(item => <SearchItem key={item.id} {...itemProps} item={item} />)}
</ul> </div>
</li> </div>
); );
})} })}
</ul> </div>
); );
}; };
@ -54,24 +54,26 @@ export const SearchResults: FC<Props> = ({
const renderDashboards = () => { const renderDashboards = () => {
return ( return (
<AutoSizer disableWidth> <div className={styles.listModeWrapper}>
{({ height }) => ( <AutoSizer disableWidth>
<FixedSizeList {({ height }) => (
aria-label="Search items" <FixedSizeList
className={styles.wrapper} aria-label="Search items"
innerElementType="ul" className={styles.wrapper}
itemSize={ITEM_HEIGHT} innerElementType="ul"
height={height} itemSize={SEARCH_ITEM_HEIGHT + SEARCH_ITEM_MARGIN}
itemCount={items.length} height={height}
width="100%" itemCount={items.length}
> width="100%"
{({ index, style }) => { >
const item = items[index]; {({ index, style }) => {
return <SearchItem key={item.id} {...itemProps} item={item} style={style} />; const item = items[index];
}} return <SearchItem key={item.id} {...itemProps} item={item} style={style} />;
</FixedSizeList> }}
)} </FixedSizeList>
</AutoSizer> )}
</AutoSizer>
</div>
); );
}; };
@ -80,24 +82,28 @@ export const SearchResults: FC<Props> = ({
} else if (!results || !results.length) { } else if (!results || !results.length) {
return <h6>No dashboards matching your query were found.</h6>; return <h6>No dashboards matching your query were found.</h6>;
} }
return ( return (
<div className={cx('results-container', styles.resultsContainer)}> <div className={styles.resultsContainer}>{layout !== SearchLayout.List ? renderFolders() : renderDashboards()}</div>
{layout !== SearchLayout.List ? renderFolders() : renderDashboards()}
</div>
); );
}; };
const getSectionStyles = stylesFactory((theme: GrafanaTheme) => { const getSectionStyles = stylesFactory((theme: GrafanaTheme) => {
const { xs, sm, md } = theme.spacing; const { md } = theme.spacing;
return { return {
wrapper: css` wrapper: css`
list-style: none; display: flex;
flex-direction: column;
`, `,
section: css` section: css`
display: flex;
flex-direction: column;
background: ${theme.colors.panelBg}; background: ${theme.colors.panelBg};
border-bottom: solid 1px ${theme.isLight ? theme.palette.gray95 : theme.palette.gray25}; border-bottom: solid 1px ${theme.colors.border2};
padding: 0px ${xs} ${xs}; `,
margin-bottom: 3px; sectionItems: css`
margin: 0 24px 0 32px;
`, `,
spinner: css` spinner: css`
display: flex; display: flex;
@ -106,14 +112,18 @@ const getSectionStyles = stylesFactory((theme: GrafanaTheme) => {
min-height: 100px; min-height: 100px;
`, `,
resultsContainer: css` resultsContainer: css`
padding: ${sm};
position: relative; position: relative;
flex-grow: 10; flex-grow: 10;
margin-bottom: ${md}; margin-bottom: ${md};
background: ${theme.palette.gray10}; background: ${theme.colors.bg1};
border: 1px solid ${theme.palette.gray15}; border: 1px solid ${theme.colors.border1};
border-radius: 3px; border-radius: 3px;
height: 100%; height: 100%;
`, `,
listModeWrapper: css`
position: relative;
height: 100%;
padding: ${md};
`,
}; };
}); });

View File

@ -19,7 +19,7 @@ export const SectionHeader: FC<SectionHeaderProps> = ({
editable = false, editable = false,
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const styles = getSectionHeaderStyles(theme, section.selected); const styles = getSectionHeaderStyles(theme, section.selected, editable);
const onSectionExpand = () => { const onSectionExpand = () => {
onSectionClick(section); onSectionClick(section);
@ -39,7 +39,10 @@ export const SectionHeader: FC<SectionHeaderProps> = ({
return ( return (
<div className={styles.wrapper} onClick={onSectionExpand}> <div className={styles.wrapper} onClick={onSectionExpand}>
<SearchCheckbox editable={editable} checked={section.checked} onClick={onSectionChecked} /> <SearchCheckbox editable={editable} checked={section.checked} onClick={onSectionChecked} />
<Icon className={styles.icon} name={section.icon as IconName} />
<div className={styles.icon}>
<Icon name={section.icon as IconName} />
</div>
<span className={styles.text}>{section.title}</span> <span className={styles.text}>{section.title}</span>
{section.url && ( {section.url && (
@ -52,15 +55,15 @@ export const SectionHeader: FC<SectionHeaderProps> = ({
); );
}; };
const getSectionHeaderStyles = stylesFactory((theme: GrafanaTheme, selected = false) => { const getSectionHeaderStyles = stylesFactory((theme: GrafanaTheme, selected = false, editable: boolean) => {
const { sm, xs } = theme.spacing; const { sm } = theme.spacing;
return { return {
wrapper: cx( wrapper: cx(
css` css`
display: flex; display: flex;
align-items: center; align-items: center;
font-size: ${theme.typography.size.base}; font-size: ${theme.typography.size.base};
padding: ${sm} ${xs} ${xs}; padding: 12px;
color: ${theme.colors.textWeak}; color: ${theme.colors.textWeak};
&:hover, &:hover,
@ -78,7 +81,7 @@ const getSectionHeaderStyles = stylesFactory((theme: GrafanaTheme, selected = fa
{ selected } { selected }
), ),
icon: css` icon: css`
width: 43px; padding: 0 ${sm} 0 ${editable ? 0 : sm};
`, `,
text: css` text: css`
flex-grow: 1; flex-grow: 1;

View File

@ -1,4 +1,5 @@
export const NO_ID_SECTIONS = ['Recent', 'Starred']; export const NO_ID_SECTIONS = ['Recent', 'Starred'];
// Height of the search result item // Height of the search result item
export const ITEM_HEIGHT = 40; export const SEARCH_ITEM_HEIGHT = 48;
export const SEARCH_ITEM_MARGIN = 4;
export const DEFAULT_SORT = { label: 'A-Z', value: 'alpha-asc' }; export const DEFAULT_SORT = { label: 'A-Z', value: 'alpha-asc' };

View File

@ -20,9 +20,14 @@
white-space: nowrap; white-space: nowrap;
border-radius: 3px; border-radius: 3px;
text-shadow: none; text-shadow: none;
font-size: 13px; font-size: 12px;
padding: 0px 6px; padding: 0px 6px;
border: 1px solid lighten($purple, 10%); line-height: 20px;
height: 20px;
svg {
margin-bottom: 0;
}
.icon-tag { .icon-tag {
position: relative; position: relative;

View File

@ -46,30 +46,30 @@
} }
.tag-filter { .tag-filter {
line-height: 22px;
flex-grow: 1; flex-grow: 1;
.label-tag { .label-tag {
margin-left: 6px; margin-left: 6px;
font-size: 11px;
cursor: pointer; cursor: pointer;
.fa.fa-remove { .fa.fa-remove {
margin-right: 3px; margin-right: 3px;
} }
} }
}
.tag-filter-option { .tag-filter-option {
position: relative; position: relative;
text-align: left; text-align: left;
width: 100%; width: 100%;
display: block; display: block;
border-radius: 0; border-radius: 0;
} cursor: pointer;
padding: 2px 0;
}
.tag-count-label { .tag-count-label {
margin-left: 3px; margin-left: 3px;
}
} }
.tag-filter-values { .tag-filter-values {