From 7f6fca4f7269aa1389d5bbe550f8c3d41ad69616 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 13:57:13 -0800 Subject: [PATCH 01/14] sweet pt-flex-container mixin --- packages/core/src/common/_flex.scss | 42 +++++++++++++++++++++++++++ packages/core/src/common/_mixins.scss | 1 + 2 files changed, 43 insertions(+) create mode 100644 packages/core/src/common/_flex.scss diff --git a/packages/core/src/common/_flex.scss b/packages/core/src/common/_flex.scss new file mode 100644 index 0000000000..8097023097 --- /dev/null +++ b/packages/core/src/common/_flex.scss @@ -0,0 +1,42 @@ +// Copyright 2018 Palantir Technologies, Inc. All rights reserved. +// Licensed under the terms of the LICENSE file distributed with this project. + +// this element becomes a flex container in the given direction. +// supply `$margin` to put space between each child. +// supply `$inline: inline` to use `display: flex-inline`. +// customize `flex: 1 1` child selector with $fill. +@mixin pt-flex-container($direction: row, $margin: none, $inline: none, $fill: ".pt-fill") { + @if $inline == inline or $inline == true { + display: inline-flex; + } @else { + display: flex; + } + flex-direction: $direction; + + > * { + flex: 0 0 auto; + } + + > #{$fill} { + flex: 1 1 auto; + } + + @if $margin != none { + @include pt-flex-margin($direction, $margin); + } +} + +// applies margin along axis of direction between every direct child, with no margins on either end. +// $direction: row | column +// $margin: margin[-right|-bottom] value +@mixin pt-flex-margin($direction, $margin) { + // CSS API support + &::before, + > :not(:last-child) { + @if $direction == row { + margin-right: $margin; + } @else { + margin-bottom: $margin; + } + } +} diff --git a/packages/core/src/common/_mixins.scss b/packages/core/src/common/_mixins.scss index 108071a7d9..dec4397074 100644 --- a/packages/core/src/common/_mixins.scss +++ b/packages/core/src/common/_mixins.scss @@ -2,6 +2,7 @@ // Licensed under the terms of the LICENSE file distributed with this project. @import "colors"; +@import "flex"; $pt-intent-colors: ( "primary": $pt-intent-primary, From 71030fe846bae75ab2de0b7781e3a55a118363a6 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 16:34:00 -0800 Subject: [PATCH 02/14] break margin logic into default, last --- packages/core/src/common/_flex.scss | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/core/src/common/_flex.scss b/packages/core/src/common/_flex.scss index 8097023097..5a7964bcdf 100644 --- a/packages/core/src/common/_flex.scss +++ b/packages/core/src/common/_flex.scss @@ -30,13 +30,18 @@ // $direction: row | column // $margin: margin[-right|-bottom] value @mixin pt-flex-margin($direction, $margin) { + $margin-prop: if($direction == row, margin-right, margin-bottom); + // CSS API support &::before, - > :not(:last-child) { - @if $direction == row { - margin-right: $margin; - } @else { - margin-bottom: $margin; - } + // space after all children + > * { + #{$margin-prop}: $margin; + } + + // remove space after last child + &:empty::before, + > :last-child { + #{$margin-prop}: 0; } } From 06f34763ca36dbb74547a1420b842c9e44e42601 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 16:31:58 -0800 Subject: [PATCH 03/14] add "Icons only" switch to button and button group examples to streamline testing of states --- .../core-examples/buttonGroupExample.tsx | 26 +++++++--- .../examples/core-examples/buttonsExample.tsx | 50 +++++++++++-------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx b/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx index 83a17618ce..2f8f639808 100644 --- a/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx +++ b/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx @@ -10,35 +10,39 @@ import { AnchorButton, Button, ButtonGroup, Switch } from "@blueprintjs/core"; import { BaseExample, handleBooleanChange } from "@blueprintjs/docs-theme"; export interface IButtonGroupExampleState { - fill?: boolean; - minimal?: boolean; - large?: boolean; - vertical?: boolean; + fill: boolean; + iconOnly: boolean; + minimal: boolean; + large: boolean; + vertical: boolean; } export class ButtonGroupExample extends BaseExample { public state: IButtonGroupExampleState = { fill: false, + iconOnly: false, large: false, minimal: false, vertical: false, }; private handleFillChange = handleBooleanChange(fill => this.setState({ fill })); + private handleIconOnlyChange = handleBooleanChange(iconOnly => this.setState({ iconOnly })); private handleLargeChange = handleBooleanChange(large => this.setState({ large })); private handleMinimalChange = handleBooleanChange(minimal => this.setState({ minimal })); private handleVerticalChange = handleBooleanChange(vertical => this.setState({ vertical })); protected renderExample() { + const { iconOnly, ...bgProps } = this.state; // have the container take up the full-width if `fill` is true; // otherwise, disable full-width styles to keep a vertical button group // from taking up the full width. const style: React.CSSProperties = { flexGrow: this.state.fill ? 1 : undefined }; return ( - - - - Options + + + + {!iconOnly && "Options"} ); } @@ -60,6 +64,12 @@ export class ButtonGroupExample extends BaseExample { label="Vertical" onChange={this.handleVerticalChange} />, + , ], ]; } diff --git a/packages/docs-app/src/examples/core-examples/buttonsExample.tsx b/packages/docs-app/src/examples/core-examples/buttonsExample.tsx index dcb2256896..305cd61366 100644 --- a/packages/docs-app/src/examples/core-examples/buttonsExample.tsx +++ b/packages/docs-app/src/examples/core-examples/buttonsExample.tsx @@ -13,19 +13,22 @@ import { BaseExample, handleBooleanChange, handleStringChange } from "@blueprint import { IntentSelect } from "./common/intentSelect"; export interface IButtonsExampleState { - active?: boolean; - disabled?: boolean; - intent?: Intent; - loading?: boolean; - large?: boolean; - minimal?: boolean; - wiggling?: boolean; + active: boolean; + disabled: boolean; + iconOnly: boolean; + intent: Intent; + loading: boolean; + large: boolean; + minimal: boolean; + wiggling: boolean; } export class ButtonsExample extends BaseExample { public state: IButtonsExampleState = { active: false, disabled: false, + iconOnly: false, + intent: Intent.NONE, large: false, loading: false, minimal: false, @@ -34,6 +37,7 @@ export class ButtonsExample extends BaseExample { private handleActiveChange = handleBooleanChange(active => this.setState({ active })); private handleDisabledChange = handleBooleanChange(disabled => this.setState({ disabled })); + private handleIconOnlyChange = handleBooleanChange(iconOnly => this.setState({ iconOnly })); private handleLargeChange = handleBooleanChange(large => this.setState({ large })); private handleLoadingChange = handleBooleanChange(loading => this.setState({ loading })); private handleMinimalChange = handleBooleanChange(minimal => this.setState({ minimal })); @@ -46,9 +50,10 @@ export class ButtonsExample extends BaseExample { } protected renderExample() { + const { large, minimal, iconOnly, wiggling, ...buttonProps } = this.state; const classes = classNames({ - [Classes.LARGE]: this.state.large, - [Classes.MINIMAL]: this.state.minimal, + [Classes.LARGE]: large, + [Classes.MINIMAL]: minimal, }); return ( @@ -59,14 +64,12 @@ export class ButtonsExample extends BaseExample {
AnchorButton @@ -74,15 +77,12 @@ export class ButtonsExample extends BaseExample {
@@ -116,7 +116,15 @@ export class ButtonsExample extends BaseExample { onChange={this.handleMinimalChange} />, ], - [], + [ + , + , + ], ]; } From 5902e4b7b556529838c7e34c5492e58205b064bb Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 16:35:01 -0800 Subject: [PATCH 04/14] pt-button-base() is inline flex row --- packages/core/src/components/button/_common.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/components/button/_common.scss b/packages/core/src/components/button/_common.scss index 7b478b87cd..a5f96aee26 100644 --- a/packages/core/src/components/button/_common.scss +++ b/packages/core/src/components/button/_common.scss @@ -94,12 +94,14 @@ $button-intents: ( ) !default; @mixin pt-button-base() { - display: inline-block; + @include pt-flex-container(row, $button-icon-spacing, $inline: inline); + align-items: center; + justify-content: center; + border: none; border-radius: $pt-border-radius; cursor: pointer; padding: $button-padding; - vertical-align: middle; font-family: inherit; font-size: $pt-font-size; } From 666d6649d9ed2b75413dad2ac64bc7f17c169fed Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 16:36:33 -0800 Subject: [PATCH 05/14] remove button margins (applied by flex mixin now) and fix icon-only --- .../core/src/components/button/_button.scss | 57 ++++++------------- 1 file changed, 17 insertions(+), 40 deletions(-) diff --git a/packages/core/src/components/button/_button.scss b/packages/core/src/components/button/_button.scss index 41c185afed..76a7dc9e69 100644 --- a/packages/core/src/components/button/_button.scss +++ b/packages/core/src/components/button/_button.scss @@ -28,6 +28,10 @@ Styleguide pt-button @include pt-button-base(); @include pt-button-height($pt-button-height); + &:empty { + padding: 0; + } + &:disabled, &.pt-disabled { cursor: not-allowed; @@ -81,19 +85,6 @@ Styleguide pt-button Styleguide pt-button.pt-icon */ - &[class*="pt-icon-"]::before { - @include pt-icon(); - margin-right: $button-icon-spacing; - color: $pt-icon-color; - } - - // icon-only button - .pt-icon:first-child:last-child { - // center icon horizontally. this works for large buttons too. - $icon-margin-offset: -($pt-button-height - $pt-icon-size-standard) / 2; - margin-right: $icon-margin-offset; - margin-left: $icon-margin-offset; - } /* Advanced icon usage @@ -120,15 +111,23 @@ Styleguide pt-button Styleguide pt-button.pt-icon-advanced */ + &[class*="pt-icon-"] { + &::before { + @include pt-icon(); + color: $pt-icon-color; + } + } #{$icon-classes} { - margin-right: $button-icon-spacing; color: $pt-icon-color; + } - &.pt-align-right { - margin-right: 0; - margin-left: $button-icon-spacing; - } + // button with SVG icon only (no text or children) + .pt-icon:first-child:last-child, + // if loading, then it contains exactly [spinner, icon] + .pt-spinner + .pt-icon:last-child { + // center icon horizontally. this works for large buttons too. + margin: 0 (-($pt-button-height - $pt-icon-size-standard) / 2); } .pt-dark & { @@ -191,34 +190,12 @@ Styleguide pt-button &.pt-large, .pt-large & { @include pt-button-large(); - - &[class*="pt-icon-"]::before { - margin-right: $button-icon-spacing-large; - } - - #{$icon-classes} { - margin-right: $button-icon-spacing-large; - - &.pt-align-right { - margin-right: 0; - margin-left: $button-icon-spacing-large; - } - } } &.pt-small, .pt-small & { @include pt-button-small(); } - - // ensure icon button with no text is a perfect square - &[class*="pt-icon-"]:empty { - padding: 0; - - &::before { - margin-right: 0; - } - } } a.pt-button { From 00291df61f06ff402c76f5276cfd0d0298d39a71 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 16:37:36 -0800 Subject: [PATCH 06/14] Button renders text and children inside .pt-fill container - vertical buttons automatically align left and push rightIcon to right side! - loading easily hides this container without needing to wrap children! --- .../src/components/button/abstractButton.tsx | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/core/src/components/button/abstractButton.tsx b/packages/core/src/components/button/abstractButton.tsx index 2fcc71ce38..1244117766 100644 --- a/packages/core/src/components/button/abstractButton.tsx +++ b/packages/core/src/components/button/abstractButton.tsx @@ -113,26 +113,18 @@ export abstract class AbstractButton extends React.Component { - if (child === "") { - // render as undefined to avoid extra space after icon - return undefined; - } else if (typeof child === "string") { - // must wrap string children in spans so loading prop can hide them - return {child}; - } - return child; - }); - + const { children, iconName, loading, rightIconName, text } = this.props; return ( <> - {loading && } - {text != null && {text}} - {children} - + + {(text || children) && ( + + {text} + {children} + + )} + ); } From 106705283aba29faaa4cf2069af0303d7846cad0 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 18:27:08 -0800 Subject: [PATCH 07/14] multiline and alignment support in styles --- .../core/src/components/button/_button.scss | 18 ++++++++++++++++++ .../core/src/components/button/_common.scss | 9 ++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/core/src/components/button/_button.scss b/packages/core/src/components/button/_button.scss index 76a7dc9e69..d965ebae2d 100644 --- a/packages/core/src/components/button/_button.scss +++ b/packages/core/src/components/button/_button.scss @@ -38,9 +38,16 @@ Styleguide pt-button } &.pt-fill { + display: flex; width: 100%; } + // default is `text-align: left` so we only need `right` case. + &.pt-align-right, + .pt-align-right & { + text-align: right; + } + &:not([class*="pt-intent-"]) { @include pt-button(); } @@ -214,3 +221,14 @@ a.pt-button { color: $button-color-disabled; } } + +.pt-button-text { + // default: don't grow to fill but allow shrinking as necessary + flex: 0 1 auto; + + // when alignment is set, grow to fill and inherit `text-align` set on `.pt-button` + .pt-align-left &, + .pt-align-right & { + flex: 1 1 auto; + } +} diff --git a/packages/core/src/components/button/_common.scss b/packages/core/src/components/button/_common.scss index a5f96aee26..1011edbd06 100644 --- a/packages/core/src/components/button/_common.scss +++ b/packages/core/src/components/button/_common.scss @@ -6,9 +6,9 @@ @import "../progress/common"; $button-border-width: 1px !default; -$button-padding: 0 $pt-grid-size !default; -$button-padding-small: 0 ($pt-grid-size * 0.7) !default; -$button-padding-large: 0 ($pt-grid-size * 1.5) !default; +$button-padding: ($pt-grid-size / 2) $pt-grid-size !default; +$button-padding-small: ($pt-grid-size / 2) ($pt-grid-size * 0.7) !default; +$button-padding-large: ($pt-grid-size / 2) ($pt-grid-size * 1.5) !default; $button-icon-spacing: ($pt-button-height - $pt-icon-size-standard) / 2 !default; $button-icon-spacing-large: ($pt-button-height-large - $pt-icon-size-large) / 2 !default; @@ -102,6 +102,7 @@ $button-intents: ( border-radius: $pt-border-radius; cursor: pointer; padding: $button-padding; + text-align: left; font-family: inherit; font-size: $pt-font-size; } @@ -109,8 +110,6 @@ $button-intents: ( @mixin pt-button-height($height) { min-width: $height; min-height: $height; - // for text centering - line-height: $height; } @mixin pt-button() { From bd445905b930b5ded344ef1f83fbc392c1535e3d Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 18:27:20 -0800 Subject: [PATCH 08/14] add align prop to Button and ButtonGroup --- .../core/src/components/button/abstractButton.tsx | 12 +++++++++++- .../core/src/components/button/buttonGroup.tsx | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/core/src/components/button/abstractButton.tsx b/packages/core/src/components/button/abstractButton.tsx index 1244117766..7385775f39 100644 --- a/packages/core/src/components/button/abstractButton.tsx +++ b/packages/core/src/components/button/abstractButton.tsx @@ -22,6 +22,14 @@ export interface IButtonProps extends IActionProps { */ active?: boolean; + /** + * Text alignment within button. By default, icons and text will be centered within the button. + * Passing this prop will cause the text container to fill the button and align the text within that + * to the appropriate side. `icon` and `rightIcon` will be pushed to either side. + * @default "center" + */ + align?: "left" | "center" | "right"; + /** A ref handler that receives the native HTML element backing this component. */ elementRef?: (ref: HTMLElement) => any; @@ -71,6 +79,8 @@ export abstract class AbstractButton extends React.Component extends React.Component} {(text || children) && ( - + {text} {children} diff --git a/packages/core/src/components/button/buttonGroup.tsx b/packages/core/src/components/button/buttonGroup.tsx index 0a0dbd0ecf..df01d7f12e 100644 --- a/packages/core/src/components/button/buttonGroup.tsx +++ b/packages/core/src/components/button/buttonGroup.tsx @@ -10,6 +10,14 @@ import * as Classes from "../../common/classes"; import { IProps } from "../../common/props"; export interface IButtonGroupProps extends IProps, React.HTMLProps { + /** + * Text alignment of button contents. + * `align="left"` will left-align button text and push `rightIcon` to right side. + * `align="right"` right-aligns text and pushes `icon` to left side. + * This prop only has an effect if buttons are wider than their default widths. + */ + align?: "left" | "center" | "right"; + /** * Whether the button group should take up the full width of its container. * @default false @@ -41,13 +49,15 @@ export class ButtonGroup extends React.PureComponent { public static displayName = "Blueprint2.ButtonGroup"; public render() { - const { className, fill, minimal, large, vertical, ...htmlProps } = this.props; + const { align, className, fill, minimal, large, vertical, ...htmlProps } = this.props; const buttonGroupClasses = classNames( Classes.BUTTON_GROUP, { + [Classes.ALIGN_LEFT]: align === "left", + [Classes.ALIGN_RIGHT]: align === "right", [Classes.FILL]: fill, - [Classes.MINIMAL]: minimal, [Classes.LARGE]: large, + [Classes.MINIMAL]: minimal, [Classes.VERTICAL]: vertical, }, className, From 788f97bd1a89f8ddc2d859a21ad2063f1b65fb21 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 18:27:31 -0800 Subject: [PATCH 09/14] add AlignmentSelect to ButtonGroup examples --- .../src/components/button/button-group.md | 6 ++- .../core-examples/buttonGroupExample.tsx | 12 +++++- .../buttonGroupPopoverExample.tsx | 31 +++++++++------- .../core-examples/common/alignmentSelect.tsx | 37 +++++++++++++++++++ 4 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 packages/docs-app/src/examples/core-examples/common/alignmentSelect.tsx diff --git a/packages/core/src/components/button/button-group.md b/packages/core/src/components/button/button-group.md index d6e295b78e..84ed8f6e18 100644 --- a/packages/core/src/components/button/button-group.md +++ b/packages/core/src/components/button/button-group.md @@ -29,7 +29,8 @@ You can adjust the specific size of a button with the `flex-basis` CSS property. Add the class `pt-vertical` to create a vertical button group. The buttons in a vertical group all have the same size as the widest button in the group. -Add the modifier class `pt-align-left` to left-align all button text and icons. +Add the modifier class `pt-align-left` (or `align="left"` in the React component) to +left-align button text and icon and right-align `rightIcon`. You can also combine vertical groups with the `pt-fill` and `pt-minimal` class modifiers. @@ -40,7 +41,8 @@ You can also combine vertical groups with the `pt-fill` and `pt-minimal` class m The `ButtonGroup` component is available in the __@blueprintjs/core__ package. Make sure to review the [general usage docs for JS components](#blueprint.usage). -This component is a simple wrapper around the CSS API, and supports the full range of HTML props. +This component is a simple wrapper around the CSS API. +It exposes shorthand props for CSS modifier classes and supports the full range of HTML props. ```tsx diff --git a/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx b/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx index 2f8f639808..c09ab47b7d 100644 --- a/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx +++ b/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx @@ -8,8 +8,10 @@ import * as React from "react"; import { AnchorButton, Button, ButtonGroup, Switch } from "@blueprintjs/core"; import { BaseExample, handleBooleanChange } from "@blueprintjs/docs-theme"; +import { AlignmentSelect } from "./common/alignmentSelect"; export interface IButtonGroupExampleState { + align: "left" | "center" | "right"; fill: boolean; iconOnly: boolean; minimal: boolean; @@ -19,6 +21,7 @@ export interface IButtonGroupExampleState { export class ButtonGroupExample extends BaseExample { public state: IButtonGroupExampleState = { + align: "center", fill: false, iconOnly: false, large: false, @@ -37,12 +40,14 @@ export class ButtonGroupExample extends BaseExample { // have the container take up the full-width if `fill` is true; // otherwise, disable full-width styles to keep a vertical button group // from taking up the full width. - const style: React.CSSProperties = { flexGrow: this.state.fill ? 1 : undefined }; + const style: React.CSSProperties = { minWidth: 200, flexGrow: this.state.fill ? 1 : undefined }; return ( - {!iconOnly && "Options"} + + {!iconOnly && "Options"} + ); } @@ -70,7 +75,10 @@ export class ButtonGroupExample extends BaseExample { label="Icons only" onChange={this.handleIconOnlyChange} />, + , ], ]; } + + private handleAlignChange = (align: IButtonGroupExampleState["align"]) => this.setState({ align }); } diff --git a/packages/docs-app/src/examples/core-examples/buttonGroupPopoverExample.tsx b/packages/docs-app/src/examples/core-examples/buttonGroupPopoverExample.tsx index b8ef6d446b..49e5c82fa9 100644 --- a/packages/docs-app/src/examples/core-examples/buttonGroupPopoverExample.tsx +++ b/packages/docs-app/src/examples/core-examples/buttonGroupPopoverExample.tsx @@ -4,23 +4,25 @@ * Licensed under the terms of the LICENSE file distributed with this project. */ -import { Button, ButtonGroup, Classes, IconName, Intent, Popover, Position, Switch } from "@blueprintjs/core"; +import { Button, ButtonGroup, IconName, Intent, Popover, Position, Switch } from "@blueprintjs/core"; import { BaseExample, handleBooleanChange, handleStringChange } from "@blueprintjs/docs-theme"; -import * as classNames from "classnames"; import * as React from "react"; +import { AlignmentSelect } from "./common/alignmentSelect"; import { FileMenu } from "./common/fileMenu"; import { IntentSelect } from "./common/intentSelect"; export interface IButtonGroupPopoverExampleState { - intent?: Intent; - large?: boolean; - minimal?: boolean; - vertical?: boolean; + align: "left" | "center" | "right"; + intent: Intent; + large: boolean; + minimal: boolean; + vertical: boolean; } export class ButtonGroupPopoverExample extends BaseExample { public state: IButtonGroupPopoverExampleState = { + align: "center", intent: Intent.NONE, large: false, minimal: false, @@ -35,15 +37,10 @@ export class ButtonGroupPopoverExample extends BaseExample this.setState({ vertical })); protected renderExample() { - const { large, minimal, vertical } = this.state; + const { intent, ...bgProps } = this.state; return ( - + {this.renderButton("File", "document")} {this.renderButton("Edit", "edit")} {this.renderButton("View", "eye-open")} @@ -52,9 +49,9 @@ export class ButtonGroupPopoverExample extends BaseExample, , , ], + [ + , + , + ], ]; } @@ -82,4 +83,6 @@ export class ButtonGroupPopoverExample extends BaseExample ); } + + private handleAlignChange = (align: IButtonGroupPopoverExampleState["align"]) => this.setState({ align }); } diff --git a/packages/docs-app/src/examples/core-examples/common/alignmentSelect.tsx b/packages/docs-app/src/examples/core-examples/common/alignmentSelect.tsx new file mode 100644 index 0000000000..5911ca0a3d --- /dev/null +++ b/packages/docs-app/src/examples/core-examples/common/alignmentSelect.tsx @@ -0,0 +1,37 @@ +/* + * Copyright 2016 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the terms of the LICENSE file distributed with this project. + */ + +import { Button, ButtonGroup } from "@blueprintjs/core"; +import * as React from "react"; + +export interface IAlignSelectProps { + align: "left" | "right" | "center" | undefined; + onChange: (align: IAlignSelectProps["align"]) => void; +} + +export class AlignmentSelect extends React.PureComponent { + public render() { + const { align } = this.props; + return ( +
+ Button alignment + +
+ ); + } + + private handleAlignLeft = () => this.props.onChange("left"); + private handleAlignCenter = () => this.props.onChange("center"); + private handleAlignRight = () => this.props.onChange("right"); +} From 4e510136131dcc6ed128649fcdcd0273943b1a95 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 18:34:06 -0800 Subject: [PATCH 10/14] fix row margin when large, right icon spacing in CSS API --- packages/core/src/components/button/_button.scss | 4 ++++ packages/core/src/components/button/_common.scss | 1 + 2 files changed, 5 insertions(+) diff --git a/packages/core/src/components/button/_button.scss b/packages/core/src/components/button/_button.scss index d965ebae2d..dc7526f527 100644 --- a/packages/core/src/components/button/_button.scss +++ b/packages/core/src/components/button/_button.scss @@ -127,6 +127,10 @@ Styleguide pt-button #{$icon-classes} { color: $pt-icon-color; + + &.pt-align-right { + margin-left: $button-icon-spacing; + } } // button with SVG icon only (no text or children) diff --git a/packages/core/src/components/button/_common.scss b/packages/core/src/components/button/_common.scss index 1011edbd06..ba920833ab 100644 --- a/packages/core/src/components/button/_common.scss +++ b/packages/core/src/components/button/_common.scss @@ -433,6 +433,7 @@ $button-intents: ( @mixin pt-button-large() { @include pt-button-height($pt-button-height-large); + @include pt-flex-margin(row, $button-icon-spacing-large); padding: $button-padding-large; font-size: $pt-font-size-large; } From 1ccd10f04c84935c7855cc678191cc26018c537f Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 21:29:29 -0800 Subject: [PATCH 11/14] fix buttons in input group --- .../core/src/components/button/_common.scss | 2 +- .../src/components/forms/_input-group.scss | 18 +++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/core/src/components/button/_common.scss b/packages/core/src/components/button/_common.scss index ba920833ab..4ba49e1f2b 100644 --- a/packages/core/src/components/button/_common.scss +++ b/packages/core/src/components/button/_common.scss @@ -7,7 +7,7 @@ $button-border-width: 1px !default; $button-padding: ($pt-grid-size / 2) $pt-grid-size !default; -$button-padding-small: ($pt-grid-size / 2) ($pt-grid-size * 0.7) !default; +$button-padding-small: 0 ($pt-grid-size * 0.7) !default; $button-padding-large: ($pt-grid-size / 2) ($pt-grid-size * 1.5) !default; $button-icon-spacing: ($pt-button-height - $pt-icon-size-standard) / 2 !default; $button-icon-spacing-large: ($pt-button-height-large - $pt-icon-size-large) / 2 !default; diff --git a/packages/core/src/components/forms/_input-group.scss b/packages/core/src/components/forms/_input-group.scss index bfc7808459..a34d7c44ef 100644 --- a/packages/core/src/components/forms/_input-group.scss +++ b/packages/core/src/components/forms/_input-group.scss @@ -74,17 +74,14 @@ $input-button-height-large: $pt-button-height !default; .pt-button { @include pt-button-height($input-button-height); margin: ($pt-input-height - $input-button-height) / 2; - padding-top: 0; - padding-bottom: 0; + padding: $button-padding-small; - .pt-icon { - $small-icon-margin: ($input-button-height - $pt-icon-size-standard) / 2; - margin-top: $small-icon-margin; - margin-bottom: $small-icon-margin; - } + // icons CSS API support + &:empty { padding: 0; } } - .pt-icon { + // direct descendant to exclude icons in buttons + > .pt-icon { @include pt-icon($pt-icon-size-standard); // bump icon up so it sits above input @@ -146,10 +143,9 @@ $input-button-height-large: $pt-button-height !default; .pt-button { @include pt-button-height($input-button-height-large); margin: ($pt-input-height-large - $input-button-height-large) / 2; - line-height: 0; } - .pt-icon { + > .pt-icon { margin: ($pt-input-height-large - $pt-icon-size-standard) / 2; } @@ -194,7 +190,7 @@ $input-button-height-large: $pt-button-height !default; @include pt-input-intent($color); } - .pt-icon { + > .pt-icon { color: map-get($pt-intent-text-colors, $intent); .pt-dark & { From a4e4b670537211e9470c0fc8775be5bb07b26087 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 21:29:48 -0800 Subject: [PATCH 12/14] control-group uses pt-flex-container (already used flex) --- .../core/src/components/forms/_control-group.scss | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/core/src/components/forms/_control-group.scss b/packages/core/src/components/forms/_control-group.scss index 9a0a6c18ee..70721bf6f6 100644 --- a/packages/core/src/components/forms/_control-group.scss +++ b/packages/core/src/components/forms/_control-group.scss @@ -46,14 +46,9 @@ Styleguide pt-control-group .pt-control-group { // create a new stacking context to isolate all the z-indices @include new-render-layer(); - display: flex; - align-items: flex-start; - - // apply these properties to all direct children, too many permutations to enumerate - // stylelint-disable selector-no-universal - > * { - flex: 0 0 auto; - } + @include pt-flex-container(row); + // each child is full height + align-items: stretch; // similarly to button groups, elements in control groups are stacked in a // very particular order for best visual results. in each level of selector @@ -251,7 +246,6 @@ Styleguide pt-control-group > * { margin-top: -$button-border-width; - width: 100%; } > :first-child { From 3e84b009dee9f40843dda73dc4a96c62590fc749 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Fri, 2 Feb 2018 21:31:32 -0800 Subject: [PATCH 13/14] let flex figure out heights of numeric-input buttons :ok_hand: --- .../src/components/forms/_numeric-input.scss | 54 +++---------------- 1 file changed, 6 insertions(+), 48 deletions(-) diff --git a/packages/core/src/components/forms/_numeric-input.scss b/packages/core/src/components/forms/_numeric-input.scss index 32edf6f39b..2ab2915613 100644 --- a/packages/core/src/components/forms/_numeric-input.scss +++ b/packages/core/src/components/forms/_numeric-input.scss @@ -2,28 +2,15 @@ @import "../../common/variables"; @import "./common"; -$numeric-input-button-height: ($pt-button-height / 2); -$numeric-input-button-height-large: ($pt-button-height-large / 2); -$dark-numeric-input-button-height: ($pt-button-height / 2) - 1px; -$dark-numeric-input-button-height-large: ($pt-button-height-large / 2) - 1px; - -@mixin numeric-input-button-heights($height) { - // the bottom (last-child) button is 1px shorter to fit the bottom border - // on the top (first-child) button above it. - &:first-child { - min-height: $height + 1; - } - - &:last-child { - min-height: $height; - } -} - .pt-numeric-input { // we need a very-specific selector here to override specificicty of selectors defined elsewhere. .pt-button-group.pt-vertical > .pt-button { - @include numeric-input-button-heights($numeric-input-button-height); + // let the buttons shrink to equal heights + flex: 1 1 auto; + width: $pt-button-height; + min-height: 0; + padding: 0; &:first-child { border-radius: 0 $pt-border-radius 0 0; @@ -32,23 +19,6 @@ $dark-numeric-input-button-height-large: ($pt-button-height-large / 2) - 1px; &:last-child { border-radius: 0 0 $pt-border-radius 0; } - - &[class*="pt-icon-"]::before, - .pt-icon { - // these styles are super unintuitive, but they basically are present to - // ensure that the focus outlines around the buttons will be perfect - // rectangles. delete any one of these styles, and the focus outlines will - // become jagged, because we're trying to fit a 16-px tall icon inside a - // ~15px-tall button. - display: block; - height: $pt-button-height / 2 - 1; - overflow: hidden; - line-height: $pt-button-height / 2 - 1; - } - - .pt-icon { - margin: 0 -3px; - } } // fix button border radii when the buttons are on the left @@ -65,18 +35,6 @@ $dark-numeric-input-button-height-large: ($pt-button-height-large / 2) - 1px; } &.pt-large .pt-button-group.pt-vertical > .pt-button { - @include numeric-input-button-heights($numeric-input-button-height-large); - } - - // need to shrink button sizes slightly in dark theme - .pt-dark & { - .pt-button-group.pt-vertical > .pt-button { - @include numeric-input-button-heights($dark-numeric-input-button-height); - line-height: $dark-numeric-input-button-height; - } - - &.pt-large .pt-button-group.pt-vertical > .pt-button { - @include numeric-input-button-heights($dark-numeric-input-button-height-large); - } + width: $pt-button-height-large; } } From 7d51281ef6c3ad235c1db292b3100b80ccbd97d7 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Mon, 5 Feb 2018 11:18:19 -0800 Subject: [PATCH 14/14] align => alignText, add more button classes --- packages/core/src/common/classes.ts | 2 ++ .../core/src/components/button/abstractButton.tsx | 15 ++++++++------- .../core/src/components/button/buttonGroup.tsx | 8 ++++---- .../examples/core-examples/buttonGroupExample.tsx | 8 ++++---- .../core-examples/buttonGroupPopoverExample.tsx | 11 ++++++----- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index 2f8a15c0a2..f9a9802e5c 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -42,6 +42,8 @@ export const BREADCRUMBS_COLLAPSED = "pt-breadcrumbs-collapsed"; export const BUTTON = "pt-button"; export const BUTTON_GROUP = "pt-button-group"; +export const BUTTON_SPINNER = "pt-button-spinner"; +export const BUTTON_TEXT = "pt-button-text"; export const CALLOUT = "pt-callout"; export const CALLOUT_ICON = "pt-callout-icon"; diff --git a/packages/core/src/components/button/abstractButton.tsx b/packages/core/src/components/button/abstractButton.tsx index 7385775f39..9a10781837 100644 --- a/packages/core/src/components/button/abstractButton.tsx +++ b/packages/core/src/components/button/abstractButton.tsx @@ -28,7 +28,7 @@ export interface IButtonProps extends IActionProps { * to the appropriate side. `icon` and `rightIcon` will be pushed to either side. * @default "center" */ - align?: "left" | "center" | "right"; + alignText?: "left" | "center" | "right"; /** A ref handler that receives the native HTML element backing this component. */ elementRef?: (ref: HTMLElement) => any; @@ -73,16 +73,17 @@ export abstract class AbstractButton extends React.Component extends React.Component - {loading && } + {loading && } {(text || children) && ( - + {text} {children} diff --git a/packages/core/src/components/button/buttonGroup.tsx b/packages/core/src/components/button/buttonGroup.tsx index df01d7f12e..1461ead7ed 100644 --- a/packages/core/src/components/button/buttonGroup.tsx +++ b/packages/core/src/components/button/buttonGroup.tsx @@ -16,7 +16,7 @@ export interface IButtonGroupProps extends IProps, React.HTMLProps { public static displayName = "Blueprint2.ButtonGroup"; public render() { - const { align, className, fill, minimal, large, vertical, ...htmlProps } = this.props; + const { alignText, className, fill, minimal, large, vertical, ...htmlProps } = this.props; const buttonGroupClasses = classNames( Classes.BUTTON_GROUP, { - [Classes.ALIGN_LEFT]: align === "left", - [Classes.ALIGN_RIGHT]: align === "right", + [Classes.ALIGN_LEFT]: alignText === "left", + [Classes.ALIGN_RIGHT]: alignText === "right", [Classes.FILL]: fill, [Classes.LARGE]: large, [Classes.MINIMAL]: minimal, diff --git a/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx b/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx index c09ab47b7d..1afc878905 100644 --- a/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx +++ b/packages/docs-app/src/examples/core-examples/buttonGroupExample.tsx @@ -11,7 +11,7 @@ import { BaseExample, handleBooleanChange } from "@blueprintjs/docs-theme"; import { AlignmentSelect } from "./common/alignmentSelect"; export interface IButtonGroupExampleState { - align: "left" | "center" | "right"; + alignText: "left" | "center" | "right"; fill: boolean; iconOnly: boolean; minimal: boolean; @@ -21,7 +21,7 @@ export interface IButtonGroupExampleState { export class ButtonGroupExample extends BaseExample { public state: IButtonGroupExampleState = { - align: "center", + alignText: "center", fill: false, iconOnly: false, large: false, @@ -75,10 +75,10 @@ export class ButtonGroupExample extends BaseExample { label="Icons only" onChange={this.handleIconOnlyChange} />, - , + , ], ]; } - private handleAlignChange = (align: IButtonGroupExampleState["align"]) => this.setState({ align }); + private handleAlignChange = (alignText: IButtonGroupExampleState["alignText"]) => this.setState({ alignText }); } diff --git a/packages/docs-app/src/examples/core-examples/buttonGroupPopoverExample.tsx b/packages/docs-app/src/examples/core-examples/buttonGroupPopoverExample.tsx index 49e5c82fa9..6883518c1a 100644 --- a/packages/docs-app/src/examples/core-examples/buttonGroupPopoverExample.tsx +++ b/packages/docs-app/src/examples/core-examples/buttonGroupPopoverExample.tsx @@ -13,7 +13,7 @@ import { FileMenu } from "./common/fileMenu"; import { IntentSelect } from "./common/intentSelect"; export interface IButtonGroupPopoverExampleState { - align: "left" | "center" | "right"; + alignText: "left" | "center" | "right"; intent: Intent; large: boolean; minimal: boolean; @@ -22,7 +22,7 @@ export interface IButtonGroupPopoverExampleState { export class ButtonGroupPopoverExample extends BaseExample { public state: IButtonGroupPopoverExampleState = { - align: "center", + alignText: "center", intent: Intent.NONE, large: false, minimal: false, @@ -49,7 +49,7 @@ export class ButtonGroupPopoverExample extends BaseExample, @@ -68,7 +68,7 @@ export class ButtonGroupPopoverExample extends BaseExample, - , + , ], ]; } @@ -84,5 +84,6 @@ export class ButtonGroupPopoverExample extends BaseExample this.setState({ align }); + private handleAlignChange = (alignText: IButtonGroupPopoverExampleState["alignText"]) => + this.setState({ alignText }); }