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

flex button #2078

Merged
merged 15 commits into from
Feb 5, 2018
Merged
Show file tree
Hide file tree
Changes from 13 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
47 changes: 47 additions & 0 deletions packages/core/src/common/_flex.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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) {
$margin-prop: if($direction == row, margin-right, margin-bottom);

// CSS API support
&::before,
// space after all children
> * {
#{$margin-prop}: $margin;
}

// remove space after last child
&:empty::before,
> :last-child {
#{$margin-prop}: 0;
}
}
1 change: 1 addition & 0 deletions packages/core/src/common/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
73 changes: 36 additions & 37 deletions packages/core/src/components/button/_button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,26 @@ Styleguide pt-button
@include pt-button-base();
@include pt-button-height($pt-button-height);

&:empty {
padding: 0;
}

&:disabled,
&.pt-disabled {
cursor: not-allowed;
}

&.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();
}
Expand Down Expand Up @@ -81,19 +92,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
Expand All @@ -120,17 +118,29 @@ 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 & {
&:not([class*="pt-intent-"]) {
@include pt-dark-button();
Expand Down Expand Up @@ -191,34 +201,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 {
Expand All @@ -237,3 +225,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;
}
}
14 changes: 8 additions & 6 deletions packages/core/src/components/button/_common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
@import "../progress/common";

$button-border-width: 1px !default;
$button-padding: 0 $pt-grid-size !default;
$button-padding: ($pt-grid-size / 2) $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-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;

Expand Down Expand Up @@ -94,21 +94,22 @@ $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;
text-align: left;
font-family: inherit;
font-size: $pt-font-size;
}

@mixin pt-button-height($height) {
min-width: $height;
min-height: $height;
// for text centering
line-height: $height;
}

@mixin pt-button() {
Expand Down Expand Up @@ -432,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;
}
Expand Down
36 changes: 19 additions & 17 deletions packages/core/src/components/button/abstractButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍 😍 😍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this conflicts somewhat with the NavbarGroup align prop -- here, you are referring to the alignment of content while for NavbarGroup (as well as most design tools), align refers to the component's alignment in its container. I suggest renaming this to textAlignment or alignText

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm yes good catch @adidahiya! i'll go with alignText


/** A ref handler that receives the native HTML element backing this component. */
elementRef?: (ref: HTMLElement) => any;

Expand Down Expand Up @@ -71,6 +79,8 @@ export abstract class AbstractButton<T> extends React.Component<React.HTMLProps<
Classes.BUTTON,
{
[Classes.ACTIVE]: this.state.isActive || this.props.active,
[Classes.ALIGN_LEFT]: this.props.align === "left",
[Classes.ALIGN_RIGHT]: this.props.align === "right",
[Classes.DISABLED]: disabled,
[Classes.LOADING]: this.props.loading,
},
Expand Down Expand Up @@ -113,26 +123,18 @@ export abstract class AbstractButton<T> extends React.Component<React.HTMLProps<
};

protected renderChildren(): React.ReactNode {
const { iconName, loading, rightIconName, text } = this.props;

const children = React.Children.map(this.props.children, (child, index) => {
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 <span key={`text-${index}`}>{child}</span>;
}
return child;
});

const { children, iconName, loading, rightIconName, text } = this.props;
return (
<>
<Icon iconName={iconName} />
{loading && <Spinner className="pt-small pt-button-spinner" />}
{text != null && <span>{text}</span>}
{children}
<Icon className={Classes.ALIGN_RIGHT} iconName={rightIconName} />
<Icon iconName={iconName} />
{(text || children) && (
<span className="pt-button-text">
{text}
{children}
</span>
)}
<Icon iconName={rightIconName} />
</>
);
}
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/components/button/button-group.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
<ButtonGroup minimal={true} large={false} onMouseEnter={...}>
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/components/button/buttonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import * as Classes from "../../common/classes";
import { IProps } from "../../common/props";

export interface IButtonGroupProps extends IProps, React.HTMLProps<HTMLDivElement> {
/**
* 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
Expand Down Expand Up @@ -41,13 +49,15 @@ export class ButtonGroup extends React.PureComponent<IButtonGroupProps, {}> {
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would prefer not to add tangential new props in this PR.
i plan to add modifier props across the board after 2.0 (non-breaking)

[Classes.VERTICAL]: vertical,
},
className,
Expand Down
12 changes: 3 additions & 9 deletions packages/core/src/components/forms/_control-group.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -251,7 +246,6 @@ Styleguide pt-control-group

> * {
margin-top: -$button-border-width;
width: 100%;
}

> :first-child {
Expand Down
Loading