diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index e1b76423e2..ab26ad8d53 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -102,6 +102,11 @@ export const DIALOG_HEADER = `${DIALOG}-header`; export const DIVIDER = `${NS}-divider`; +export const DRAWER = `${NS}-drawer`; +export const DRAWER_BODY = `${DRAWER}-body`; +export const DRAWER_FOOTER = `${DRAWER}-footer`; +export const DRAWER_HEADER = `${DRAWER}-header`; + export const EDITABLE_TEXT = `${NS}-editable-text`; export const EDITABLE_TEXT_CONTENT = `${EDITABLE_TEXT}-content`; export const EDITABLE_TEXT_EDITING = `${EDITABLE_TEXT}-editing`; @@ -168,6 +173,7 @@ export const OVERFLOW_LIST_SPACER = `${OVERFLOW_LIST}-spacer`; export const OVERLAY = `${NS}-overlay`; export const OVERLAY_BACKDROP = `${OVERLAY}-backdrop`; +export const OVERLAY_CONTAINER = `${OVERLAY}-container`; export const OVERLAY_CONTENT = `${OVERLAY}-content`; export const OVERLAY_INLINE = `${OVERLAY}-inline`; export const OVERLAY_OPEN = `${OVERLAY}-open`; diff --git a/packages/core/src/components/_index.scss b/packages/core/src/components/_index.scss index f887c9aa65..2bca22eeae 100644 --- a/packages/core/src/components/_index.scss +++ b/packages/core/src/components/_index.scss @@ -11,6 +11,7 @@ @import "context-menu/context-menu"; @import "divider/divider"; @import "dialog/dialog"; +@import "drawer/drawer"; @import "editable-text/editable-text"; @import "forms/index"; @import "html-select/html-select"; diff --git a/packages/core/src/components/components.md b/packages/core/src/components/components.md index f8b261a105..ab1a8a2642 100644 --- a/packages/core/src/components/components.md +++ b/packages/core/src/components/components.md @@ -54,6 +54,7 @@ @page alert @page context-menu @page dialog +@page drawer @page popover @page toast @page tooltip diff --git a/packages/core/src/components/drawer/_drawer.scss b/packages/core/src/components/drawer/_drawer.scss new file mode 100644 index 0000000000..627535e006 --- /dev/null +++ b/packages/core/src/components/drawer/_drawer.scss @@ -0,0 +1,134 @@ +// Copyright 2018 Palantir Technologies, Inc. All rights reserved. +// Licensed under the terms of the LICENSE file distributed with this project. + +@import "~@blueprintjs/icons/src/icons"; +@import "../../common/mixins"; +@import "../../common/react-transition"; +@import "../../common/variables"; + +$drawer-margin: ($pt-grid-size * 3) 0 !default; +$drawer-padding: $pt-grid-size * 2 !default; + +$drawer-default-size: 50%; + +.#{$ns}-drawer { + display: flex; + flex-direction: column; + margin: 0; + box-shadow: $pt-elevation-shadow-4; + background: $white; + padding: 0; + + &:focus { + outline: 0; + } + + &:not(.#{$ns}-vertical) { + @include react-transition-phase( + "#{$ns}-overlay", + "enter", + (transform: (translateX(100%), translateX(0))), + $pt-transition-duration * 2, + $pt-transition-ease, + $before: "&" + ); + @include react-transition-phase( + "#{$ns}-overlay", + "exit", + (transform: (translateX(100%), translateX(0))), + $pt-transition-duration, + $before: "&" + ); + + top: 0; + right: 0; + bottom: 0; + width: $drawer-default-size; + } + + &.#{$ns}-vertical { + @include react-transition-phase( + "#{$ns}-overlay", + "enter", + (transform: (translateY(100%), translateY(0))), + $pt-transition-duration * 2, + $pt-transition-ease, + $before: "&" + ); + @include react-transition-phase( + "#{$ns}-overlay", + "exit", + (transform: (translateY(100%), translateY(0))), + $pt-transition-duration, + $before: "&" + ); + + right: 0; + bottom: 0; + left: 0; + height: $drawer-default-size; + } + + &.#{$ns}-dark, + .#{$ns}-dark & { + box-shadow: $pt-dark-dialog-box-shadow; + background: $dark-gray4; + color: $pt-dark-text-color; + } +} + +.#{$ns}-drawer-header { + display: flex; + flex: 0 0 auto; + align-items: center; + position: relative; + border-radius: 0; + box-shadow: 0 1px 0 $pt-divider-black; + min-height: $pt-icon-size-large + $dialog-padding; + padding: $dialog-padding / 4; + padding-left: $dialog-padding; + + .#{$ns}-icon-large, + .#{$ns}-icon { + flex: 0 0 auto; + margin-right: $dialog-padding / 2; + color: $pt-icon-color; + } + + .#{$ns}-heading { + @include overflow-ellipsis(); + flex: 1 1 auto; + margin: 0; + line-height: inherit; + + &:last-child { + margin-right: $dialog-padding; + } + } + + .#{$ns}-dark & { + box-shadow: 0 1px 0 $pt-dark-divider-black; + + .#{$ns}-icon-large, + .#{$ns}-icon { + color: $pt-dark-icon-color; + } + } +} + +.#{$ns}-drawer-body { + flex: 1 1 auto; + overflow: auto; + line-height: $pt-grid-size * 1.8; +} + +.#{$ns}-drawer-footer { + flex: 0 0 auto; + position: relative; + box-shadow: inset 0 1px 0 $pt-divider-black; + padding: $dialog-padding/2 $dialog-padding; + + .#{$ns}-dark & { + box-shadow: inset 0 1px 0 $pt-dark-divider-black; + } +} diff --git a/packages/core/src/components/drawer/drawer.md b/packages/core/src/components/drawer/drawer.md new file mode 100644 index 0000000000..a3319bc1d3 --- /dev/null +++ b/packages/core/src/components/drawer/drawer.md @@ -0,0 +1,21 @@ +--- +tag: new +--- + +@# Drawer + +Drawers overlay content over existing parts of the UI and are anchored to the edge of the screen. + +@reactExample DrawerExample + +@## Props + +`Drawer` is a stateless React component controlled by the `isOpen` prop. + +Use the `size` prop to set the size of the `Drawer`. This prop sets CSS `width` if `vertical={false}` (default) and `height` otherwise. Constants are available for common sizes: + +- `Drawer.SIZE_SMALL = 360px` +- `Drawer.SIZE_STANDARD = 50%` (default) +- `Drawer.SIZE_LARGE = 90%` + +@interface IDrawerProps diff --git a/packages/core/src/components/drawer/drawer.tsx b/packages/core/src/components/drawer/drawer.tsx new file mode 100644 index 0000000000..c757c4a3d9 --- /dev/null +++ b/packages/core/src/components/drawer/drawer.tsx @@ -0,0 +1,146 @@ +/* + * Copyright 2018 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the terms of the LICENSE file distributed with this project. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { AbstractPureComponent } from "../../common/abstractPureComponent"; +import * as Classes from "../../common/classes"; +import * as Errors from "../../common/errors"; +import { DISPLAYNAME_PREFIX, IProps, MaybeElement } from "../../common/props"; +import { Button } from "../button/buttons"; +import { H4 } from "../html/html"; +import { Icon, IconName } from "../icon/icon"; +import { IBackdropProps, IOverlayableProps, Overlay } from "../overlay/overlay"; + +export interface IDrawerProps extends IOverlayableProps, IBackdropProps, IProps { + /** + * Name of a Blueprint UI icon (or an icon element) to render in the + * drawer's header. Note that the header will only be rendered if `title` is + * provided. + */ + icon?: IconName | MaybeElement; + + /** + * Whether to show the close button in the dialog's header. + * Note that the header will only be rendered if `title` is provided. + * @default true + */ + isCloseButtonShown?: boolean; + + /** + * Toggles the visibility of the overlay and its children. + * This prop is required because the component is controlled. + */ + isOpen: boolean; + + /** + * CSS size of the drawer. This sets `width` if `vertical={false}` (default) + * and `height` otherwise. + * + * Constants are available for common sizes: + * - `Drawer.SIZE_SMALL = 360px` + * - `Drawer.SIZE_STANDARD = 50%` + * - `Drawer.SIZE_LARGE = 90%` + * + * @default Drawer.SIZE_STANDARD = "50%" + */ + size?: number | string; + + /** + * CSS styles to apply to the dialog. + * @default {} + */ + style?: React.CSSProperties; + + /** + * Title of the dialog. If provided, an element with `Classes.DIALOG_HEADER` + * will be rendered inside the dialog before any children elements. + */ + title?: React.ReactNode; + + /** + * Name of the transition for internal `CSSTransition`. Providing your own + * name here will require defining new CSS transition properties. + */ + transitionName?: string; + + /** + * Whether the drawer should appear with vertical styling. + * @default false + */ + vertical?: boolean; +} + +export class Drawer extends AbstractPureComponent { + public static displayName = `${DISPLAYNAME_PREFIX}.Drawer`; + public static defaultProps: IDrawerProps = { + canOutsideClickClose: true, + isCloseButtonShown: true, + isOpen: false, + style: {}, + vertical: false, + }; + + public static readonly SIZE_SMALL = "360px"; + public static readonly SIZE_STANDARD = "50%"; + public static readonly SIZE_LARGE = "90%"; + + public render() { + const { size, style, vertical } = this.props; + const classes = classNames(Classes.DRAWER, { [Classes.VERTICAL]: vertical }, this.props.className); + const styleProp = size == null ? style : { ...style, [vertical ? "height" : "width"]: size }; + return ( + +
+ {this.maybeRenderHeader()} + {this.props.children} +
+
+ ); + } + + protected validateProps(props: IDrawerProps) { + if (props.title == null) { + if (props.icon != null) { + console.warn(Errors.DIALOG_WARN_NO_HEADER_ICON); + } + if (props.isCloseButtonShown != null) { + console.warn(Errors.DIALOG_WARN_NO_HEADER_CLOSE_BUTTON); + } + } + } + + private maybeRenderCloseButton() { + if (this.props.isCloseButtonShown) { + return ( + + +
+
+

+ + Data integration is the seminal problem of the digital age. For over ten years, + we’ve helped the world’s premier organizations rise to the challenge. + +

+

+ Palantir Foundry radically reimagines the way enterprises interact with data by + amplifying and extending the power of data integration. With Foundry, anyone can source, + fuse, and transform data into any shape they desire. Business analysts become data + engineers — and leaders in their organization’s data revolution. +

+

+ Foundry’s back end includes a suite of best-in-class data integration capabilities: data + provenance, git-style versioning semantics, granular access controls, branching, + transformation authoring, and more. But these powers are not limited to the back-end IT + shop. +

+

+ In Foundry, tables, applications, reports, presentations, and spreadsheets operate as + data integrations in their own right. Access controls, transformation logic, and data + quality flow from original data source to intermediate analysis to presentation in real + time. Every end product created in Foundry becomes a new data source that other users + can build upon. And the enterprise data foundation goes where the business drives it. +

+

Start the revolution. Unleash the power of data integration with Palantir Foundry.

+
+
+
Footer
+
+ + ); + } + + private renderOptions() { + const { autoFocus, enforceFocus, canEscapeKeyClose, canOutsideClickClose, hasBackdrop, usePortal } = this.state; + return ( + <> +
Props
+ + + + + + + + Use Portal + + + + + ); + } + + private handleOpen = () => this.setState({ isOpen: true }); + private handleClose = () => this.setState({ isOpen: false }); +} + +const SIZES: Array = [ + { label: "Default", value: undefined }, + { label: "Small", value: Drawer.SIZE_SMALL }, + { label: "Standard", value: Drawer.SIZE_STANDARD }, + { label: "Large", value: Drawer.SIZE_LARGE }, + "72%", + "560px", +]; diff --git a/packages/docs-app/src/examples/core-examples/index.ts b/packages/docs-app/src/examples/core-examples/index.ts index e3f41e8bbc..9d5f32d1f8 100644 --- a/packages/docs-app/src/examples/core-examples/index.ts +++ b/packages/docs-app/src/examples/core-examples/index.ts @@ -15,10 +15,11 @@ export * from "./checkboxExample"; export * from "./collapseExample"; export * from "./cardExample"; export * from "./collapsibleListExample"; +export * from "./contextMenuExample"; export * from "./controlGroupExample"; export * from "./dialogExample"; export * from "./dividerExample"; -export * from "./contextMenuExample"; +export * from "./drawerExample"; export * from "./dropdownMenuExample"; export * from "./editableTextExample"; export * from "./focusExample"; diff --git a/packages/docs-theme/src/components/documentation.tsx b/packages/docs-theme/src/components/documentation.tsx index 0c0c549ae9..e36bd400e8 100644 --- a/packages/docs-theme/src/components/documentation.tsx +++ b/packages/docs-theme/src/components/documentation.tsx @@ -8,7 +8,7 @@ import classNames from "classnames"; import { IHeadingNode, IPageData, IPageNode, isPageNode, ITsDocBase, linkify } from "documentalist/dist/client"; import * as React from "react"; -import { Classes, FocusStyleManager, Hotkey, Hotkeys, HotkeysTarget, IProps, Overlay, Utils } from "@blueprintjs/core"; +import { Classes, Drawer, FocusStyleManager, Hotkey, Hotkeys, HotkeysTarget, IProps, Utils } from "@blueprintjs/core"; import { DocumentationContextTypes, hasTypescriptData, IDocsData, IDocumentationContext } from "../common/context"; import { eachLayoutNode } from "../common/utils"; @@ -160,7 +160,7 @@ export class Documentation extends React.PureComponent {this.props.banner} @@ -199,9 +199,9 @@ export class Documentation extends React.PureComponent - + - +