Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QueryList itemRenderer prop & renderItem method #1877

Merged
merged 18 commits into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,22 @@
* Licensed under the terms of the LICENSE file distributed with this project.
*/

import { Classes, MenuItem } from "@blueprintjs/core";
import { ItemRenderer } from "@blueprintjs/select";
import * as classNames from "classnames";
import * as React from "react";

export interface IFilm {
/** Title of film. */
title: string;
/** Release year. */
year: number;
/** IMDb ranking. */
rank: number;
}

/** Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top */
export const TOP_100_FILMS = [
export const TOP_100_FILMS: IFilm[] = [
{ title: "The Shawshank Redemption", year: 1994 },
{ title: "The Godfather", year: 1972 },
{ title: "The Godfather: Part II", year: 1974 },
Expand Down Expand Up @@ -108,4 +122,31 @@ export const TOP_100_FILMS = [
{ title: "Monty Python and the Holy Grail", year: 1975 },
].map((m, index) => ({ ...m, rank: index + 1 }));

export type Film = typeof TOP_100_FILMS[0];
export const renderFilm: ItemRenderer<IFilm> = (film, { handleClick, modifiers }) => {
if (!modifiers.filtered) {
return null;
}
const classes = classNames({
[Classes.ACTIVE]: modifiers.active,
[Classes.INTENT_PRIMARY]: modifiers.active,
});
return (
<MenuItem
className={classes}
label={film.year.toString()}
key={film.rank}
onClick={handleClick}
text={`${film.rank}. ${film.title}`}
/>
);
};

export function filterFilm(query: string, film: IFilm) {
return `${film.rank}. ${film.title.toLowerCase()} ${film.year}`.indexOf(query.toLowerCase()) >= 0;
}

export const filmSelectProps = {
itemPredicate: filterFilm,
itemRenderer: renderFilm,
items: TOP_100_FILMS,
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import * as React from "react";

import { Classes, Intent, ITagProps, MenuItem, Switch } from "@blueprintjs/core";
import { BaseExample } from "@blueprintjs/docs-theme";
import { ISelectItemRendererProps, MultiSelect } from "@blueprintjs/select";
import { Film, TOP_100_FILMS } from "./data";
import { ItemRenderer, MultiSelect } from "@blueprintjs/select";
import { filmSelectProps, IFilm, TOP_100_FILMS } from "./films";

const FilmMultiSelect = MultiSelect.ofType<Film>();
const FilmMultiSelect = MultiSelect.ofType<IFilm>();

const INTENTS = [Intent.NONE, Intent.PRIMARY, Intent.SUCCESS, Intent.DANGER, Intent.WARNING];

export interface IMultiSelectExampleState {
films?: Film[];
films?: IFilm[];
hasInitialContent?: boolean;
intent?: boolean;
openOnKeyDown?: boolean;
Expand Down Expand Up @@ -59,10 +59,9 @@ export class MultiSelectExample extends BaseExample<IMultiSelectExampleState> {

return (
<FilmMultiSelect
{...filmSelectProps}
{...flags}
initialContent={initialContent}
items={TOP_100_FILMS}
itemPredicate={this.filterFilm}
itemRenderer={this.renderFilm}
noResults={<MenuItem disabled={true} text="No results." />}
onItemSelect={this.handleFilmSelect}
Expand Down Expand Up @@ -119,14 +118,16 @@ export class MultiSelectExample extends BaseExample<IMultiSelectExampleState> {
];
}

private renderTag = (film: Film) => {
return film.title;
};
private renderTag = (film: IFilm) => film.title;

private renderFilm = ({ handleClick, isActive, item: film }: ISelectItemRendererProps<Film>) => {
private renderFilm: ItemRenderer<IFilm> = (film, { modifiers, handleClick }) => {
if (!modifiers.filtered) {
return null;
}
// NOTE: not using Films.itemRenderer here so we can set icons.
const classes = classNames({
[Classes.ACTIVE]: isActive,
[Classes.INTENT_PRIMARY]: isActive,
[Classes.ACTIVE]: modifiers.active,
[Classes.INTENT_PRIMARY]: modifiers.active,
});

return (
Expand All @@ -142,31 +143,27 @@ export class MultiSelectExample extends BaseExample<IMultiSelectExampleState> {
);
};

private filterFilm(query: string, film: Film, index: number) {
return `${index + 1}. ${film.title.toLowerCase()} ${film.year}`.indexOf(query.toLowerCase()) >= 0;
}

private handleTagRemove = (_tag: string, index: number) => {
this.deselectFilm(index);
};

private getSelectedFilmIndex(film: Film) {
private getSelectedFilmIndex(film: IFilm) {
return this.state.films.indexOf(film);
}

private isFilmSelected(film: Film) {
private isFilmSelected(film: IFilm) {
return this.getSelectedFilmIndex(film) !== -1;
}

private selectFilm(film: Film) {
private selectFilm(film: IFilm) {
this.setState({ films: [...this.state.films, film] });
}

private deselectFilm(index: number) {
this.setState({ films: this.state.films.filter((_film, i) => i !== index) });
}

private handleFilmSelect = (film: Film) => {
private handleFilmSelect = (film: IFilm) => {
if (!this.isFilmSelected(film)) {
this.selectFilm(film);
} else {
Expand Down
45 changes: 6 additions & 39 deletions packages/docs-app/src/examples/select-examples/omnibarExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,14 @@
* Licensed under the terms of the LICENSE file distributed with this project.
*/

import * as classNames from "classnames";
import * as React from "react";

import {
Button,
Classes,
Hotkey,
Hotkeys,
HotkeysTarget,
MenuItem,
Position,
Switch,
Toaster,
} from "@blueprintjs/core";
import { Button, Hotkey, Hotkeys, HotkeysTarget, MenuItem, Position, Switch, Toaster } from "@blueprintjs/core";
import { BaseExample, handleBooleanChange } from "@blueprintjs/docs-theme";
import { ISelectItemRendererProps, Omnibar } from "@blueprintjs/select";
import { Film, TOP_100_FILMS } from "./data";
import { Omnibar } from "@blueprintjs/select";
import { filmSelectProps, IFilm } from "./films";

const FilmOmnibar = Omnibar.ofType<Film>();
const FilmOmnibar = Omnibar.ofType<IFilm>();

export interface IOmnibarExampleState {
isOpen: boolean;
Expand Down Expand Up @@ -61,10 +50,8 @@ export class OmnibarExample extends BaseExample<IOmnibarExampleState> {
return (
<div>
<FilmOmnibar
{...filmSelectProps}
{...this.state}
items={TOP_100_FILMS}
itemPredicate={this.filterFilm}
itemRenderer={this.renderFilm}
noResults={<MenuItem disabled={true} text="No results." />}
onItemSelect={this.handleItemSelect}
onClose={this.handleClose}
Expand Down Expand Up @@ -99,31 +86,11 @@ export class OmnibarExample extends BaseExample<IOmnibarExampleState> {
];
}

private renderFilm({ handleClick, isActive, item: film }: ISelectItemRendererProps<Film>) {
const classes = classNames({
[Classes.ACTIVE]: isActive,
[Classes.INTENT_PRIMARY]: isActive,
});
return (
<MenuItem
className={classes}
label={film.year.toString()}
key={film.rank}
onClick={handleClick}
text={`${film.rank}. ${film.title}`}
/>
);
}

private filterFilm(query: string, film: Film, index: number) {
return `${index + 1}. ${film.title.toLowerCase()} ${film.year}`.indexOf(query.toLowerCase()) >= 0;
}

private handleClick = (_event: React.MouseEvent<HTMLElement>) => {
this.setState({ isOpen: true });
};

private handleItemSelect = (film: Film) => {
private handleItemSelect = (film: IFilm) => {
this.setState({ isOpen: false });

this.toaster.show({
Expand Down
37 changes: 7 additions & 30 deletions packages/docs-app/src/examples/select-examples/selectExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@
* Licensed under the terms of the LICENSE file distributed with this project.
*/

import * as classNames from "classnames";
import * as React from "react";

import { Button, Classes, MenuItem, Switch } from "@blueprintjs/core";
import { Button, MenuItem, Switch } from "@blueprintjs/core";
import { BaseExample } from "@blueprintjs/docs-theme";
import { ISelectItemRendererProps, Select } from "@blueprintjs/select";
import { Film, TOP_100_FILMS } from "./data";
import { Select } from "@blueprintjs/select";
import { filmSelectProps, IFilm, TOP_100_FILMS } from "./films";

const FilmSelect = Select.ofType<Film>();
const FilmSelect = Select.ofType<IFilm>();

export interface ISelectExampleState {
film?: Film;
film?: IFilm;
filterable?: boolean;
hasInitialContent?: boolean;
minimal?: boolean;
Expand Down Expand Up @@ -53,12 +52,10 @@ export class SelectExample extends BaseExample<ISelectExampleState> {

return (
<FilmSelect
{...filmSelectProps}
{...flags}
disabled={disabled}
initialContent={initialContent}
items={TOP_100_FILMS}
itemPredicate={this.filterFilm}
itemRenderer={this.renderFilm}
noResults={<MenuItem disabled={true} text="No results." />}
onItemSelect={this.handleValueChange}
popoverProps={{ minimal }}
Expand Down Expand Up @@ -111,27 +108,7 @@ export class SelectExample extends BaseExample<ISelectExampleState> {
];
}

private renderFilm({ handleClick, isActive, item: film }: ISelectItemRendererProps<Film>) {
const classes = classNames({
[Classes.ACTIVE]: isActive,
[Classes.INTENT_PRIMARY]: isActive,
});
return (
<MenuItem
className={classes}
label={film.year.toString()}
key={film.rank}
onClick={handleClick}
text={`${film.rank}. ${film.title}`}
/>
);
}

private filterFilm(query: string, film: Film, index: number) {
return `${index + 1}. ${film.title.toLowerCase()} ${film.year}`.indexOf(query.toLowerCase()) >= 0;
}

private handleValueChange = (film: Film) => this.setState({ film });
private handleValueChange = (film: IFilm) => this.setState({ film });

private handleSwitchChange(prop: keyof ISelectExampleState) {
return (event: React.FormEvent<HTMLInputElement>) => {
Expand Down
39 changes: 7 additions & 32 deletions packages/docs-app/src/examples/select-examples/suggestExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@
* Licensed under the terms of the LICENSE file distributed with this project.
*/

import * as classNames from "classnames";
import * as React from "react";

import { Classes, MenuItem, Switch } from "@blueprintjs/core";
import { BaseExample } from "@blueprintjs/docs-theme";
import { ISelectItemRendererProps, Suggest } from "@blueprintjs/select";
import { Film, TOP_100_FILMS } from "./data";
import { Suggest } from "@blueprintjs/select";
import { filmSelectProps, IFilm, TOP_100_FILMS } from "./films";

const FilmSuggest = Suggest.ofType<Film>();
const FilmSuggest = Suggest.ofType<IFilm>();

export interface ISuggestExampleState {
closeOnSelect?: boolean;
film?: Film;
film?: IFilm;
minimal?: boolean;
openOnKeyDown?: boolean;
}
Expand All @@ -37,11 +36,9 @@ export class SuggestExample extends BaseExample<ISuggestExampleState> {
const { film, minimal, ...flags } = this.state;
return (
<FilmSuggest
{...filmSelectProps}
{...flags}
inputValueRenderer={this.renderInputValue}
items={TOP_100_FILMS}
itemPredicate={this.filterFilm}
itemRenderer={this.renderFilm}
noResults={<MenuItem disabled={true} text="No results." />}
onItemSelect={this.handleValueChange}
popoverProps={{ popoverClassName: minimal ? Classes.MINIMAL : "" }}
Expand Down Expand Up @@ -74,31 +71,9 @@ export class SuggestExample extends BaseExample<ISuggestExampleState> {
];
}

private renderFilm({ handleClick, isActive, item: film }: ISelectItemRendererProps<Film>) {
const classes = classNames({
[Classes.ACTIVE]: isActive,
[Classes.INTENT_PRIMARY]: isActive,
});
return (
<MenuItem
className={classes}
label={film.year.toString()}
key={film.rank}
onClick={handleClick}
text={`${film.rank}. ${film.title}`}
/>
);
}

private renderInputValue = (film: Film) => {
return film.title;
};

private filterFilm(query: string, film: Film, index: number) {
return `${index + 1}. ${film.title.toLowerCase()} ${film.year}`.indexOf(query.toLowerCase()) >= 0;
}
private renderInputValue = (film: IFilm) => film.title;

private handleValueChange = (film: Film) => this.setState({ film });
private handleValueChange = (film: IFilm) => this.setState({ film });

private handleSwitchChange(prop: keyof ISuggestExampleState) {
return (event: React.FormEvent<HTMLInputElement>) => {
Expand Down
1 change: 1 addition & 0 deletions packages/select/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

export * from "./omnibar/omnibar";
export * from "./query-list/itemRenderer";
export * from "./query-list/queryList";
export * from "./select/multiSelect";
export * from "./select/select";
Expand Down
Loading