-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
[Labs] new TagInput component! #1232
Changes from 7 commits
1435bb6
48f7c71
c9d67b4
7bebcd9
0de6fef
c6545df
5166bc1
41a9e1f
e5ccb76
f219fda
7275e31
6e74699
960f305
d441a73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,25 +14,34 @@ import { isFunction } from "../../common/utils"; | |
|
||
import * as Classes from "../../common/classes"; | ||
|
||
export interface ITagProps extends IProps, IIntentProps, React.HTMLProps<HTMLSpanElement> { | ||
export interface ITagProps extends IProps, IIntentProps, React.HTMLAttributes<Tag> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💥 |
||
/** | ||
* If set to `true`, the tag will display in an active state. | ||
* This is equivalent to setting `className="pt-active"`. | ||
* @default false | ||
*/ | ||
active?: boolean; | ||
|
||
/** | ||
* Click handler for remove button. | ||
* Button will only be rendered if this prop is defined. | ||
*/ | ||
onRemove?: (e: React.MouseEvent<HTMLSpanElement>) => void; | ||
onRemove?: (e: React.MouseEvent<HTMLButtonElement>) => void; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. previous type was incorrect, as the |
||
} | ||
|
||
@PureRender | ||
export class Tag extends React.Component<ITagProps, {}> { | ||
public static displayName = "Blueprint.Tag"; | ||
|
||
public render() { | ||
const { className, intent, onRemove } = this.props; | ||
const { active, className, intent, onRemove } = this.props; | ||
const tagClasses = classNames(Classes.TAG, Classes.intentClass(intent), { | ||
[Classes.TAG_REMOVABLE]: onRemove != null, | ||
[Classes.ACTIVE]: active, | ||
}, className); | ||
const button = | ||
isFunction(onRemove) ? <button type="button" className={Classes.TAG_REMOVE} onClick={onRemove} /> : undefined; | ||
const button = isFunction(onRemove) | ||
? <button type="button" className={Classes.TAG_REMOVE} onClick={onRemove} /> | ||
: undefined; | ||
|
||
return ( | ||
<span {...removeNonHTMLProps(this.props)} className={tagClasses}> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ | |
*/ | ||
|
||
export * from "./selectExample"; | ||
export * from "./tagInputExample"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright 2017 Palantir Technologies, Inc. All rights reserved. | ||
* Licensed under the BSD-3 License as modified (the “License”); you may obtain a copy | ||
* of the license at https://github.com/palantir/blueprint/blob/master/LICENSE | ||
* and https://github.com/palantir/blueprint/blob/master/PATENTS | ||
*/ | ||
|
||
import * as React from "react"; | ||
|
||
import { Classes, Intent, ITagProps, Switch } from "@blueprintjs/core"; | ||
import { BaseExample, handleBooleanChange } from "@blueprintjs/docs"; | ||
import { TagInput } from "../src/tagInput/tagInput"; | ||
|
||
const INTENTS = [Intent.NONE, Intent.PRIMARY, Intent.SUCCESS, Intent.DANGER, Intent.WARNING]; | ||
|
||
export interface ITagInputExampleState { | ||
intent?: boolean; | ||
large?: boolean; | ||
minimal?: boolean; | ||
values?: string[]; | ||
} | ||
|
||
export class TagInputExample extends BaseExample<ITagInputExampleState> { | ||
public state: ITagInputExampleState = { | ||
intent: false, | ||
large: false, | ||
minimal: false, | ||
values: ["Albert", "Bartholomew", "Casper"], | ||
}; | ||
|
||
private handleIntentChange = handleBooleanChange((intent) => this.setState({ intent })); | ||
private handleLargeChange = handleBooleanChange((large) => this.setState({ large })); | ||
private handleMinimalChange = handleBooleanChange((minimal) => this.setState({ minimal })); | ||
|
||
protected renderExample() { | ||
const { large, values } = this.state; | ||
|
||
// define a new function every time so switch changes will cause it to re-render | ||
// NOTE: avoid this pattern in your app (use this.getTagProps instead); this is only for | ||
// example purposes!! | ||
const getTagProps = (_v: string, index: number): ITagProps => ({ | ||
className: this.state.minimal ? Classes.MINIMAL : "", | ||
intent: this.state.intent ? INTENTS[index % INTENTS.length] : Intent.NONE, | ||
}); | ||
|
||
return ( | ||
<TagInput | ||
className={large ? Classes.LARGE : ""} | ||
onAdd={this.handleAdd} | ||
onRemove={this.handleRemove} | ||
tagProps={getTagProps} | ||
values={values} | ||
/> | ||
); | ||
} | ||
|
||
protected renderOptions() { | ||
return [ | ||
[ | ||
<Switch | ||
checked={this.state.large} | ||
label="Large" | ||
key="large" | ||
onChange={this.handleLargeChange} | ||
/>, | ||
], [ | ||
<label key="heading" className={Classes.LABEL}>Tag props</label>, | ||
<Switch | ||
checked={this.state.minimal} | ||
label="Use minimal tags" | ||
key="minimal" | ||
onChange={this.handleMinimalChange} | ||
/>, | ||
<Switch | ||
checked={this.state.intent} | ||
label="Cycle through intents" | ||
key="intent" | ||
onChange={this.handleIntentChange} | ||
/>, | ||
], | ||
]; | ||
} | ||
|
||
private handleAdd = (newValue: string) => { | ||
this.setState({ values: [...this.state.values, newValue] }); | ||
} | ||
private handleRemove = (_removedValue: string, removedIndex: number) => { | ||
this.setState({ values: this.state.values.filter((_, i) => i !== removedIndex) }); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,4 @@ | |
|
||
export * from "./inputList"; | ||
export * from "./select"; | ||
export * from "./tagInput/tagInput"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright 2017 Palantir Technologies, Inc. All rights reserved. | ||
* Licensed under the BSD-3 License as modified (the “License”); you may obtain a copy | ||
* of the license at https://github.com/palantir/blueprint/blob/master/LICENSE | ||
* and https://github.com/palantir/blueprint/blob/master/PATENTS | ||
*/ | ||
|
||
@import "~@blueprintjs/core/src/common/variables"; | ||
@import "~@blueprintjs/core/src/components/forms/common"; | ||
@import "~@blueprintjs/core/src/components/tag/common"; | ||
|
||
$input-padding: ($pt-input-height - $tag-height) / 2; | ||
|
||
.pt-tag-input { | ||
display: flex; | ||
flex-wrap: wrap; | ||
cursor: text; | ||
height: auto; | ||
padding: $input-padding 0 0 $input-padding; | ||
|
||
.pt-tag { | ||
margin: 0 $input-padding $input-padding 0; | ||
} | ||
|
||
.pt-input-ghost { | ||
// input fills remaining line | ||
flex: 1 1 auto; | ||
margin-bottom: $input-padding; | ||
// essentially a min-width, cuz flex allows it to grow or shrink: | ||
width: $pt-grid-size * 10; | ||
line-height: $tag-height; | ||
} | ||
|
||
&.pt-large { | ||
height: auto; | ||
|
||
.pt-input-ghost { | ||
line-height: $tag-height-large; | ||
} | ||
} | ||
|
||
&.pt-active { | ||
box-shadow: input-transition-shadow($input-shadow-color-focus, true), $input-box-shadow-focus; | ||
background-color: $input-background-color; | ||
} | ||
} | ||
|
||
// TODO: this is probably a useful modifier that we should pull into core, and use in EditableText | ||
.pt-input-ghost { | ||
// reset browser input styles (we're using an input solely because you can type in it) | ||
border: none; | ||
box-shadow: none; | ||
background: none; | ||
padding: 0; | ||
|
||
&:focus { | ||
// remove focus state too | ||
// stylelint-disable-next-line declaration-no-important | ||
outline: none !important; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops