From 62b5df2cfcccb9efd0de0cd3eca5a4e43e2c921c Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 16:03:11 +0200 Subject: [PATCH 01/21] Initial commit --- packages/core/src/common/classes.ts | 10 ++ packages/core/src/components/_index.scss | 1 + packages/core/src/components/components.md | 1 + packages/core/src/components/index.ts | 2 + .../core/src/components/section/_section.scss | 59 ++++++++++ .../components/section/section-content.tsx | 44 ++++++++ .../core/src/components/section/section.md | 17 +++ .../core/src/components/section/section.tsx | 89 +++++++++++++++ .../src/examples/core-examples/index.ts | 1 + .../examples/core-examples/sectionExample.tsx | 106 ++++++++++++++++++ 10 files changed, 330 insertions(+) create mode 100644 packages/core/src/components/section/_section.scss create mode 100644 packages/core/src/components/section/section-content.tsx create mode 100644 packages/core/src/components/section/section.md create mode 100644 packages/core/src/components/section/section.tsx create mode 100644 packages/docs-app/src/examples/core-examples/sectionExample.tsx diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index 33b5af5dad..4eccb1d9f1 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -72,6 +72,8 @@ export const INTENT_DANGER = intentClass(Intent.DANGER)!; export const FOCUS_DISABLED = `${NS}-focus-disabled`; export const FOCUS_STYLE_MANAGER_IGNORE = `${NS}-focus-style-manager-ignore`; +export const PADDED = `${NS}-padded`; + // text utilities export const UI_TEXT = `${NS}-ui-text`; export const RUNNING_TEXT = `${NS}-running-text`; @@ -213,6 +215,14 @@ export const MULTISTEP_DIALOG_RIGHT_PANEL = `${MULTISTEP_DIALOG}-right-panel`; export const MULTISTEP_DIALOG_NAV_TOP = `${MULTISTEP_DIALOG}-nav-top`; export const MULTISTEP_DIALOG_NAV_RIGHT = `${MULTISTEP_DIALOG}-nav-right`; +export const SECTION = `${NS}-section`; +export const SECTION_HEADER = `${SECTION}-header`; +export const SECTION_HEADER_LEFT = `${SECTION_HEADER}-left`; +export const SECTION_HEADER_TITLE = `${SECTION_HEADER}-title`; +export const SECTION_HEADER_SUB_TITLE = `${SECTION_HEADER}-sub-title`; +export const SECTION_HEADER_RIGHT = `${SECTION_HEADER}-right`; +export const SECTION_CONTENT = `${SECTION}-content`; + export const NAVBAR = `${NS}-navbar`; export const NAVBAR_GROUP = `${NAVBAR}-group`; export const NAVBAR_HEADING = `${NAVBAR}-heading`; diff --git a/packages/core/src/components/_index.scss b/packages/core/src/components/_index.scss index 3c065d9702..b45a29ebf0 100644 --- a/packages/core/src/components/_index.scss +++ b/packages/core/src/components/_index.scss @@ -31,6 +31,7 @@ @import "popover/popover"; @import "portal/portal"; @import "progress-bar/progress-bar"; +@import "section/section"; @import "skeleton/skeleton"; @import "slider/slider"; @import "spinner/spinner"; diff --git a/packages/core/src/components/components.md b/packages/core/src/components/components.md index 8700a5195d..65371e41dd 100644 --- a/packages/core/src/components/components.md +++ b/packages/core/src/components/components.md @@ -22,6 +22,7 @@ @page panel-stack2 @page progress-bar @page resize-sensor +@page section @page skeleton @page spinner @page tabs diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index 53586422ac..68ad4bb853 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -97,6 +97,8 @@ export { ResizeEntry, ResizeSensor, ResizeSensorProps } from "./resize-sensor/re export { HandleHtmlProps, HandleInteractionKind, HandleProps, HandleType } from "./slider/handleProps"; export { MultiSlider, MultiSliderProps, SliderBaseProps } from "./slider/multiSlider"; export { NumberRange, RangeSlider, RangeSliderProps } from "./slider/rangeSlider"; +export { Section, SectionProps } from "./section/section"; +export { SectionContent, SectionContentProps } from "./section/section-content"; export { Slider, SliderProps } from "./slider/slider"; export { Spinner, SpinnerProps, SpinnerSize } from "./spinner/spinner"; export { Tab, TabId, TabProps } from "./tabs/tab"; diff --git a/packages/core/src/components/section/_section.scss b/packages/core/src/components/section/_section.scss new file mode 100644 index 0000000000..027202a92d --- /dev/null +++ b/packages/core/src/components/section/_section.scss @@ -0,0 +1,59 @@ +@use "sass:math"; +@import "../../common/variables"; + +.#{$ns}-section { + padding: 0; + width: 100%; + + &-header { + align-items: stretch; + display: flex; + justify-content: space-between; + position: relative; + width: 100%; + gap: $pt-grid-size; + min-height: 50px; + padding: $pt-grid-size $pt-grid-size $pt-grid-size $pt-grid-size * 2; + + border-bottom: 1px solid $pt-divider-black; + + &.#{$ns}-dark, + .#{$ns}-dark & { + border-color: $pt-dark-divider-white; + } + + &-left { + align-items: center; + display: flex; + gap: $pt-grid-size; + } + + &-title { + margin-bottom: 0; + } + + &-sub-title { + margin-top: 2px; + } + + &-right { + align-items: center; + display: flex; + } + } + + &-content { + &.#{$ns}-padded { + padding: $pt-grid-size * 2; + } + + &:not(:last-child) { + border-bottom: 1px solid $pt-divider-black; + + &.#{$ns}-dark, + .#{$ns}-dark & { + border-color: $pt-dark-divider-white; + } + } + } +} diff --git a/packages/core/src/components/section/section-content.tsx b/packages/core/src/components/section/section-content.tsx new file mode 100644 index 0000000000..04d6b8abce --- /dev/null +++ b/packages/core/src/components/section/section-content.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { Classes } from "../../common"; +import { DISPLAYNAME_PREFIX, HTMLDivProps, Props } from "../../common/props"; + +export interface SectionContentProps extends Props, HTMLDivProps, React.RefAttributes { + padded?: boolean; +} + +/** + * Section content component. + * + * @see https://blueprintjs.com/docs/#core/components/section-content + */ +export const SectionContent: React.FC = React.forwardRef((props, ref) => { + const { className, children, padded, ...htmlProps } = props; + const classes = classNames(Classes.SECTION_CONTENT, { [Classes.PADDED]: padded }, className); + return ( +
+ {children} +
+ ); +}); +SectionContent.defaultProps = { + padded: true, +}; +SectionContent.displayName = `${DISPLAYNAME_PREFIX}.SectionContent`; diff --git a/packages/core/src/components/section/section.md b/packages/core/src/components/section/section.md new file mode 100644 index 0000000000..d987c4c6bf --- /dev/null +++ b/packages/core/src/components/section/section.md @@ -0,0 +1,17 @@ +@# Section + +todo + +@reactExample SectionExample + +@## Props + +@interface SectionProps + +@# Section Content + +- Multiple section content can be added under one section, they will be stacked. + +@## Props + +@interface SectionContentProps diff --git a/packages/core/src/components/section/section.tsx b/packages/core/src/components/section/section.tsx new file mode 100644 index 0000000000..1817076c2b --- /dev/null +++ b/packages/core/src/components/section/section.tsx @@ -0,0 +1,89 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { IconName } from "@blueprintjs/icons"; + +import { Classes, Elevation } from "../../common"; +import { SECTION_HEADER } from "../../common/classes"; +import { DISPLAYNAME_PREFIX, MaybeElement, Props } from "../../common/props"; +import { Card, CardProps } from "../card/card"; +import { H6 } from "../html/html"; +import { Icon } from "../icon/icon"; + +export interface SectionProps + extends Props, + Omit, + React.RefAttributes { + /** + * Name of a Blueprint UI icon (or an icon element) to render in the + * section's header. Note that the header will only be rendered if `sectionTitle` is + * provided. + */ + icon?: IconName | MaybeElement; + + /** + * Title of the section. + */ + sectionTitle?: JSX.Element | string; + + /** + * Sub-title of the section. + * Note that the header will only be rendered if `sectionTitle` is provided. + */ + subtitle?: JSX.Element | string; + + /** + * Element to render on the right side of the section header + */ + rightItem?: JSX.Element; +} + +/** + * Section component. + * + * @see https://blueprintjs.com/docs/#core/components/section + */ +export const Section: React.FC = React.forwardRef((props, ref) => { + const { className, icon, sectionTitle, rightItem, subtitle, children, ...cardProps } = props; + const classes = classNames(Classes.SECTION, className); + return ( + +
+
+ {sectionTitle && icon && ( + + )} +
+ {sectionTitle &&
{sectionTitle}
} + {sectionTitle && subtitle && ( +
+ {subtitle} +
+ )} +
+
+ {rightItem &&
{rightItem}
} +
+ + {children} +
+ ); +}); +Section.defaultProps = {}; +Section.displayName = `${DISPLAYNAME_PREFIX}.Section`; diff --git a/packages/docs-app/src/examples/core-examples/index.ts b/packages/docs-app/src/examples/core-examples/index.ts index 9b82562c05..e550cab033 100644 --- a/packages/docs-app/src/examples/core-examples/index.ts +++ b/packages/docs-app/src/examples/core-examples/index.ts @@ -63,6 +63,7 @@ export * from "./popoverSizingExample"; export * from "./progressExample"; export * from "./rangeSliderExample"; export * from "./radioExample"; +export * from "./sectionExample"; export * from "./sliderExample"; export * from "./switchExample"; export * from "./tagInputExample"; diff --git a/packages/docs-app/src/examples/core-examples/sectionExample.tsx b/packages/docs-app/src/examples/core-examples/sectionExample.tsx new file mode 100644 index 0000000000..0666453f9d --- /dev/null +++ b/packages/docs-app/src/examples/core-examples/sectionExample.tsx @@ -0,0 +1,106 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as React from "react"; + +import { Button, Classes, H5, Intent, Section, SectionContent, Switch, Text } from "@blueprintjs/core"; +import { Example, ExampleProps } from "@blueprintjs/docs-theme"; +import { IconNames } from "@blueprintjs/icons"; + +export interface SectionExampleState { + hasIcon: boolean; + hasDescription: boolean; + hasRightItem: boolean; + hasMultipleSectionContent: boolean; +} + +export class SectionExample extends React.PureComponent { + public state: SectionExampleState = { + hasDescription: false, + hasIcon: false, + hasMultipleSectionContent: false, + hasRightItem: true, + }; + + public render() { + const { hasDescription, hasIcon, hasRightItem, hasMultipleSectionContent } = this.state; + + const options = ( + <> +
Props
+ + + + + + ); + + return ( + +
+ Edit + + ) : undefined + } + > + + + Basil; Ocimum basilicum, also called great basil, is a culinary herb of the family Lamiaceae + (mints). It is a tender plant, and is used in cuisines worldwide. In Western cuisine, the + generic term "basil" refers to the variety also known as sweet basil or Genovese basil. + Basil is native to tropical regions from Central Africa to Southeast Asia. + + + + {hasMultipleSectionContent && ( + +
+
+ KingdomPlantae +
+
+ CladeTracheophytes +
+
+ FamilyLamiaceae +
+
+
+ )} +
+
+ ); + } + + private handleIconChange = () => this.setState({ hasIcon: !this.state.hasIcon }); + + private handleDescriptionChange = () => this.setState({ hasDescription: !this.state.hasDescription }); + + private handleRightItemChange = () => this.setState({ hasRightItem: !this.state.hasRightItem }); + + private handleMultpleSectionContentChange = () => + this.setState({ hasMultipleSectionContent: !this.state.hasMultipleSectionContent }); +} From 28528bc6e64251dc0e5f095aea85972a5233ee48 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 16:56:06 +0200 Subject: [PATCH 02/21] initial commit --- packages/core/src/common/classes.ts | 4 ++ .../src/components/card-list/CardList.tsx | 53 +++++++++++++++++++ .../src/components/card-list/card-list.scss | 20 +++++++ 3 files changed, 77 insertions(+) create mode 100644 packages/core/src/components/card-list/CardList.tsx create mode 100644 packages/core/src/components/card-list/card-list.scss diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index 33b5af5dad..73ecf36636 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -72,6 +72,8 @@ export const INTENT_DANGER = intentClass(Intent.DANGER)!; export const FOCUS_DISABLED = `${NS}-focus-disabled`; export const FOCUS_STYLE_MANAGER_IGNORE = `${NS}-focus-style-manager-ignore`; +export const CONTAINED = `${NS}-contained`; + // text utilities export const UI_TEXT = `${NS}-ui-text`; export const RUNNING_TEXT = `${NS}-running-text`; @@ -115,6 +117,8 @@ export const CARD = `${NS}-card`; export const COLLAPSE = `${NS}-collapse`; export const COLLAPSE_BODY = `${COLLAPSE}-body`; +export const CARD_LIST = `${NS}-card-list`; + export const CONTEXT_MENU = `${NS}-context-menu`; export const CONTEXT_MENU_VIRTUAL_TARGET = `${CONTEXT_MENU}-virtual-target`; export const CONTEXT_MENU_POPOVER = `${CONTEXT_MENU}-popover`; diff --git a/packages/core/src/components/card-list/CardList.tsx b/packages/core/src/components/card-list/CardList.tsx new file mode 100644 index 0000000000..70139ec3ff --- /dev/null +++ b/packages/core/src/components/card-list/CardList.tsx @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import React from "react"; + +import { AbstractPureComponent2, Classes, DISPLAYNAME_PREFIX, Elevation, HTMLDivProps, Props } from "../../common"; + +// eslint-disable-next-line deprecation/deprecation +export type CardListProps = ICardListProps; +/** @deprecated use CardProps */ +export interface ICardListProps extends Props, HTMLDivProps { + // Set true if the list is in a container without padding + contained?: boolean; +} + +export class CardList extends AbstractPureComponent2 { + public static displayName = `${DISPLAYNAME_PREFIX}.CardList`; + + public render() { + const { className, contained, children, ...htmlProps } = this.props; + const renderableChildren = React.Children.toArray(children); // .filter(isRenderable); + const classes = classNames( + Classes.CARD_LIST, + { [Classes.CONTAINED]: contained }, + { [Classes.ELEVATION_0]: !contained }, + className, + ); + + return renderableChildren.length > 0 ? ( +
+ {renderableChildren.map((child, index) => ( +
{child}
+ ))} +
+ ) : null; + } +} + +// const isRenderable = (o: T | undefined | null | boolean) => isNonNullable(o) && isNotBoolean(o); diff --git a/packages/core/src/components/card-list/card-list.scss b/packages/core/src/components/card-list/card-list.scss new file mode 100644 index 0000000000..73547d722f --- /dev/null +++ b/packages/core/src/components/card-list/card-list.scss @@ -0,0 +1,20 @@ +@import "../../common/variables"; + +.card-list { + &:not(.contained) { + border-radius: $pt-border-radius; + } + + & > .#{$ns}-card { + border-radius: 0; + box-shadow: none; + + &:not(:last-child) { + border-bottom: 1px solid $pt-divider-black; + + .#{$ns}-dark & { + border-color: $pt-dark-divider-white; + } + } + } +} From eb8e6fd34f47ca8918bf363494807cf1c236a86a Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 14:25:56 +0200 Subject: [PATCH 03/21] Dark mode --- packages/core/src/common/classes.ts | 5 +- packages/core/src/components/_index.scss | 1 + .../src/components/card-list/CardList.tsx | 53 ------------- .../src/components/card-list/card-list.md | 33 ++++++++ .../src/components/card-list/card-list.scss | 44 +++++++++-- .../src/components/card-list/card-list.tsx | 53 +++++++++++++ packages/core/src/components/components.md | 1 + packages/core/src/components/index.ts | 1 + .../core-examples/cardListExample.tsx | 76 +++++++++++++++++++ .../src/examples/core-examples/index.ts | 1 + 10 files changed, 206 insertions(+), 62 deletions(-) delete mode 100644 packages/core/src/components/card-list/CardList.tsx create mode 100644 packages/core/src/components/card-list/card-list.md create mode 100644 packages/core/src/components/card-list/card-list.tsx create mode 100644 packages/docs-app/src/examples/core-examples/cardListExample.tsx diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index 73ecf36636..a1f2f66ef7 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -114,11 +114,12 @@ export const CALLOUT_ICON = `${CALLOUT}-icon`; export const CARD = `${NS}-card`; +export const CARD_LIST = `${NS}-card-list`; +export const CARD_LIST_ITEM = `${CARD_LIST}-item`; + export const COLLAPSE = `${NS}-collapse`; export const COLLAPSE_BODY = `${COLLAPSE}-body`; -export const CARD_LIST = `${NS}-card-list`; - export const CONTEXT_MENU = `${NS}-context-menu`; export const CONTEXT_MENU_VIRTUAL_TARGET = `${CONTEXT_MENU}-virtual-target`; export const CONTEXT_MENU_POPOVER = `${CONTEXT_MENU}-popover`; diff --git a/packages/core/src/components/_index.scss b/packages/core/src/components/_index.scss index 3c065d9702..1c937565b5 100644 --- a/packages/core/src/components/_index.scss +++ b/packages/core/src/components/_index.scss @@ -7,6 +7,7 @@ @import "button/button-group"; @import "callout/callout"; @import "card/card"; +@import "card-list/card-list"; @import "collapse/collapse"; @import "context-menu/context-menu"; @import "divider/divider"; diff --git a/packages/core/src/components/card-list/CardList.tsx b/packages/core/src/components/card-list/CardList.tsx deleted file mode 100644 index 70139ec3ff..0000000000 --- a/packages/core/src/components/card-list/CardList.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import classNames from "classnames"; -import React from "react"; - -import { AbstractPureComponent2, Classes, DISPLAYNAME_PREFIX, Elevation, HTMLDivProps, Props } from "../../common"; - -// eslint-disable-next-line deprecation/deprecation -export type CardListProps = ICardListProps; -/** @deprecated use CardProps */ -export interface ICardListProps extends Props, HTMLDivProps { - // Set true if the list is in a container without padding - contained?: boolean; -} - -export class CardList extends AbstractPureComponent2 { - public static displayName = `${DISPLAYNAME_PREFIX}.CardList`; - - public render() { - const { className, contained, children, ...htmlProps } = this.props; - const renderableChildren = React.Children.toArray(children); // .filter(isRenderable); - const classes = classNames( - Classes.CARD_LIST, - { [Classes.CONTAINED]: contained }, - { [Classes.ELEVATION_0]: !contained }, - className, - ); - - return renderableChildren.length > 0 ? ( -
- {renderableChildren.map((child, index) => ( -
{child}
- ))} -
- ) : null; - } -} - -// const isRenderable = (o: T | undefined | null | boolean) => isNonNullable(o) && isNotBoolean(o); diff --git a/packages/core/src/components/card-list/card-list.md b/packages/core/src/components/card-list/card-list.md new file mode 100644 index 0000000000..1dc5d6459d --- /dev/null +++ b/packages/core/src/components/card-list/card-list.md @@ -0,0 +1,33 @@ +@# Card List + +A card list is a wrapper around Cards. Compared to stand-alone cards, it can be used to reduce visual weight and allows inner scrolling. + +@reactExample CardListExample + + +@## Props + +```tsx +import { Button, Card, Elevation } from "@blueprintjs/core"; + + + + Chicken Basquaise + + + + + Tarte Flambée + + + + + Pain au Chocolat + + + +``` + +@interface CardListProps + +@css card-list diff --git a/packages/core/src/components/card-list/card-list.scss b/packages/core/src/components/card-list/card-list.scss index 73547d722f..fe85595cc1 100644 --- a/packages/core/src/components/card-list/card-list.scss +++ b/packages/core/src/components/card-list/card-list.scss @@ -1,20 +1,50 @@ @import "../../common/variables"; -.card-list { - &:not(.contained) { - border-radius: $pt-border-radius; +.#{$ns}-card-list { + width: 100%; + overflow: auto; + padding: 0; + + &.#{$ns}-contained { + box-shadow: none; + border-radius: 0; + } + + // HACK: The card dark mode border is inset, adding margin / padding to prevent items from going over it + .#{$ns}-dark & { + padding: 1px; + + &.#{$ns}-contained { + margin: 1px; + } } - & > .#{$ns}-card { + &-item { + > .#{$ns}-card { border-radius: 0; box-shadow: none; + min-height: 50px; + padding-top: 10px; + padding-bottom: 10px; + display: flex; + align-items: center; - &:not(:last-child) { - border-bottom: 1px solid $pt-divider-black; + &.#{$ns}-interactive:hover { + background-color: $light-gray5; + box-shadow: none; .#{$ns}-dark & { - border-color: $pt-dark-divider-white; + background-color: $dark-gray4; } } + } + + &:not(:last-child) > .#{$ns}-card { + border-bottom: 1px solid $pt-divider-black; + + .#{$ns}-dark & { + border-color: $pt-dark-divider-white; + } + } } } diff --git a/packages/core/src/components/card-list/card-list.tsx b/packages/core/src/components/card-list/card-list.tsx new file mode 100644 index 0000000000..27ba72dbcc --- /dev/null +++ b/packages/core/src/components/card-list/card-list.tsx @@ -0,0 +1,53 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { Classes, DISPLAYNAME_PREFIX, Elevation, HTMLDivProps, Props } from "../../common"; +import { Card } from "../card/card"; + +export interface CardListProps extends Props, HTMLDivProps, React.RefAttributes { + /** + * Set true if the list is in a container without padding. + * Elevation and border radius will be removed. + */ + contained?: boolean; +} + +export const CardList: React.FC = React.forwardRef((props, ref) => { + const { className, contained, children, ...htmlProps } = props; + + const renderableChildren = React.Children.toArray(children); // .filter(isRenderable); + const classes = classNames(Classes.CARD_LIST, { [Classes.CONTAINED]: contained }, className); + + return renderableChildren.length > 0 ? ( + + {renderableChildren.map((child, index) => ( +
+ {child} +
+ ))} +
+ ) : null; +}); + +CardList.defaultProps = { + contained: false, +}; +CardList.displayName = `${DISPLAYNAME_PREFIX}.CardList`; + +// const isRenderable = (o: T | undefined | null | boolean) => isNonNullable(o) && isNotBoolean(o); diff --git a/packages/core/src/components/components.md b/packages/core/src/components/components.md index 8700a5195d..079f687d00 100644 --- a/packages/core/src/components/components.md +++ b/packages/core/src/components/components.md @@ -7,6 +7,7 @@ @page button-group @page callout @page card +@page card-list @page collapse @page divider @page editable-text diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index 53586422ac..73fbd1758d 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -27,6 +27,7 @@ export type { export { ButtonGroup, ButtonGroupProps } from "./button/buttonGroup"; export { Callout, CalloutProps } from "./callout/callout"; export { Card, CardProps } from "./card/card"; +export { CardList, CardListProps } from "./card-list/card-list"; export { Collapse, CollapseProps } from "./collapse/collapse"; export { ContextMenu, diff --git a/packages/docs-app/src/examples/core-examples/cardListExample.tsx b/packages/docs-app/src/examples/core-examples/cardListExample.tsx new file mode 100644 index 0000000000..c2750de9fb --- /dev/null +++ b/packages/docs-app/src/examples/core-examples/cardListExample.tsx @@ -0,0 +1,76 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as React from "react"; + +import { Button, Card, CardList, Classes, H6, Icon, Intent, Tag } from "@blueprintjs/core"; +import { Example, ExampleProps } from "@blueprintjs/docs-theme"; +import { IconNames } from "@blueprintjs/icons"; + +export class CardListExample extends React.PureComponent { + public render() { + return ( + + +
+
My french reciepes
+
+ + + Chicken Basquaise + + + + Tarte Flambée + + + + Pain au Chocolat + + + +
+ + + + Olive oil + + + + Ground black pepper + + + + Carrots + + Added + + + + Onions + + + +
+ ); + } +} diff --git a/packages/docs-app/src/examples/core-examples/index.ts b/packages/docs-app/src/examples/core-examples/index.ts index 9b82562c05..b65437fd26 100644 --- a/packages/docs-app/src/examples/core-examples/index.ts +++ b/packages/docs-app/src/examples/core-examples/index.ts @@ -24,6 +24,7 @@ export * from "./calloutExample"; export * from "./checkboxExample"; export * from "./collapseExample"; export * from "./cardExample"; +export * from "./cardListExample"; export { ContextMenuExample } from "./contextMenuExample"; export { ContextMenuPopoverExample } from "./contextMenuPopoverExample"; export * from "./controlGroupExample"; From e84529f67793d0bbac25a62168a39ab9f2316e6a Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 16:03:11 +0200 Subject: [PATCH 04/21] Initial commit --- packages/core/src/common/classes.ts | 9 ++ packages/core/src/components/_index.scss | 1 + packages/core/src/components/components.md | 1 + packages/core/src/components/index.ts | 2 + .../core/src/components/section/_section.scss | 59 ++++++++++ .../components/section/section-content.tsx | 44 ++++++++ .../core/src/components/section/section.md | 17 +++ .../core/src/components/section/section.tsx | 89 +++++++++++++++ .../src/examples/core-examples/index.ts | 1 + .../examples/core-examples/sectionExample.tsx | 106 ++++++++++++++++++ 10 files changed, 329 insertions(+) create mode 100644 packages/core/src/components/section/_section.scss create mode 100644 packages/core/src/components/section/section-content.tsx create mode 100644 packages/core/src/components/section/section.md create mode 100644 packages/core/src/components/section/section.tsx create mode 100644 packages/docs-app/src/examples/core-examples/sectionExample.tsx diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index a1f2f66ef7..48c3c9ca11 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -73,6 +73,7 @@ export const FOCUS_DISABLED = `${NS}-focus-disabled`; export const FOCUS_STYLE_MANAGER_IGNORE = `${NS}-focus-style-manager-ignore`; export const CONTAINED = `${NS}-contained`; +export const PADDED = `${NS}-padded`; // text utilities export const UI_TEXT = `${NS}-ui-text`; @@ -218,6 +219,14 @@ export const MULTISTEP_DIALOG_RIGHT_PANEL = `${MULTISTEP_DIALOG}-right-panel`; export const MULTISTEP_DIALOG_NAV_TOP = `${MULTISTEP_DIALOG}-nav-top`; export const MULTISTEP_DIALOG_NAV_RIGHT = `${MULTISTEP_DIALOG}-nav-right`; +export const SECTION = `${NS}-section`; +export const SECTION_HEADER = `${SECTION}-header`; +export const SECTION_HEADER_LEFT = `${SECTION_HEADER}-left`; +export const SECTION_HEADER_TITLE = `${SECTION_HEADER}-title`; +export const SECTION_HEADER_SUB_TITLE = `${SECTION_HEADER}-sub-title`; +export const SECTION_HEADER_RIGHT = `${SECTION_HEADER}-right`; +export const SECTION_CONTENT = `${SECTION}-content`; + export const NAVBAR = `${NS}-navbar`; export const NAVBAR_GROUP = `${NAVBAR}-group`; export const NAVBAR_HEADING = `${NAVBAR}-heading`; diff --git a/packages/core/src/components/_index.scss b/packages/core/src/components/_index.scss index 1c937565b5..43483c3ba9 100644 --- a/packages/core/src/components/_index.scss +++ b/packages/core/src/components/_index.scss @@ -32,6 +32,7 @@ @import "popover/popover"; @import "portal/portal"; @import "progress-bar/progress-bar"; +@import "section/section"; @import "skeleton/skeleton"; @import "slider/slider"; @import "spinner/spinner"; diff --git a/packages/core/src/components/components.md b/packages/core/src/components/components.md index 079f687d00..348e10a96b 100644 --- a/packages/core/src/components/components.md +++ b/packages/core/src/components/components.md @@ -23,6 +23,7 @@ @page panel-stack2 @page progress-bar @page resize-sensor +@page section @page skeleton @page spinner @page tabs diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index 73fbd1758d..38f7ef9576 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -98,6 +98,8 @@ export { ResizeEntry, ResizeSensor, ResizeSensorProps } from "./resize-sensor/re export { HandleHtmlProps, HandleInteractionKind, HandleProps, HandleType } from "./slider/handleProps"; export { MultiSlider, MultiSliderProps, SliderBaseProps } from "./slider/multiSlider"; export { NumberRange, RangeSlider, RangeSliderProps } from "./slider/rangeSlider"; +export { Section, SectionProps } from "./section/section"; +export { SectionContent, SectionContentProps } from "./section/section-content"; export { Slider, SliderProps } from "./slider/slider"; export { Spinner, SpinnerProps, SpinnerSize } from "./spinner/spinner"; export { Tab, TabId, TabProps } from "./tabs/tab"; diff --git a/packages/core/src/components/section/_section.scss b/packages/core/src/components/section/_section.scss new file mode 100644 index 0000000000..027202a92d --- /dev/null +++ b/packages/core/src/components/section/_section.scss @@ -0,0 +1,59 @@ +@use "sass:math"; +@import "../../common/variables"; + +.#{$ns}-section { + padding: 0; + width: 100%; + + &-header { + align-items: stretch; + display: flex; + justify-content: space-between; + position: relative; + width: 100%; + gap: $pt-grid-size; + min-height: 50px; + padding: $pt-grid-size $pt-grid-size $pt-grid-size $pt-grid-size * 2; + + border-bottom: 1px solid $pt-divider-black; + + &.#{$ns}-dark, + .#{$ns}-dark & { + border-color: $pt-dark-divider-white; + } + + &-left { + align-items: center; + display: flex; + gap: $pt-grid-size; + } + + &-title { + margin-bottom: 0; + } + + &-sub-title { + margin-top: 2px; + } + + &-right { + align-items: center; + display: flex; + } + } + + &-content { + &.#{$ns}-padded { + padding: $pt-grid-size * 2; + } + + &:not(:last-child) { + border-bottom: 1px solid $pt-divider-black; + + &.#{$ns}-dark, + .#{$ns}-dark & { + border-color: $pt-dark-divider-white; + } + } + } +} diff --git a/packages/core/src/components/section/section-content.tsx b/packages/core/src/components/section/section-content.tsx new file mode 100644 index 0000000000..04d6b8abce --- /dev/null +++ b/packages/core/src/components/section/section-content.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { Classes } from "../../common"; +import { DISPLAYNAME_PREFIX, HTMLDivProps, Props } from "../../common/props"; + +export interface SectionContentProps extends Props, HTMLDivProps, React.RefAttributes { + padded?: boolean; +} + +/** + * Section content component. + * + * @see https://blueprintjs.com/docs/#core/components/section-content + */ +export const SectionContent: React.FC = React.forwardRef((props, ref) => { + const { className, children, padded, ...htmlProps } = props; + const classes = classNames(Classes.SECTION_CONTENT, { [Classes.PADDED]: padded }, className); + return ( +
+ {children} +
+ ); +}); +SectionContent.defaultProps = { + padded: true, +}; +SectionContent.displayName = `${DISPLAYNAME_PREFIX}.SectionContent`; diff --git a/packages/core/src/components/section/section.md b/packages/core/src/components/section/section.md new file mode 100644 index 0000000000..d987c4c6bf --- /dev/null +++ b/packages/core/src/components/section/section.md @@ -0,0 +1,17 @@ +@# Section + +todo + +@reactExample SectionExample + +@## Props + +@interface SectionProps + +@# Section Content + +- Multiple section content can be added under one section, they will be stacked. + +@## Props + +@interface SectionContentProps diff --git a/packages/core/src/components/section/section.tsx b/packages/core/src/components/section/section.tsx new file mode 100644 index 0000000000..1817076c2b --- /dev/null +++ b/packages/core/src/components/section/section.tsx @@ -0,0 +1,89 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { IconName } from "@blueprintjs/icons"; + +import { Classes, Elevation } from "../../common"; +import { SECTION_HEADER } from "../../common/classes"; +import { DISPLAYNAME_PREFIX, MaybeElement, Props } from "../../common/props"; +import { Card, CardProps } from "../card/card"; +import { H6 } from "../html/html"; +import { Icon } from "../icon/icon"; + +export interface SectionProps + extends Props, + Omit, + React.RefAttributes { + /** + * Name of a Blueprint UI icon (or an icon element) to render in the + * section's header. Note that the header will only be rendered if `sectionTitle` is + * provided. + */ + icon?: IconName | MaybeElement; + + /** + * Title of the section. + */ + sectionTitle?: JSX.Element | string; + + /** + * Sub-title of the section. + * Note that the header will only be rendered if `sectionTitle` is provided. + */ + subtitle?: JSX.Element | string; + + /** + * Element to render on the right side of the section header + */ + rightItem?: JSX.Element; +} + +/** + * Section component. + * + * @see https://blueprintjs.com/docs/#core/components/section + */ +export const Section: React.FC = React.forwardRef((props, ref) => { + const { className, icon, sectionTitle, rightItem, subtitle, children, ...cardProps } = props; + const classes = classNames(Classes.SECTION, className); + return ( + +
+
+ {sectionTitle && icon && ( + + )} +
+ {sectionTitle &&
{sectionTitle}
} + {sectionTitle && subtitle && ( +
+ {subtitle} +
+ )} +
+
+ {rightItem &&
{rightItem}
} +
+ + {children} +
+ ); +}); +Section.defaultProps = {}; +Section.displayName = `${DISPLAYNAME_PREFIX}.Section`; diff --git a/packages/docs-app/src/examples/core-examples/index.ts b/packages/docs-app/src/examples/core-examples/index.ts index b65437fd26..47fe9b2183 100644 --- a/packages/docs-app/src/examples/core-examples/index.ts +++ b/packages/docs-app/src/examples/core-examples/index.ts @@ -64,6 +64,7 @@ export * from "./popoverSizingExample"; export * from "./progressExample"; export * from "./rangeSliderExample"; export * from "./radioExample"; +export * from "./sectionExample"; export * from "./sliderExample"; export * from "./switchExample"; export * from "./tagInputExample"; diff --git a/packages/docs-app/src/examples/core-examples/sectionExample.tsx b/packages/docs-app/src/examples/core-examples/sectionExample.tsx new file mode 100644 index 0000000000..0666453f9d --- /dev/null +++ b/packages/docs-app/src/examples/core-examples/sectionExample.tsx @@ -0,0 +1,106 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as React from "react"; + +import { Button, Classes, H5, Intent, Section, SectionContent, Switch, Text } from "@blueprintjs/core"; +import { Example, ExampleProps } from "@blueprintjs/docs-theme"; +import { IconNames } from "@blueprintjs/icons"; + +export interface SectionExampleState { + hasIcon: boolean; + hasDescription: boolean; + hasRightItem: boolean; + hasMultipleSectionContent: boolean; +} + +export class SectionExample extends React.PureComponent { + public state: SectionExampleState = { + hasDescription: false, + hasIcon: false, + hasMultipleSectionContent: false, + hasRightItem: true, + }; + + public render() { + const { hasDescription, hasIcon, hasRightItem, hasMultipleSectionContent } = this.state; + + const options = ( + <> +
Props
+ + + + + + ); + + return ( + +
+ Edit + + ) : undefined + } + > + + + Basil; Ocimum basilicum, also called great basil, is a culinary herb of the family Lamiaceae + (mints). It is a tender plant, and is used in cuisines worldwide. In Western cuisine, the + generic term "basil" refers to the variety also known as sweet basil or Genovese basil. + Basil is native to tropical regions from Central Africa to Southeast Asia. + + + + {hasMultipleSectionContent && ( + +
+
+ KingdomPlantae +
+
+ CladeTracheophytes +
+
+ FamilyLamiaceae +
+
+
+ )} +
+
+ ); + } + + private handleIconChange = () => this.setState({ hasIcon: !this.state.hasIcon }); + + private handleDescriptionChange = () => this.setState({ hasDescription: !this.state.hasDescription }); + + private handleRightItemChange = () => this.setState({ hasRightItem: !this.state.hasRightItem }); + + private handleMultpleSectionContentChange = () => + this.setState({ hasMultipleSectionContent: !this.state.hasMultipleSectionContent }); +} From b6e165c0481bdcf9ed92f09d414c114b3eb098f6 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 16:21:25 +0200 Subject: [PATCH 05/21] Use section --- .../core-examples/cardListExample.tsx | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/docs-app/src/examples/core-examples/cardListExample.tsx b/packages/docs-app/src/examples/core-examples/cardListExample.tsx index c2750de9fb..0f3d0a9f98 100644 --- a/packages/docs-app/src/examples/core-examples/cardListExample.tsx +++ b/packages/docs-app/src/examples/core-examples/cardListExample.tsx @@ -16,7 +16,7 @@ import * as React from "react"; -import { Button, Card, CardList, Classes, H6, Icon, Intent, Tag } from "@blueprintjs/core"; +import { Button, Card, CardList, Classes, Icon, Intent, Section, SectionContent, Tag } from "@blueprintjs/core"; import { Example, ExampleProps } from "@blueprintjs/docs-theme"; import { IconNames } from "@blueprintjs/icons"; @@ -24,25 +24,24 @@ export class CardListExample extends React.PureComponent { public render() { return ( - -
-
My french reciepes
-
- - - Chicken Basquaise - - - - Tarte Flambée - - - - Pain au Chocolat - - - -
+
+ + + + Chicken Basquaise + + + + Tarte Flambée + + + + Pain au Chocolat + + + + +
From bd5a2f4eea0654ae156d714d3d32304646b3c308 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 18:20:20 +0200 Subject: [PATCH 06/21] Small --- packages/core/src/common/_color-aliases.scss | 3 + .../src/components/card-list/card-list.md | 24 +--- .../src/components/card-list/card-list.scss | 23 +++- .../src/components/card-list/card-list.tsx | 17 ++- .../core-examples/cardListExample.tsx | 115 +++++++++++------- 5 files changed, 106 insertions(+), 76 deletions(-) diff --git a/packages/core/src/common/_color-aliases.scss b/packages/core/src/common/_color-aliases.scss index c81def3560..7ea06d1a90 100644 --- a/packages/core/src/common/_color-aliases.scss +++ b/packages/core/src/common/_color-aliases.scss @@ -43,8 +43,11 @@ $pt-dark-icon-color-disabled: $pt-dark-text-color-disabled !default; $pt-dark-icon-color-selected: $pt-intent-primary !default; $pt-divider-black: rgba($black, 0.15) !default; +$pt-divider-black-muted: rgba($black, 0.1) !default; $pt-dark-divider-black: rgba($black, 0.4) !default; +$pt-dark-divider-black-muted: rgba($black, 0.2) !default; $pt-dark-divider-white: rgba($white, 0.2) !default; +$pt-dark-divider-white-muted: rgba($white, 0.1) !default; $pt-code-text-color: $pt-text-color-muted !default; $pt-dark-code-text-color: $pt-dark-text-color-muted !default; diff --git a/packages/core/src/components/card-list/card-list.md b/packages/core/src/components/card-list/card-list.md index 1dc5d6459d..b2cf0eb274 100644 --- a/packages/core/src/components/card-list/card-list.md +++ b/packages/core/src/components/card-list/card-list.md @@ -1,33 +1,19 @@ @# Card List -A card list is a wrapper around Cards. Compared to stand-alone cards, it can be used to reduce visual weight and allows inner scrolling. +Simple wrapper around Cards. Can be used to reduce visual weight and have inner scrolling. @reactExample CardListExample - @## Props ```tsx import { Button, Card, Elevation } from "@blueprintjs/core"; - - - Chicken Basquaise - - - - - Tarte Flambée - - - - - Pain au Chocolat - - + + Olive oil + Ground black pepper + Carrots ``` @interface CardListProps - -@css card-list diff --git a/packages/core/src/components/card-list/card-list.scss b/packages/core/src/components/card-list/card-list.scss index fe85595cc1..389f6920ab 100644 --- a/packages/core/src/components/card-list/card-list.scss +++ b/packages/core/src/components/card-list/card-list.scss @@ -1,5 +1,11 @@ @import "../../common/variables"; +$card-default-min-height: 50px; +$card-default-padding: 10px 20px; + +$card-small-min-height: 40px; +$card-small-padding: 7px 15px; + .#{$ns}-card-list { width: 100%; overflow: auto; @@ -23,13 +29,13 @@ > .#{$ns}-card { border-radius: 0; box-shadow: none; - min-height: 50px; - padding-top: 10px; - padding-bottom: 10px; + min-height: $card-default-min-height; + padding: $card-default-padding; display: flex; align-items: center; - &.#{$ns}-interactive:hover { + &.#{$ns}-interactive:hover, + &.#{$ns}-interactive:active { background-color: $light-gray5; box-shadow: none; @@ -40,11 +46,16 @@ } &:not(:last-child) > .#{$ns}-card { - border-bottom: 1px solid $pt-divider-black; + border-bottom: 1px solid $pt-divider-black-muted; .#{$ns}-dark & { - border-color: $pt-dark-divider-white; + border-color: $pt-dark-divider-white-muted; } } } + + &.#{$ns}-small .#{$ns}-card-list-item > .#{$ns}-card { + min-height: $card-small-min-height; + padding: $card-small-padding; + } } diff --git a/packages/core/src/components/card-list/card-list.tsx b/packages/core/src/components/card-list/card-list.tsx index 27ba72dbcc..6260204e44 100644 --- a/packages/core/src/components/card-list/card-list.tsx +++ b/packages/core/src/components/card-list/card-list.tsx @@ -26,13 +26,21 @@ export interface CardListProps extends Props, HTMLDivProps, React.RefAttributes< * Elevation and border radius will be removed. */ contained?: boolean; + + /** Whether card list items should use small styles. */ + small?: boolean; } export const CardList: React.FC = React.forwardRef((props, ref) => { - const { className, contained, children, ...htmlProps } = props; + const { className, contained, children, small, ...htmlProps } = props; - const renderableChildren = React.Children.toArray(children); // .filter(isRenderable); - const classes = classNames(Classes.CARD_LIST, { [Classes.CONTAINED]: contained }, className); + const renderableChildren = React.Children.toArray(children); + const classes = classNames( + Classes.CARD_LIST, + { [Classes.CONTAINED]: contained }, + { [Classes.SMALL]: small }, + className, + ); return renderableChildren.length > 0 ? ( @@ -47,7 +55,6 @@ export const CardList: React.FC = React.forwardRef((props, ref) = CardList.defaultProps = { contained: false, + small: false, }; CardList.displayName = `${DISPLAYNAME_PREFIX}.CardList`; - -// const isRenderable = (o: T | undefined | null | boolean) => isNonNullable(o) && isNotBoolean(o); diff --git a/packages/docs-app/src/examples/core-examples/cardListExample.tsx b/packages/docs-app/src/examples/core-examples/cardListExample.tsx index 0f3d0a9f98..a7677570a4 100644 --- a/packages/docs-app/src/examples/core-examples/cardListExample.tsx +++ b/packages/docs-app/src/examples/core-examples/cardListExample.tsx @@ -16,60 +16,83 @@ import * as React from "react"; -import { Button, Card, CardList, Classes, Icon, Intent, Section, SectionContent, Tag } from "@blueprintjs/core"; +import { Button, Card, CardList, Classes, H5, Icon, Intent, Section, SectionContent, Switch } from "@blueprintjs/core"; import { Example, ExampleProps } from "@blueprintjs/docs-theme"; import { IconNames } from "@blueprintjs/icons"; +export interface CardListExampleState { + isContained: boolean; + hasInteractiveCards: boolean; + isSmall: boolean; +} + export class CardListExample extends React.PureComponent { + public state: CardListExampleState = { + hasInteractiveCards: true, + isContained: false, + isSmall: false, + }; + public render() { + const { isContained, hasInteractiveCards, isSmall } = this.state; + + const options = ( + <> +
Props
+ + +
Example
+ + + ); + return ( - -
- - - - Chicken Basquaise - - - - Tarte Flambée - - - - Pain au Chocolat - - - - -
+ + {isContained ? ( +
+ {this.renderList()} +
+ ) : ( + this.renderList() + )} +
+ ); + } - - - Olive oil - - - - Ground black pepper - - - - Carrots - - Added - - - - Onions - + private renderList() { + const { hasInteractiveCards, isContained, isSmall } = this.state; + const ingredients: string[] = ["Olive oil", "Ground black pepper", "Carrots", "Onions"]; + + return ( + + {ingredients.map(ingredient => ( + + {ingredient} + {hasInteractiveCards ? ( + + ) : ( + + )} - -
+ ))} +
); } + + private handleSmallChange = () => this.setState({ isSmall: !this.state.isSmall }); + + private handleContainedChange = () => this.setState({ isContained: !this.state.isContained }); + + private handleInteractiveCardsChange = () => + this.setState({ hasInteractiveCards: !this.state.hasInteractiveCards }); } From 4322ae2320ff7bc414d4a72c01422d31096a940f Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 18:30:44 +0200 Subject: [PATCH 07/21] small --- .../core/src/components/section/_section.scss | 23 ++++++++++++++++--- .../core/src/components/section/section.tsx | 7 ++++-- .../examples/core-examples/sectionExample.tsx | 8 ++++++- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/core/src/components/section/_section.scss b/packages/core/src/components/section/_section.scss index 027202a92d..dc66910b99 100644 --- a/packages/core/src/components/section/_section.scss +++ b/packages/core/src/components/section/_section.scss @@ -1,6 +1,12 @@ @use "sass:math"; @import "../../common/variables"; +$section-default-min-height: 50px; +$section-default-padding: $pt-grid-size * 2; + +$section-small-min-height: 40px; +$section-small-padding: 15px; + .#{$ns}-section { padding: 0; width: 100%; @@ -12,8 +18,8 @@ position: relative; width: 100%; gap: $pt-grid-size; - min-height: 50px; - padding: $pt-grid-size $pt-grid-size $pt-grid-size $pt-grid-size * 2; + min-height: $section-default-min-height; + padding: $pt-grid-size $pt-grid-size $pt-grid-size $section-default-padding; border-bottom: 1px solid $pt-divider-black; @@ -44,7 +50,7 @@ &-content { &.#{$ns}-padded { - padding: $pt-grid-size * 2; + padding: $section-default-padding; } &:not(:last-child) { @@ -56,4 +62,15 @@ } } } + + &.#{$ns}-small { + .#{$ns}-section-header { + min-height: $section-small-min-height; + padding: 7px 7px 7px $section-small-padding; + } + + .#{$ns}-section-content { + padding: $section-small-padding; + } + } } diff --git a/packages/core/src/components/section/section.tsx b/packages/core/src/components/section/section.tsx index 1817076c2b..0ec89f252e 100644 --- a/packages/core/src/components/section/section.tsx +++ b/packages/core/src/components/section/section.tsx @@ -52,6 +52,9 @@ export interface SectionProps * Element to render on the right side of the section header */ rightItem?: JSX.Element; + + /** Whether this section should use small styles. */ + small?: boolean; } /** @@ -60,8 +63,8 @@ export interface SectionProps * @see https://blueprintjs.com/docs/#core/components/section */ export const Section: React.FC = React.forwardRef((props, ref) => { - const { className, icon, sectionTitle, rightItem, subtitle, children, ...cardProps } = props; - const classes = classNames(Classes.SECTION, className); + const { className, icon, sectionTitle, rightItem, subtitle, children, small, ...cardProps } = props; + const classes = classNames(Classes.SECTION, { [Classes.SMALL]: small }, className); return (
diff --git a/packages/docs-app/src/examples/core-examples/sectionExample.tsx b/packages/docs-app/src/examples/core-examples/sectionExample.tsx index 0666453f9d..83453fb6ce 100644 --- a/packages/docs-app/src/examples/core-examples/sectionExample.tsx +++ b/packages/docs-app/src/examples/core-examples/sectionExample.tsx @@ -25,6 +25,7 @@ export interface SectionExampleState { hasDescription: boolean; hasRightItem: boolean; hasMultipleSectionContent: boolean; + isSmall: boolean; } export class SectionExample extends React.PureComponent { @@ -33,14 +34,16 @@ export class SectionExample extends React.PureComponent
Props
+ @@ -55,6 +58,7 @@ export class SectionExample extends React.PureComponent
this.setState({ isSmall: !this.state.isSmall }); + private handleIconChange = () => this.setState({ hasIcon: !this.state.hasIcon }); private handleDescriptionChange = () => this.setState({ hasDescription: !this.state.hasDescription }); From ec6c339754b18aee7573f8f92ba085233a55a019 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 18:40:57 +0200 Subject: [PATCH 08/21] small section --- .../docs-app/src/examples/core-examples/cardListExample.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-app/src/examples/core-examples/cardListExample.tsx b/packages/docs-app/src/examples/core-examples/cardListExample.tsx index a7677570a4..60c5e42de2 100644 --- a/packages/docs-app/src/examples/core-examples/cardListExample.tsx +++ b/packages/docs-app/src/examples/core-examples/cardListExample.tsx @@ -53,7 +53,7 @@ export class CardListExample extends React.PureComponent { return ( {isContained ? ( -
+
{this.renderList()}
) : ( From 06504ac63fd1581a482465493f10a685cfe1ba6d Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 26 Jun 2023 18:43:49 +0200 Subject: [PATCH 09/21] padded --- packages/core/src/components/section/_section.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/components/section/_section.scss b/packages/core/src/components/section/_section.scss index dc66910b99..d5eee6df5e 100644 --- a/packages/core/src/components/section/_section.scss +++ b/packages/core/src/components/section/_section.scss @@ -69,7 +69,7 @@ $section-small-padding: 15px; padding: 7px 7px 7px $section-small-padding; } - .#{$ns}-section-content { + .#{$ns}-section-content.#{$ns}-padded { padding: $section-small-padding; } } From 9e46523b18a3df12e64f8621d5dd9829e6619f4e Mon Sep 17 00:00:00 2001 From: Charles Date: Tue, 27 Jun 2023 01:34:52 +0200 Subject: [PATCH 10/21] Switch card --- packages/core/src/common/classes.ts | 2 + packages/core/src/components/card/_card.scss | 7 +++ packages/core/src/components/card/card.md | 4 ++ .../core/src/components/card/switch-card.tsx | 56 +++++++++++++++++++ packages/core/src/components/index.ts | 1 + .../src/examples/core-examples/index.ts | 1 + .../core-examples/switchCardExample.tsx | 47 ++++++++++++++++ 7 files changed, 118 insertions(+) create mode 100644 packages/core/src/components/card/switch-card.tsx create mode 100644 packages/docs-app/src/examples/core-examples/switchCardExample.tsx diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index 48c3c9ca11..818ef27f7a 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -115,6 +115,8 @@ export const CALLOUT_ICON = `${CALLOUT}-icon`; export const CARD = `${NS}-card`; +export const CARD_SWITCH = `${NS}-card-switch`; + export const CARD_LIST = `${NS}-card-list`; export const CARD_LIST_ITEM = `${CARD_LIST}-item`; diff --git a/packages/core/src/components/card/_card.scss b/packages/core/src/components/card/_card.scss index e97162a7de..cb8c52e78b 100644 --- a/packages/core/src/components/card/_card.scss +++ b/packages/core/src/components/card/_card.scss @@ -99,3 +99,10 @@ $dark-elevation-shadows: ( } } } + +.#{$ns}-card-switch { + & > .#{$ns}-switch.#{$ns}-control.#{$ns}-inline { + margin: 0; + width: 100%; + } +} diff --git a/packages/core/src/components/card/card.md b/packages/core/src/components/card/card.md index 330fd562c3..19ee0e6307 100644 --- a/packages/core/src/components/card/card.md +++ b/packages/core/src/components/card/card.md @@ -38,3 +38,7 @@ interactions. When you hover over cards with this class applied, the mouse changes to a pointer and increases the elevation shadow on the card. @css card + +@# Switch Card + +@reactExample SwitchCardExample diff --git a/packages/core/src/components/card/switch-card.tsx b/packages/core/src/components/card/switch-card.tsx new file mode 100644 index 0000000000..2df77e8625 --- /dev/null +++ b/packages/core/src/components/card/switch-card.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { Alignment, Classes } from "../../common"; +import { DISPLAYNAME_PREFIX, Props } from "../../common/props"; +import { Switch, SwitchProps } from "../forms/controls"; +import { Card, CardProps } from "./card"; + +export interface SwitchCardProps extends Props, SwitchProps { + cardProps?: CardProps; +} + +/** + * Switch Card component. + * + * @see https://blueprintjs.com/docs/#core/components/card#switch-card + */ +export const SwitchCard: React.FC = React.forwardRef(props => { + const { className, cardProps, children, ...switchProps } = props; + const classes = classNames(Classes.CARD_SWITCH, className); + + const isControlled = switchProps.checked != null; + + const handleChange = React.useCallback( + evemt => { + if (isControlled && switchProps.onChange != null) { + switchProps?.onChange(evemt); + } + }, + [switchProps], + ); + + return ( + + + + ); +}); +SwitchCard.defaultProps = {}; +SwitchCard.displayName = `${DISPLAYNAME_PREFIX}.SwitchCard`; diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index 38f7ef9576..80517f3e68 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -102,6 +102,7 @@ export { Section, SectionProps } from "./section/section"; export { SectionContent, SectionContentProps } from "./section/section-content"; export { Slider, SliderProps } from "./slider/slider"; export { Spinner, SpinnerProps, SpinnerSize } from "./spinner/spinner"; +export { SwitchCard, SwitchCardProps } from "./card/switch-card"; export { Tab, TabId, TabProps } from "./tabs/tab"; // eslint-disable-next-line deprecation/deprecation export { Tabs, TabsProps, TabsExpander, Expander } from "./tabs/tabs"; diff --git a/packages/docs-app/src/examples/core-examples/index.ts b/packages/docs-app/src/examples/core-examples/index.ts index 47fe9b2183..45fd13685f 100644 --- a/packages/docs-app/src/examples/core-examples/index.ts +++ b/packages/docs-app/src/examples/core-examples/index.ts @@ -67,6 +67,7 @@ export * from "./radioExample"; export * from "./sectionExample"; export * from "./sliderExample"; export * from "./switchExample"; +export * from "./switchCardExample"; export * from "./tagInputExample"; export * from "./textAreaExample"; export * from "./textExample"; diff --git a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx new file mode 100644 index 0000000000..1f3e24f446 --- /dev/null +++ b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as React from "react"; + +import { CardList, SwitchCard } from "@blueprintjs/core"; +import { Example, ExampleProps } from "@blueprintjs/docs-theme"; + +export class SwitchCardExample extends React.PureComponent { + public render() { + return ( + +
+ Wifi + Bluetooth + NFC +
+ + + Wifi + Bluetooth + NFC + +
+ ); + } +} From 37f404a97b04f34dc3ff56fb09184e16992a8ec2 Mon Sep 17 00:00:00 2001 From: Charles Date: Tue, 27 Jun 2023 12:56:24 +0200 Subject: [PATCH 11/21] Event --- .../src/components/card/checkbox-card.tsx | 56 +++++++++++++++++++ .../core/src/components/card/switch-card.tsx | 25 ++++++--- .../core-examples/checkboxCardExample.tsx | 47 ++++++++++++++++ .../core-examples/switchCardExample.tsx | 43 +++++++++++--- 4 files changed, 156 insertions(+), 15 deletions(-) create mode 100644 packages/core/src/components/card/checkbox-card.tsx create mode 100644 packages/docs-app/src/examples/core-examples/checkboxCardExample.tsx diff --git a/packages/core/src/components/card/checkbox-card.tsx b/packages/core/src/components/card/checkbox-card.tsx new file mode 100644 index 0000000000..1dc2296709 --- /dev/null +++ b/packages/core/src/components/card/checkbox-card.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { Classes } from "../../common"; +import { DISPLAYNAME_PREFIX, Props } from "../../common/props"; +import { Checkbox, CheckboxProps } from "../forms/controls"; +import { Card, CardProps } from "./card"; + +export interface CheckboxCardProps extends Props, CheckboxProps { + cardProps?: CardProps; +} + +/** + * Checkbox Card component. + * + * @see https://blueprintjs.com/docs/#core/components/card#checkbox-card + */ +export const CheckBoxCard: React.FC = React.forwardRef(props => { + const { className, cardProps, children, ...switchProps } = props; + const classes = classNames(Classes.CARD_SWITCH, className); + + const isControlled = switchProps.checked != null; + + const handleChange = React.useCallback( + evemt => { + if (isControlled && switchProps.onChange != null) { + switchProps?.onChange(evemt); + } + }, + [switchProps], + ); + + return ( + + + + ); +}); +CheckBoxCard.defaultProps = {}; +CheckBoxCard.displayName = `${DISPLAYNAME_PREFIX}.SwitchCard`; diff --git a/packages/core/src/components/card/switch-card.tsx b/packages/core/src/components/card/switch-card.tsx index 2df77e8625..16e81b4d43 100644 --- a/packages/core/src/components/card/switch-card.tsx +++ b/packages/core/src/components/card/switch-card.tsx @@ -18,11 +18,11 @@ import classNames from "classnames"; import * as React from "react"; import { Alignment, Classes } from "../../common"; -import { DISPLAYNAME_PREFIX, Props } from "../../common/props"; +import { DISPLAYNAME_PREFIX } from "../../common/props"; import { Switch, SwitchProps } from "../forms/controls"; import { Card, CardProps } from "./card"; -export interface SwitchCardProps extends Props, SwitchProps { +export interface SwitchCardProps extends SwitchProps { cardProps?: CardProps; } @@ -32,23 +32,32 @@ export interface SwitchCardProps extends Props, SwitchProps { * @see https://blueprintjs.com/docs/#core/components/card#switch-card */ export const SwitchCard: React.FC = React.forwardRef(props => { - const { className, cardProps, children, ...switchProps } = props; + const { className, cardProps, children, onChange, ...switchProps } = props; const classes = classNames(Classes.CARD_SWITCH, className); - const isControlled = switchProps.checked != null; + const isControlled = switchProps.checked !== undefined; const handleChange = React.useCallback( evemt => { - if (isControlled && switchProps.onChange != null) { - switchProps?.onChange(evemt); + if (isControlled && onChange != null) { + onChange(evemt); + console.log("change"); + evemt?.stopPropagation(); } }, - [switchProps], + [onChange, isControlled], ); return ( - + ); }); diff --git a/packages/docs-app/src/examples/core-examples/checkboxCardExample.tsx b/packages/docs-app/src/examples/core-examples/checkboxCardExample.tsx new file mode 100644 index 0000000000..1f3e24f446 --- /dev/null +++ b/packages/docs-app/src/examples/core-examples/checkboxCardExample.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as React from "react"; + +import { CardList, SwitchCard } from "@blueprintjs/core"; +import { Example, ExampleProps } from "@blueprintjs/docs-theme"; + +export class SwitchCardExample extends React.PureComponent { + public render() { + return ( + +
+ Wifi + Bluetooth + NFC +
+ + + Wifi + Bluetooth + NFC + +
+ ); + } +} diff --git a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx index 1f3e24f446..dae4d22070 100644 --- a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx +++ b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx @@ -19,8 +19,18 @@ import * as React from "react"; import { CardList, SwitchCard } from "@blueprintjs/core"; import { Example, ExampleProps } from "@blueprintjs/docs-theme"; -export class SwitchCardExample extends React.PureComponent { +export interface SwitchCardExampleState { + switchChecked: boolean[]; +} + +export class SwitchCardExample extends React.PureComponent { + public state: SwitchCardExampleState = { + switchChecked: [false, true, false, true, true, false], + }; + public render() { + const { switchChecked } = this.state; + return (
{ width: "100%", }} > - Wifi - Bluetooth - NFC + + Wifi + + + Bluetooth + + + NFC +
- Wifi - Bluetooth - NFC + + Wifi + + + Bluetooth + + + NFC +
); } + + private handleSetCheckedState = (index: number) => () => { + const switchChecked = [...this.state.switchChecked]; + switchChecked[index] = !this.state.switchChecked[index]; + + this.setState({ switchChecked }); + }; } From f0158e1ecb0d67d4c929309b63ecdfebd2848582 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Tue, 27 Jun 2023 13:25:18 -0400 Subject: [PATCH 12/21] adjust padding, fix double click issue --- .../src/components/card-list/card-list.scss | 6 ++++ packages/core/src/components/card/_card.scss | 10 +++++-- packages/core/src/components/card/card.md | 6 ++-- .../core/src/components/card/switch-card.tsx | 28 +++---------------- .../core-examples/switchCardExample.tsx | 14 +++++----- 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/packages/core/src/components/card-list/card-list.scss b/packages/core/src/components/card-list/card-list.scss index 389f6920ab..3052100f27 100644 --- a/packages/core/src/components/card-list/card-list.scss +++ b/packages/core/src/components/card-list/card-list.scss @@ -52,6 +52,12 @@ $card-small-padding: 7px 15px; border-color: $pt-dark-divider-white-muted; } } + + > .#{$ns}-card-switch { + // Make the label take up the full size so that we can use the control's onChange instead of a "click" handler + // on any container element. This is partially to work around https://github.com/palantir/blueprint/issues/6251 + padding: 0; + } } &.#{$ns}-small .#{$ns}-card-list-item > .#{$ns}-card { diff --git a/packages/core/src/components/card/_card.scss b/packages/core/src/components/card/_card.scss index cb8c52e78b..0e5f171511 100644 --- a/packages/core/src/components/card/_card.scss +++ b/packages/core/src/components/card/_card.scss @@ -101,8 +101,14 @@ $dark-elevation-shadows: ( } .#{$ns}-card-switch { - & > .#{$ns}-switch.#{$ns}-control.#{$ns}-inline { + // Make the label take up the full size so that we can use the control's onChange instead of a "click" handler + // on any container element. This is partially to work around https://github.com/palantir/blueprint/issues/6251 + padding: 0; + + // N.B. Switch always has inline={true} inside SwitchCard + > .#{$ns}-switch.#{$ns}-control.#{$ns}-inline { margin: 0; - width: 100%; + padding: $card-padding; + width: calc(100% - #{$card-padding * 2}); } } diff --git a/packages/core/src/components/card/card.md b/packages/core/src/components/card/card.md index 19ee0e6307..b1687ec5b1 100644 --- a/packages/core/src/components/card/card.md +++ b/packages/core/src/components/card/card.md @@ -12,7 +12,7 @@ height in the UI. Five elevations are supported, from 0 to 4. The `Classes.ELEVATION_*` constants can be used on any element (not just a `Card`) to apply the drop shadow. -@## Props +@## Props interface This component is a simple stateless container for its children. @@ -28,7 +28,7 @@ import { Button, Card, Elevation } from "@blueprintjs/core"; @interface CardProps -@## CSS +@## CSS API Start with `.@ns-card` and add an elevation class `.@ns-elevation-*` to apply a drop shadow that simulates height in the UI. @@ -39,6 +39,6 @@ changes to a pointer and increases the elevation shadow on the card. @css card -@# Switch Card +@## Switch Card @reactExample SwitchCardExample diff --git a/packages/core/src/components/card/switch-card.tsx b/packages/core/src/components/card/switch-card.tsx index 16e81b4d43..86dec6e193 100644 --- a/packages/core/src/components/card/switch-card.tsx +++ b/packages/core/src/components/card/switch-card.tsx @@ -22,7 +22,7 @@ import { DISPLAYNAME_PREFIX } from "../../common/props"; import { Switch, SwitchProps } from "../forms/controls"; import { Card, CardProps } from "./card"; -export interface SwitchCardProps extends SwitchProps { +export interface SwitchCardProps extends Omit { cardProps?: CardProps; } @@ -32,32 +32,12 @@ export interface SwitchCardProps extends SwitchProps { * @see https://blueprintjs.com/docs/#core/components/card#switch-card */ export const SwitchCard: React.FC = React.forwardRef(props => { - const { className, cardProps, children, onChange, ...switchProps } = props; + const { className, cardProps, children, ...switchProps } = props; const classes = classNames(Classes.CARD_SWITCH, className); - const isControlled = switchProps.checked !== undefined; - - const handleChange = React.useCallback( - evemt => { - if (isControlled && onChange != null) { - onChange(evemt); - console.log("change"); - evemt?.stopPropagation(); - } - }, - [onChange, isControlled], - ); - return ( - - + + ); }); diff --git a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx index dae4d22070..8c637f23da 100644 --- a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx +++ b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx @@ -41,25 +41,25 @@ export class SwitchCardExample extends React.PureComponent - + Wifi - + Bluetooth - + NFC
- + Wifi - + Bluetooth - + NFC @@ -67,7 +67,7 @@ export class SwitchCardExample extends React.PureComponent () => { + private getSwitchChangeHandler = (index: number) => () => { const switchChecked = [...this.state.switchChecked]; switchChecked[index] = !this.state.switchChecked[index]; From e366ed9886e971f6fe44f9612a608634f90a56f7 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Wed, 5 Jul 2023 15:59:52 -0400 Subject: [PATCH 13/21] Fix layout of control indicator --- packages/core/src/components/card/_card.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/components/card/_card.scss b/packages/core/src/components/card/_card.scss index 0e5f171511..2823cddf5e 100644 --- a/packages/core/src/components/card/_card.scss +++ b/packages/core/src/components/card/_card.scss @@ -109,6 +109,10 @@ $dark-elevation-shadows: ( > .#{$ns}-switch.#{$ns}-control.#{$ns}-inline { margin: 0; padding: $card-padding; - width: calc(100% - #{$card-padding * 2}); + width: calc(100%); + + .#{$ns}-control-indicator { + margin: 0; + } } } From e4271c043d9b78acebe44977a0db4128dfea570f Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Tue, 5 Sep 2023 13:33:37 -0400 Subject: [PATCH 14/21] Fix bad merge --- packages/core/src/common/_color-aliases.scss | 1 - packages/core/src/common/classes.ts | 12 ++--- packages/core/src/components/card/card.md | 1 - .../components/section/section-content.tsx | 44 ------------------- 4 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 packages/core/src/components/section/section-content.tsx diff --git a/packages/core/src/common/_color-aliases.scss b/packages/core/src/common/_color-aliases.scss index 7ea06d1a90..42c22a1244 100644 --- a/packages/core/src/common/_color-aliases.scss +++ b/packages/core/src/common/_color-aliases.scss @@ -45,7 +45,6 @@ $pt-dark-icon-color-selected: $pt-intent-primary !default; $pt-divider-black: rgba($black, 0.15) !default; $pt-divider-black-muted: rgba($black, 0.1) !default; $pt-dark-divider-black: rgba($black, 0.4) !default; -$pt-dark-divider-black-muted: rgba($black, 0.2) !default; $pt-dark-divider-white: rgba($white, 0.2) !default; $pt-dark-divider-white-muted: rgba($white, 0.1) !default; diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index cf3109ba6e..6a0e4402ee 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -73,8 +73,6 @@ export const INTENT_DANGER = intentClass(Intent.DANGER)!; export const FOCUS_DISABLED = `${NS}-focus-disabled`; export const FOCUS_STYLE_MANAGER_IGNORE = `${NS}-focus-style-manager-ignore`; -export const CONTAINED = `${NS}-contained`; - // text utilities export const UI_TEXT = `${NS}-ui-text`; export const RUNNING_TEXT = `${NS}-running-text`; @@ -120,7 +118,6 @@ export const CARD_SWITCH = `${NS}-card-switch`; export const CARD_LIST = `${NS}-card-list`; export const CARD_LIST_BORDERED = `${CARD_LIST}-bordered`; -export const CARD_LIST_ITEM = `${CARD_LIST}-item`; export const COLLAPSE = `${NS}-collapse`; export const COLLAPSE_BODY = `${COLLAPSE}-body`; @@ -229,16 +226,15 @@ export const MULTISTEP_DIALOG_NAV_TOP = `${MULTISTEP_DIALOG}-nav-top`; export const MULTISTEP_DIALOG_NAV_RIGHT = `${MULTISTEP_DIALOG}-nav-right`; export const SECTION = `${NS}-section`; -export const SECTION_CARD = `${SECTION}-card`; export const SECTION_COLLAPSED = `${SECTION}-collapsed`; -export const SECTION_CONTENT = `${SECTION}-content`; export const SECTION_HEADER = `${SECTION}-header`; -export const SECTION_HEADER_DIVIDER = `${SECTION_HEADER}-divider`; export const SECTION_HEADER_LEFT = `${SECTION_HEADER}-left`; -export const SECTION_HEADER_RIGHT = `${SECTION_HEADER}-right`; +export const SECTION_HEADER_TITLE = `${SECTION_HEADER}-title`; export const SECTION_HEADER_SUB_TITLE = `${SECTION_HEADER}-sub-title`; +export const SECTION_HEADER_DIVIDER = `${SECTION_HEADER}-divider`; export const SECTION_HEADER_TABS = `${SECTION_HEADER}-tabs`; -export const SECTION_HEADER_TITLE = `${SECTION_HEADER}-title`; +export const SECTION_HEADER_RIGHT = `${SECTION_HEADER}-right`; +export const SECTION_CARD = `${SECTION}-card`; export const NAVBAR = `${NS}-navbar`; export const NAVBAR_GROUP = `${NAVBAR}-group`; diff --git a/packages/core/src/components/card/card.md b/packages/core/src/components/card/card.md index 28db2931a9..acb9d36884 100644 --- a/packages/core/src/components/card/card.md +++ b/packages/core/src/components/card/card.md @@ -40,7 +40,6 @@ often fall out of sync as the design system is updated. You should use the React - Start with `.@ns-card` and add an elevation class `.@ns-elevation-*` to apply a drop shadow that simulates height in the UI. diff --git a/packages/core/src/components/section/section-content.tsx b/packages/core/src/components/section/section-content.tsx deleted file mode 100644 index 04d6b8abce..0000000000 --- a/packages/core/src/components/section/section-content.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2023 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import classNames from "classnames"; -import * as React from "react"; - -import { Classes } from "../../common"; -import { DISPLAYNAME_PREFIX, HTMLDivProps, Props } from "../../common/props"; - -export interface SectionContentProps extends Props, HTMLDivProps, React.RefAttributes { - padded?: boolean; -} - -/** - * Section content component. - * - * @see https://blueprintjs.com/docs/#core/components/section-content - */ -export const SectionContent: React.FC = React.forwardRef((props, ref) => { - const { className, children, padded, ...htmlProps } = props; - const classes = classNames(Classes.SECTION_CONTENT, { [Classes.PADDED]: padded }, className); - return ( -
- {children} -
- ); -}); -SectionContent.defaultProps = { - padded: true, -}; -SectionContent.displayName = `${DISPLAYNAME_PREFIX}.SectionContent`; From 2f2fbe89ed9fdf95f1a2eb47be7ab887dd1b7230 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 5 Oct 2023 13:12:19 -0400 Subject: [PATCH 15/21] refactor SwitchCard, fix padding issue --- packages/core/src/common/classes.ts | 4 +- packages/core/src/components/_index.scss | 1 + packages/core/src/components/card/_card.scss | 17 --- .../src/components/card/_control-card.scss | 27 ++++ .../src/components/card/checkbox-card.tsx | 56 -------- .../core/src/components/card/controlCard.tsx | 90 ++++++++++++ .../card/{switch-card.tsx => switchCard.tsx} | 20 +-- .../core/src/components/forms/controls.tsx | 128 +++++++++--------- packages/core/src/components/index.ts | 2 +- 9 files changed, 187 insertions(+), 158 deletions(-) create mode 100644 packages/core/src/components/card/_control-card.scss delete mode 100644 packages/core/src/components/card/checkbox-card.tsx create mode 100644 packages/core/src/components/card/controlCard.tsx rename packages/core/src/components/card/{switch-card.tsx => switchCard.tsx} (60%) diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index 6a0e4402ee..786e4034a1 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -113,8 +113,8 @@ export const CALLOUT_HAS_BODY_CONTENT = `${CALLOUT}-has-body-content`; export const CALLOUT_ICON = `${CALLOUT}-icon`; export const CARD = `${NS}-card`; - -export const CARD_SWITCH = `${NS}-card-switch`; +export const CONTROL_CARD = `${NS}-control-card`; +export const SWITCH_CONTROL_CARD = `${NS}-switch-control-card`; export const CARD_LIST = `${NS}-card-list`; export const CARD_LIST_BORDERED = `${CARD_LIST}-bordered`; diff --git a/packages/core/src/components/_index.scss b/packages/core/src/components/_index.scss index 43483c3ba9..32261f1937 100644 --- a/packages/core/src/components/_index.scss +++ b/packages/core/src/components/_index.scss @@ -7,6 +7,7 @@ @import "button/button-group"; @import "callout/callout"; @import "card/card"; +@import "card/control-card"; @import "card-list/card-list"; @import "collapse/collapse"; @import "context-menu/context-menu"; diff --git a/packages/core/src/components/card/_card.scss b/packages/core/src/components/card/_card.scss index 2823cddf5e..e97162a7de 100644 --- a/packages/core/src/components/card/_card.scss +++ b/packages/core/src/components/card/_card.scss @@ -99,20 +99,3 @@ $dark-elevation-shadows: ( } } } - -.#{$ns}-card-switch { - // Make the label take up the full size so that we can use the control's onChange instead of a "click" handler - // on any container element. This is partially to work around https://github.com/palantir/blueprint/issues/6251 - padding: 0; - - // N.B. Switch always has inline={true} inside SwitchCard - > .#{$ns}-switch.#{$ns}-control.#{$ns}-inline { - margin: 0; - padding: $card-padding; - width: calc(100%); - - .#{$ns}-control-indicator { - margin: 0; - } - } -} diff --git a/packages/core/src/components/card/_control-card.scss b/packages/core/src/components/card/_control-card.scss new file mode 100644 index 0000000000..0f6543cb1d --- /dev/null +++ b/packages/core/src/components/card/_control-card.scss @@ -0,0 +1,27 @@ +// Copyright 2023 Palantir Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. + +@import "../../common/variables"; + +// use an extra selector to increase specificity +.#{$ns}-card.#{$ns}-control-card { + &, + .#{$ns}-card-list > & { + // Make the label take up the full size so that we can use the control's onChange instead of a "click" handler + // on any container element. This is partially to work around https://github.com/palantir/blueprint/issues/6251 + padding: 0; + } +} + +.#{$ns}-switch-control-card { + // N.B. Switch always has inline={true} inside SwitchCard + > .#{$ns}-switch.#{$ns}-control.#{$ns}-inline { + margin: 0; + padding: $card-padding; + width: calc(100%); + + .#{$ns}-control-indicator { + margin: 0; + } + } +} diff --git a/packages/core/src/components/card/checkbox-card.tsx b/packages/core/src/components/card/checkbox-card.tsx deleted file mode 100644 index 1dc2296709..0000000000 --- a/packages/core/src/components/card/checkbox-card.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2023 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import classNames from "classnames"; -import * as React from "react"; - -import { Classes } from "../../common"; -import { DISPLAYNAME_PREFIX, Props } from "../../common/props"; -import { Checkbox, CheckboxProps } from "../forms/controls"; -import { Card, CardProps } from "./card"; - -export interface CheckboxCardProps extends Props, CheckboxProps { - cardProps?: CardProps; -} - -/** - * Checkbox Card component. - * - * @see https://blueprintjs.com/docs/#core/components/card#checkbox-card - */ -export const CheckBoxCard: React.FC = React.forwardRef(props => { - const { className, cardProps, children, ...switchProps } = props; - const classes = classNames(Classes.CARD_SWITCH, className); - - const isControlled = switchProps.checked != null; - - const handleChange = React.useCallback( - evemt => { - if (isControlled && switchProps.onChange != null) { - switchProps?.onChange(evemt); - } - }, - [switchProps], - ); - - return ( - - - - ); -}); -CheckBoxCard.defaultProps = {}; -CheckBoxCard.displayName = `${DISPLAYNAME_PREFIX}.SwitchCard`; diff --git a/packages/core/src/components/card/controlCard.tsx b/packages/core/src/components/card/controlCard.tsx new file mode 100644 index 0000000000..35ac9df65b --- /dev/null +++ b/packages/core/src/components/card/controlCard.tsx @@ -0,0 +1,90 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import classNames from "classnames"; +import * as React from "react"; + +import { Classes } from "../../common"; +import { DISPLAYNAME_PREFIX, HTMLInputProps } from "../../common/props"; +import { ControlProps, Switch } from "../forms/controls"; +import { Card, CardProps } from "./card"; + +/** + * Subset of {@link Card} which can be used to adjust its behavior. + */ +type SupportedCardProps = Omit; + +/** + * Subset of {@link ControlProps} which can be used to adjust its behavior. + */ +type SupportedControlProps = Pick; + +export interface ControlCardProps extends SupportedCardProps, SupportedControlProps { + /** + * Which kind of form control to render inside the card. + */ + controlKind: "switch"; + + // N.B. this is split out of the root properties in the inerface because it would conflict with CardProps' HTMLDivProps + /** + * HTML input attributes to forward to the control `` element. + */ + inputProps?: HTMLInputProps; +} + +/** + * ControlCard component, used to render a {@link Card} with a form control. + * + * @internal + */ +export const ControlCard: React.FC = React.forwardRef(props => { + const { + checked, + children: controlLabel, + className, + controlKind, + defaultChecked, + disabled, + inputProps, + inputRef, + ...cardProps + } = props; + + const classes = classNames(Classes.CONTROL_CARD, className, { + [Classes.SWITCH_CONTROL_CARD]: controlKind === "switch", + }); + + const controlProps: ControlProps = { + checked, + defaultChecked, + disabled, + inputRef, + labelElement: controlLabel, + ...inputProps, + }; + + return ( + + {controlKind === "switch" ? ( + + ) : ( + controlLabel + )} + + ); +}); +ControlCard.defaultProps = {}; +ControlCard.displayName = `${DISPLAYNAME_PREFIX}.ControlCard`; diff --git a/packages/core/src/components/card/switch-card.tsx b/packages/core/src/components/card/switchCard.tsx similarity index 60% rename from packages/core/src/components/card/switch-card.tsx rename to packages/core/src/components/card/switchCard.tsx index 86dec6e193..6ab161271c 100644 --- a/packages/core/src/components/card/switch-card.tsx +++ b/packages/core/src/components/card/switchCard.tsx @@ -14,32 +14,20 @@ * limitations under the License. */ -import classNames from "classnames"; import * as React from "react"; -import { Alignment, Classes } from "../../common"; import { DISPLAYNAME_PREFIX } from "../../common/props"; -import { Switch, SwitchProps } from "../forms/controls"; -import { Card, CardProps } from "./card"; +import { ControlCard, ControlCardProps } from "./controlCard"; -export interface SwitchCardProps extends Omit { - cardProps?: CardProps; -} +export type SwitchCardProps = Omit; /** * Switch Card component. * * @see https://blueprintjs.com/docs/#core/components/card#switch-card */ -export const SwitchCard: React.FC = React.forwardRef(props => { - const { className, cardProps, children, ...switchProps } = props; - const classes = classNames(Classes.CARD_SWITCH, className); - - return ( - - - - ); +export const SwitchCard: React.FC = React.forwardRef((props, ref) => { + return ; }); SwitchCard.defaultProps = {}; SwitchCard.displayName = `${DISPLAYNAME_PREFIX}.SwitchCard`; diff --git a/packages/core/src/components/forms/controls.tsx b/packages/core/src/components/forms/controls.tsx index b57cae0e5a..959114ab30 100644 --- a/packages/core/src/components/forms/controls.tsx +++ b/packages/core/src/components/forms/controls.tsx @@ -92,47 +92,50 @@ interface ControlInternalProps extends ControlProps { /** * Renders common control elements, with additional props to customize appearance. - * This function is not exported and is only used within this module for `Checkbox`, `Radio`, and `Switch` below. + * This component is not exported and is only used within this module for `Checkbox`, `Radio`, and `Switch` below. */ -function renderControl(props: ControlInternalProps, ref: React.Ref) { - const { - alignIndicator, - children, - className, - indicatorChildren, - inline, - inputRef, - label, - labelElement, - large, - style, - type, - typeClassName, - tagName = "label", - ...htmlProps - } = props; - const classes = classNames( - Classes.CONTROL, - typeClassName, - { - [Classes.DISABLED]: htmlProps.disabled, - [Classes.INLINE]: inline, - [Classes.LARGE]: large, - }, - Classes.alignmentClass(alignIndicator), - className, - ); +const ControlInternal: React.FC = React.forwardRef( + (props, ref) => { + const { + alignIndicator, + children, + className, + indicatorChildren, + inline, + inputRef, + label, + labelElement, + large, + style, + type, + typeClassName, + tagName = "label", + ...htmlProps + } = props; + const classes = classNames( + Classes.CONTROL, + typeClassName, + { + [Classes.DISABLED]: htmlProps.disabled, + [Classes.INLINE]: inline, + [Classes.LARGE]: large, + }, + Classes.alignmentClass(alignIndicator), + className, + ); - return React.createElement( - tagName, - { className: classes, style, ref }, - , - {indicatorChildren}, - label, - labelElement, - children, - ); -} + return React.createElement( + tagName, + { className: classes, style, ref }, + , + {indicatorChildren}, + label, + labelElement, + children, + ); + }, +); +ControlInternal.displayName = `${DISPLAYNAME_PREFIX}.Control`; // // Switch @@ -174,14 +177,14 @@ export const Switch: React.FC = React.forwardRef( , ] : null; - return renderControl( - { - ...controlProps, - indicatorChildren: switchLabels, - type: "checkbox", - typeClassName: Classes.SWITCH, - }, - ref, + return ( + ); }, ); @@ -198,16 +201,9 @@ export type RadioProps = ControlProps; * * @see https://blueprintjs.com/docs/#core/components/radio */ -export const Radio: React.FC = React.forwardRef((props, ref) => - renderControl( - { - ...props, - type: "radio", - typeClassName: Classes.RADIO, - }, - ref, - ), -); +export const Radio: React.FC = React.forwardRef((props, ref) => ( + +)); Radio.displayName = `${DISPLAYNAME_PREFIX}.Radio`; // @@ -267,15 +263,15 @@ export const Checkbox: React.FC = React.forwardRef((props, ref) = } }, [localInputRef, isIndeterminate]); - return renderControl( - { - ...controlProps, - inputRef, - onChange: handleChange, - type: "checkbox", - typeClassName: Classes.CHECKBOX, - }, - ref, + return ( + ); }); Checkbox.displayName = `${DISPLAYNAME_PREFIX}.Checkbox`; diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index a117d519cb..720e8d0bad 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -102,7 +102,7 @@ export { Section, SectionElevation, SectionProps } from "./section/section"; export { SectionCard, SectionCardProps } from "./section/sectionCard"; export { Slider, SliderProps } from "./slider/slider"; export { Spinner, SpinnerProps, SpinnerSize } from "./spinner/spinner"; -export { SwitchCard, SwitchCardProps } from "./card/switch-card"; +export { SwitchCard, SwitchCardProps } from "./card/switchCard"; export { Tab, TabId, TabProps } from "./tabs/tab"; // eslint-disable-next-line deprecation/deprecation export { Tabs, TabsProps, TabsExpander, Expander } from "./tabs/tabs"; From 7491671351d2bd20282373fe8f6a73570c21e6df Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 5 Oct 2023 13:46:35 -0400 Subject: [PATCH 16/21] Improve docs example, add compact styles --- .../core/src/common/_variables-extended.scss | 15 +++++ packages/core/src/components/_index.scss | 2 +- .../src/components/card-list/card-list.scss | 21 +++---- .../src/components/card/_card-variables.scss | 20 +++++++ packages/core/src/components/card/_card.scss | 22 +------ packages/core/src/components/card/card.md | 13 ++--- packages/core/src/components/components.md | 1 + .../{card => control-card}/_control-card.scss | 6 +- .../components/control-card/control-card.md | 26 +++++++++ .../{card => control-card}/controlCard.tsx | 4 +- .../{card => control-card}/switchCard.tsx | 0 packages/core/src/components/index.ts | 2 +- .../core-examples/checkboxCardExample.tsx | 47 --------------- .../core-examples/switchCardExample.tsx | 57 ++++++++++++------- packages/docs-app/src/styles/_examples.scss | 9 +++ 15 files changed, 131 insertions(+), 114 deletions(-) create mode 100644 packages/core/src/components/card/_card-variables.scss rename packages/core/src/components/{card => control-card}/_control-card.scss (86%) create mode 100644 packages/core/src/components/control-card/control-card.md rename packages/core/src/components/{card => control-card}/controlCard.tsx (95%) rename packages/core/src/components/{card => control-card}/switchCard.tsx (100%) delete mode 100644 packages/docs-app/src/examples/core-examples/checkboxCardExample.tsx diff --git a/packages/core/src/common/_variables-extended.scss b/packages/core/src/common/_variables-extended.scss index d7b5f7debd..cbc44bc310 100644 --- a/packages/core/src/common/_variables-extended.scss +++ b/packages/core/src/common/_variables-extended.scss @@ -22,3 +22,18 @@ $icon-classes: ( ".#{$ns}-icon-standard", ".#{$ns}-icon-large" ) !default; + +$elevation-shadows: ( + $pt-elevation-shadow-0 + $pt-elevation-shadow-1 + $pt-elevation-shadow-2 + $pt-elevation-shadow-3 + $pt-elevation-shadow-4 +); +$dark-elevation-shadows: ( + $pt-dark-elevation-shadow-0 + $pt-dark-elevation-shadow-1 + $pt-dark-elevation-shadow-2 + $pt-dark-elevation-shadow-3 + $pt-dark-elevation-shadow-4 +); diff --git a/packages/core/src/components/_index.scss b/packages/core/src/components/_index.scss index 32261f1937..24052e90a6 100644 --- a/packages/core/src/components/_index.scss +++ b/packages/core/src/components/_index.scss @@ -7,10 +7,10 @@ @import "button/button-group"; @import "callout/callout"; @import "card/card"; -@import "card/control-card"; @import "card-list/card-list"; @import "collapse/collapse"; @import "context-menu/context-menu"; +@import "control-card/control-card"; @import "divider/divider"; @import "dialog/dialog"; @import "dialog/dialog-body"; diff --git a/packages/core/src/components/card-list/card-list.scss b/packages/core/src/components/card-list/card-list.scss index f368d5a723..bd09298813 100644 --- a/packages/core/src/components/card-list/card-list.scss +++ b/packages/core/src/components/card-list/card-list.scss @@ -1,14 +1,7 @@ -@import "../../common/variables"; +// Copyright 2023 Palantir Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. -$card-list-border-width: 1px !default; - -// N.B. min-height is calculated as height of a button + vertical padding. We need to add an extra pixel to account for -// the bottom border. -$card-list-item-default-min-height: ($pt-grid-size * 5) + $card-list-border-width !default; -$card-list-item-default-padding: $pt-grid-size (2 * $pt-grid-size) !default; - -$card-list-item-small-min-height: ($pt-grid-size * 4) + $card-list-border-width !default; -$card-list-item-small-padding: 7px 15px !default; +@import "../card/card-variables"; .#{$ns}-card-list { overflow: auto; @@ -20,8 +13,8 @@ $card-list-item-small-padding: 7px 15px !default; border-radius: 0; box-shadow: none; display: flex; - min-height: $card-list-item-default-min-height; - padding: $card-list-item-default-padding; + min-height: $card-list-item-min-height; + padding: $card-list-item-padding; &.#{$ns}-interactive:hover, &.#{$ns}-interactive:active { @@ -43,8 +36,8 @@ $card-list-item-small-padding: 7px 15px !default; } &.#{$ns}-compact > .#{$ns}-card { - min-height: $card-list-item-small-min-height; - padding: $card-list-item-small-padding; + min-height: $card-list-item-min-height-compact; + padding: $card-list-item-padding-compact; } .#{$ns}-dark & { diff --git a/packages/core/src/components/card/_card-variables.scss b/packages/core/src/components/card/_card-variables.scss new file mode 100644 index 0000000000..fb442c2bfe --- /dev/null +++ b/packages/core/src/components/card/_card-variables.scss @@ -0,0 +1,20 @@ +// Copyright 2023 Palantir Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. + +@import "../../common/variables-extended"; + +$card-padding: $pt-grid-size * 2 !default; +$card-padding-compact: 12px 15px !default; + +$card-background-color: $white !default; +$dark-card-background-color: $dark-gray3 !default; + +$card-list-border-width: 1px !default; + +// N.B. min-height is calculated as height of a button + vertical padding. We need to add an extra pixel to account for +// the bottom border. +$card-list-item-min-height: ($pt-grid-size * 5) + $card-list-border-width !default; +$card-list-item-padding: $pt-grid-size ($pt-grid-size * 2) !default; + +$card-list-item-min-height-compact: ($pt-grid-size * 4) + $card-list-border-width !default; +$card-list-item-padding-compact: 7px 15px !default; diff --git a/packages/core/src/components/card/_card.scss b/packages/core/src/components/card/_card.scss index e97162a7de..27192b7f2d 100644 --- a/packages/core/src/components/card/_card.scss +++ b/packages/core/src/components/card/_card.scss @@ -1,7 +1,7 @@ // Copyright 2015 Palantir Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. -@import "../../common/variables"; +@import "./card-variables"; /* Cards @@ -21,26 +21,6 @@ Markup: Styleguide card */ -$card-padding: $pt-grid-size * 2 !default; - -$card-background-color: $white !default; -$dark-card-background-color: $dark-gray3 !default; - -$elevation-shadows: ( - $pt-elevation-shadow-0 - $pt-elevation-shadow-1 - $pt-elevation-shadow-2 - $pt-elevation-shadow-3 - $pt-elevation-shadow-4 -); -$dark-elevation-shadows: ( - $pt-dark-elevation-shadow-0 - $pt-dark-elevation-shadow-1 - $pt-dark-elevation-shadow-2 - $pt-dark-elevation-shadow-3 - $pt-dark-elevation-shadow-4 -); - .#{$ns}-card { background-color: $card-background-color; border-radius: $pt-border-radius; diff --git a/packages/core/src/components/card/card.md b/packages/core/src/components/card/card.md index acb9d36884..0484eac475 100644 --- a/packages/core/src/components/card/card.md +++ b/packages/core/src/components/card/card.md @@ -1,6 +1,6 @@ @# Card -A __Card__ is a bounded unit of UI content with a solid background color. +A **Card** is a bounded unit of UI content with a solid background color. @reactExample CardExample @@ -10,10 +10,12 @@ A __Card__ is a bounded unit of UI content with a solid background color. import { Button, Card, Elevation } from "@blueprintjs/core"; -
Card heading
+
+ Card heading +

Card content

-
+
; ``` @## Elevation @@ -33,6 +35,7 @@ Note that the `Classes.ELEVATION_*` classes can be used on any element (not just
Deprecated API: use [``](#core/components/card) +
CSS APIs for Blueprint components are considered deprecated, as they are verbose, error-prone, and they @@ -47,7 +50,3 @@ Add the `.@ns-interactive` modifier class to make a `.@ns-card` respond to user with this class applied, the mouse changes to a pointer and increases the elevation shadow on the card. @css card - -@## Switch Card - -@reactExample SwitchCardExample diff --git a/packages/core/src/components/components.md b/packages/core/src/components/components.md index db1b0fed18..eb61d1d589 100644 --- a/packages/core/src/components/components.md +++ b/packages/core/src/components/components.md @@ -8,6 +8,7 @@ @page callout @page card @page card-list +@page control-card @page collapse @page divider @page editable-text diff --git a/packages/core/src/components/card/_control-card.scss b/packages/core/src/components/control-card/_control-card.scss similarity index 86% rename from packages/core/src/components/card/_control-card.scss rename to packages/core/src/components/control-card/_control-card.scss index 0f6543cb1d..f1f8331b43 100644 --- a/packages/core/src/components/card/_control-card.scss +++ b/packages/core/src/components/control-card/_control-card.scss @@ -1,7 +1,7 @@ // Copyright 2023 Palantir Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. -@import "../../common/variables"; +@import "../card/card-variables"; // use an extra selector to increase specificity .#{$ns}-card.#{$ns}-control-card { @@ -20,6 +20,10 @@ padding: $card-padding; width: calc(100%); + .#{$ns}-card-list.#{$ns}-compact & { + padding: $card-padding-compact; + } + .#{$ns}-control-indicator { margin: 0; } diff --git a/packages/core/src/components/control-card/control-card.md b/packages/core/src/components/control-card/control-card.md new file mode 100644 index 0000000000..23e0eb3cc2 --- /dev/null +++ b/packages/core/src/components/control-card/control-card.md @@ -0,0 +1,26 @@ +--- +tag: new +--- + +@# Control card + +A control card is an interactive [**Card**](#core/components/card) with an embedded form control. +There are a few supported form controls: + +- [**SwitchCard**](#core/components/control-card.switch-card) +- CheckboxCard (_coming soon_) +- RadioCard (_coming soon_) + +The children of a control card will be used as the `labelElement` of the form control. Users may click anywhere +inside the card to toggle the control state. + +@## SwitchCard + +Card with an embedded [**Switch**](#core/components/switch) control. Most of the **Card** and **Switch** props are +available on the root component. + +@reactExample SwitchCardExample + +@### Props interface + +@interface SwitchCardProps diff --git a/packages/core/src/components/card/controlCard.tsx b/packages/core/src/components/control-card/controlCard.tsx similarity index 95% rename from packages/core/src/components/card/controlCard.tsx rename to packages/core/src/components/control-card/controlCard.tsx index 35ac9df65b..787c83a4ed 100644 --- a/packages/core/src/components/card/controlCard.tsx +++ b/packages/core/src/components/control-card/controlCard.tsx @@ -20,7 +20,7 @@ import * as React from "react"; import { Classes } from "../../common"; import { DISPLAYNAME_PREFIX, HTMLInputProps } from "../../common/props"; import { ControlProps, Switch } from "../forms/controls"; -import { Card, CardProps } from "./card"; +import { Card, CardProps } from "../card/card"; /** * Subset of {@link Card} which can be used to adjust its behavior. @@ -77,7 +77,7 @@ export const ControlCard: React.FC = React.forwardRef(props => }; return ( - + {controlKind === "switch" ? ( ) : ( diff --git a/packages/core/src/components/card/switchCard.tsx b/packages/core/src/components/control-card/switchCard.tsx similarity index 100% rename from packages/core/src/components/card/switchCard.tsx rename to packages/core/src/components/control-card/switchCard.tsx diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index 720e8d0bad..798790ef2d 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -102,7 +102,7 @@ export { Section, SectionElevation, SectionProps } from "./section/section"; export { SectionCard, SectionCardProps } from "./section/sectionCard"; export { Slider, SliderProps } from "./slider/slider"; export { Spinner, SpinnerProps, SpinnerSize } from "./spinner/spinner"; -export { SwitchCard, SwitchCardProps } from "./card/switchCard"; +export { SwitchCard, SwitchCardProps } from "./control-card/switchCard"; export { Tab, TabId, TabProps } from "./tabs/tab"; // eslint-disable-next-line deprecation/deprecation export { Tabs, TabsProps, TabsExpander, Expander } from "./tabs/tabs"; diff --git a/packages/docs-app/src/examples/core-examples/checkboxCardExample.tsx b/packages/docs-app/src/examples/core-examples/checkboxCardExample.tsx deleted file mode 100644 index 1f3e24f446..0000000000 --- a/packages/docs-app/src/examples/core-examples/checkboxCardExample.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Palantir Technologies, Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as React from "react"; - -import { CardList, SwitchCard } from "@blueprintjs/core"; -import { Example, ExampleProps } from "@blueprintjs/docs-theme"; - -export class SwitchCardExample extends React.PureComponent { - public render() { - return ( - -
- Wifi - Bluetooth - NFC -
- - - Wifi - Bluetooth - NFC - -
- ); - } -} diff --git a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx index df2c247803..d42661c034 100644 --- a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx +++ b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx @@ -16,51 +16,52 @@ import * as React from "react"; -import { CardList, SwitchCard } from "@blueprintjs/core"; -import { Example, ExampleProps } from "@blueprintjs/docs-theme"; +import { CardList, H5, Switch, SwitchCard } from "@blueprintjs/core"; +import { Example, ExampleProps, handleBooleanChange } from "@blueprintjs/docs-theme"; interface SwitchCardExampleState { + // TODO: add compact option + // compact: boolean; + cardListCompact: boolean; + disabled: boolean; switchChecked: boolean[]; } export class SwitchCardExample extends React.PureComponent { public state: SwitchCardExampleState = { + // TODO: add compact option + // compact: false, + cardListCompact: false, + disabled: false, switchChecked: [false, true, false, true, true, false], }; public render() { - const { switchChecked } = this.state; + const { cardListCompact, disabled, switchChecked } = this.state; + const sharedProps = { disabled }; return ( - -
- + +
+ Wifi - + Bluetooth - + NFC
- - + + Wifi - + Bluetooth - + NFC @@ -68,6 +69,22 @@ export class SwitchCardExample extends React.PureComponent +
Props
+ +
CardList Props
+ + + ); + } + + private toggleDisabled = handleBooleanChange(disabled => this.setState({ disabled })); + + private toggleCardListCompact = handleBooleanChange(cardListCompact => this.setState({ cardListCompact })); + private getSwitchChangeHandler = (index: number) => () => { const switchChecked = [...this.state.switchChecked]; switchChecked[index] = !this.state.switchChecked[index]; diff --git a/packages/docs-app/src/styles/_examples.scss b/packages/docs-app/src/styles/_examples.scss index 90357199f7..21b24be6ed 100644 --- a/packages/docs-app/src/styles/_examples.scss +++ b/packages/docs-app/src/styles/_examples.scss @@ -90,6 +90,15 @@ } } +#{example("SwitchCard")} { + .docs-control-card-grid { + display: grid; + gap: $pt-grid-size * 2; + grid-template-columns: repeat(auto-fit, minMax($pt-grid-size * 10, 1fr)); + width: 100%; + } +} + #{example("EditableText")} { .docs-example { display: block; From 5eba3aaa241386708a0b6c6aa5edced8ae0c0c32 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 5 Oct 2023 13:57:56 -0400 Subject: [PATCH 17/21] fix lint --- packages/core/src/components/control-card/controlCard.tsx | 2 +- packages/docs-app/src/styles/_examples.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/components/control-card/controlCard.tsx b/packages/core/src/components/control-card/controlCard.tsx index 787c83a4ed..8d0d4eb19e 100644 --- a/packages/core/src/components/control-card/controlCard.tsx +++ b/packages/core/src/components/control-card/controlCard.tsx @@ -19,8 +19,8 @@ import * as React from "react"; import { Classes } from "../../common"; import { DISPLAYNAME_PREFIX, HTMLInputProps } from "../../common/props"; -import { ControlProps, Switch } from "../forms/controls"; import { Card, CardProps } from "../card/card"; +import { ControlProps, Switch } from "../forms/controls"; /** * Subset of {@link Card} which can be used to adjust its behavior. diff --git a/packages/docs-app/src/styles/_examples.scss b/packages/docs-app/src/styles/_examples.scss index 21b24be6ed..e8b5fd4f29 100644 --- a/packages/docs-app/src/styles/_examples.scss +++ b/packages/docs-app/src/styles/_examples.scss @@ -94,7 +94,7 @@ .docs-control-card-grid { display: grid; gap: $pt-grid-size * 2; - grid-template-columns: repeat(auto-fit, minMax($pt-grid-size * 10, 1fr)); + grid-template-columns: repeat(auto-fit, minmax($pt-grid-size * 10, 1fr)); width: 100%; } } From d8f366e5fadc02b08fedcb7b648b00b39b0b68ad Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 5 Oct 2023 14:23:08 -0400 Subject: [PATCH 18/21] add test suite --- .../components/control-card/control-card.md | 6 ++- .../components/control-card/controlCard.tsx | 6 ++- .../test/control-card/controlCardTests.tsx | 51 +++++++++++++++++++ packages/core/test/index.ts | 1 + 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 packages/core/test/control-card/controlCardTests.tsx diff --git a/packages/core/src/components/control-card/control-card.md b/packages/core/src/components/control-card/control-card.md index 23e0eb3cc2..73f87b799f 100644 --- a/packages/core/src/components/control-card/control-card.md +++ b/packages/core/src/components/control-card/control-card.md @@ -16,11 +16,13 @@ inside the card to toggle the control state. @## SwitchCard -Card with an embedded [**Switch**](#core/components/switch) control. Most of the **Card** and **Switch** props are -available on the root component. +Card with an embedded [**Switch**](#core/components/switch) control. @reactExample SwitchCardExample @### Props interface +Most of the properties in [**CardProps**](#core/components/card.props-interface) and +[**SwitchProps**](#core/components/switch.props-interface) are available on the root component. + @interface SwitchCardProps diff --git a/packages/core/src/components/control-card/controlCard.tsx b/packages/core/src/components/control-card/controlCard.tsx index 8d0d4eb19e..2868cca4c2 100644 --- a/packages/core/src/components/control-card/controlCard.tsx +++ b/packages/core/src/components/control-card/controlCard.tsx @@ -50,7 +50,7 @@ export interface ControlCardProps extends SupportedCardProps, SupportedControlPr * * @internal */ -export const ControlCard: React.FC = React.forwardRef(props => { +export const ControlCard: React.FC = React.forwardRef((props, ref) => { const { checked, children: controlLabel, @@ -60,6 +60,7 @@ export const ControlCard: React.FC = React.forwardRef(props => disabled, inputProps, inputRef, + onChange, ...cardProps } = props; @@ -73,11 +74,12 @@ export const ControlCard: React.FC = React.forwardRef(props => disabled, inputRef, labelElement: controlLabel, + onChange, ...inputProps, }; return ( - + {controlKind === "switch" ? ( ) : ( diff --git a/packages/core/test/control-card/controlCardTests.tsx b/packages/core/test/control-card/controlCardTests.tsx new file mode 100644 index 0000000000..033bb1f708 --- /dev/null +++ b/packages/core/test/control-card/controlCardTests.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { assert } from "chai"; +import { mount } from "enzyme"; +import * as React from "react"; +import { spy, SinonSpy } from "sinon"; + +import { SwitchCard } from "../../src"; + +describe("ControlCard", () => { + let testsContainerElement: HTMLElement | undefined; + + beforeEach(() => { + testsContainerElement = document.createElement("div"); + document.body.appendChild(testsContainerElement); + }); + + afterEach(() => { + testsContainerElement?.remove(); + }); + + describe("SwitchCard", () => { + const handleControlChangeSpy = spy() as SinonSpy<[React.FormEvent], void>; + + beforeEach(() => { + handleControlChangeSpy.resetHistory(); + }); + + it("clicking on label element toggles switch state", () => { + const wrapper = mount(, { + attachTo: testsContainerElement, + }); + wrapper.find("input").simulate("change"); + assert.isTrue(handleControlChangeSpy.calledOnce, "expected onChange to be called"); + }); + }); +}); diff --git a/packages/core/test/index.ts b/packages/core/test/index.ts index bff53f46fa..c439e5ee17 100644 --- a/packages/core/test/index.ts +++ b/packages/core/test/index.ts @@ -33,6 +33,7 @@ import "./context-menu/contextMenuSingletonTests"; import "./context-menu/contextMenuTests"; import "./controls/controlsTests"; import "./controls/inputGroupTests"; +import "./control-card/controlCardTests"; import "./controls/numericInputTests"; import "./controls/radioGroupTests"; import "./dialog/dialogTests"; From ab2016a3e9a5f3b4e6e3bb4ce65c68fc078f9c50 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 5 Oct 2023 15:09:13 -0400 Subject: [PATCH 19/21] split out ControlCardListExample, use flex styles --- packages/core/src/common/classes.ts | 1 + .../src/components/card/_card-variables.scss | 22 ++++-- .../control-card/_control-card.scss | 21 +++-- .../components/control-card/control-card.md | 6 ++ .../components/control-card/controlCard.tsx | 8 +- .../core-examples/controlCardListExample.tsx | 77 +++++++++++++++++++ .../src/examples/core-examples/index.ts | 1 + .../core-examples/switchCardExample.tsx | 47 ++--------- packages/docs-app/src/styles/_examples.scss | 6 ++ 9 files changed, 134 insertions(+), 55 deletions(-) create mode 100644 packages/docs-app/src/examples/core-examples/controlCardListExample.tsx diff --git a/packages/core/src/common/classes.ts b/packages/core/src/common/classes.ts index 786e4034a1..dba6993746 100644 --- a/packages/core/src/common/classes.ts +++ b/packages/core/src/common/classes.ts @@ -114,6 +114,7 @@ export const CALLOUT_ICON = `${CALLOUT}-icon`; export const CARD = `${NS}-card`; export const CONTROL_CARD = `${NS}-control-card`; +export const CONTROL_CARD_LABEL = `${CONTROL_CARD}-label`; export const SWITCH_CONTROL_CARD = `${NS}-switch-control-card`; export const CARD_LIST = `${NS}-card-list`; diff --git a/packages/core/src/components/card/_card-variables.scss b/packages/core/src/components/card/_card-variables.scss index fb442c2bfe..d060924da1 100644 --- a/packages/core/src/components/card/_card-variables.scss +++ b/packages/core/src/components/card/_card-variables.scss @@ -4,17 +4,25 @@ @import "../../common/variables-extended"; $card-padding: $pt-grid-size * 2 !default; -$card-padding-compact: 12px 15px !default; +$card-padding-compact: $pt-grid-size * 1.5 !default; $card-background-color: $white !default; $dark-card-background-color: $dark-gray3 !default; $card-list-border-width: 1px !default; -// N.B. min-height is calculated as height of a button + vertical padding. We need to add an extra pixel to account for -// the bottom border. -$card-list-item-min-height: ($pt-grid-size * 5) + $card-list-border-width !default; -$card-list-item-padding: $pt-grid-size ($pt-grid-size * 2) !default; +// CardList Card item min-height is calculated as height of a button + vertical padding. +// We need to add an extra pixel to account for the bottom border. +$card-list-item-padding-vertical: $pt-grid-size !default; +$card-list-item-min-height: $pt-button-height + ($card-list-item-padding-vertical * 2) + $card-list-border-width !default; +$card-list-item-padding: $card-list-item-padding-vertical $card-padding !default; -$card-list-item-min-height-compact: ($pt-grid-size * 4) + $card-list-border-width !default; -$card-list-item-padding-compact: 7px 15px !default; +$card-list-item-padding-vertical-compact: 7px !default; +$card-list-item-min-height-compact: $pt-button-height + ($card-list-item-padding-vertical-compact * 2) + $card-list-border-width !default; +$card-list-item-padding-compact: $card-list-item-padding-vertical-compact $card-padding-compact !default; + +// CardList ControlCard item min-height is calculated as height of a control indicator + vertical padding +$card-list-control-item-padding-vertical: $card-padding; +$card-list-control-item-min-height: $control-indicator-size + ($card-list-control-item-padding-vertical * 2) + $card-list-border-width !default; +$card-list-control-item-padding-vertical-compact: $card-padding-compact; +$card-list-control-item-min-height-compact: $control-indicator-size + ($card-list-control-item-padding-vertical-compact * 2) + $card-list-border-width !default; diff --git a/packages/core/src/components/control-card/_control-card.scss b/packages/core/src/components/control-card/_control-card.scss index f1f8331b43..1b940f70f0 100644 --- a/packages/core/src/components/control-card/_control-card.scss +++ b/packages/core/src/components/control-card/_control-card.scss @@ -7,21 +7,32 @@ .#{$ns}-card.#{$ns}-control-card { &, .#{$ns}-card-list > & { - // Make the label take up the full size so that we can use the control's onChange instead of a "click" handler - // on any container element. This is partially to work around https://github.com/palantir/blueprint/issues/6251 + // min-height & padding will be set on the label element so that it can take up the full size of the card and + // its entire visual element will be interactive This is partially to work around https://github.com/palantir/blueprint/issues/6251 padding: 0; + min-height: auto; } } .#{$ns}-switch-control-card { - // N.B. Switch always has inline={true} inside SwitchCard - > .#{$ns}-switch.#{$ns}-control.#{$ns}-inline { + // need a lot of specificity here to override control styles + > .#{$ns}-switch.#{$ns}-control.#{$ns}-align-right { + align-items: center; + display: flex; + flex-direction: row-reverse; + justify-content: space-between; margin: 0; padding: $card-padding; width: calc(100%); + .#{$ns}-card-list & { + min-height: $card-list-control-item-min-height; + padding: $card-list-item-padding; + } + .#{$ns}-card-list.#{$ns}-compact & { - padding: $card-padding-compact; + min-height: $card-list-control-item-min-height-compact; + padding: $card-list-item-padding-compact; } .#{$ns}-control-indicator { diff --git a/packages/core/src/components/control-card/control-card.md b/packages/core/src/components/control-card/control-card.md index 73f87b799f..45453c0378 100644 --- a/packages/core/src/components/control-card/control-card.md +++ b/packages/core/src/components/control-card/control-card.md @@ -26,3 +26,9 @@ Most of the properties in [**CardProps**](#core/components/card.props-interface) [**SwitchProps**](#core/components/switch.props-interface) are available on the root component. @interface SwitchCardProps + +@## Composing with CardList + +Control cards work just like regular cards inside a [**CardList**](#core/components/card-list). + +@reactExample ControlCardListExample diff --git a/packages/core/src/components/control-card/controlCard.tsx b/packages/core/src/components/control-card/controlCard.tsx index 2868cca4c2..4ce1137cdf 100644 --- a/packages/core/src/components/control-card/controlCard.tsx +++ b/packages/core/src/components/control-card/controlCard.tsx @@ -53,7 +53,7 @@ export interface ControlCardProps extends SupportedCardProps, SupportedControlPr export const ControlCard: React.FC = React.forwardRef((props, ref) => { const { checked, - children: controlLabel, + children: labelContent, className, controlKind, defaultChecked, @@ -68,12 +68,14 @@ export const ControlCard: React.FC = React.forwardRef((props, [Classes.SWITCH_CONTROL_CARD]: controlKind === "switch", }); + // use a container element to achieve a good flex layout + const labelElement =
{labelContent}
; const controlProps: ControlProps = { checked, defaultChecked, disabled, inputRef, - labelElement: controlLabel, + labelElement, onChange, ...inputProps, }; @@ -83,7 +85,7 @@ export const ControlCard: React.FC = React.forwardRef((props, {controlKind === "switch" ? ( ) : ( - controlLabel + labelElement )}
); diff --git a/packages/docs-app/src/examples/core-examples/controlCardListExample.tsx b/packages/docs-app/src/examples/core-examples/controlCardListExample.tsx new file mode 100644 index 0000000000..55da3b6aac --- /dev/null +++ b/packages/docs-app/src/examples/core-examples/controlCardListExample.tsx @@ -0,0 +1,77 @@ +/* + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as React from "react"; + +import { CardList, Classes, H5, Section, SectionCard, Switch, SwitchCard } from "@blueprintjs/core"; +import { Example, ExampleProps, handleBooleanChange } from "@blueprintjs/docs-theme"; +import { Cog, Moon, PageLayout } from "@blueprintjs/icons"; + +interface ControlCardListExampleState { + compact: boolean; + disabled: boolean; +} + +export class ControlCardListExample extends React.PureComponent { + public state: ControlCardListExampleState = { + compact: false, + disabled: false, + }; + + public render() { + const { compact, disabled } = this.state; + const sharedProps = { disabled }; + + return ( + +
+ + + + + Dark theme + + + + Show scrollbars + + + + Developer mode + + + +
+
+ ); + } + + private renderOptions() { + const { compact, disabled } = this.state; + return ( + <> +
Section & CardList Props
+ +
SwitchCard Props
+ + + ); + } + + private toggleDisabled = handleBooleanChange(disabled => this.setState({ disabled })); + + private toggleCompact = handleBooleanChange(compact => this.setState({ compact })); +} diff --git a/packages/docs-app/src/examples/core-examples/index.ts b/packages/docs-app/src/examples/core-examples/index.ts index dba9c13419..92f68e3cef 100644 --- a/packages/docs-app/src/examples/core-examples/index.ts +++ b/packages/docs-app/src/examples/core-examples/index.ts @@ -27,6 +27,7 @@ export * from "./cardExample"; export * from "./cardListExample"; export { ContextMenuExample } from "./contextMenuExample"; export { ContextMenuPopoverExample } from "./contextMenuPopoverExample"; +export { ControlCardListExample } from "./controlCardListExample"; export * from "./controlGroupExample"; export * from "./dialogExample"; export * from "./multistepDialogExample"; diff --git a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx index d42661c034..5668112d26 100644 --- a/packages/docs-app/src/examples/core-examples/switchCardExample.tsx +++ b/packages/docs-app/src/examples/core-examples/switchCardExample.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + * Copyright 2023 Palantir Technologies, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,79 +16,46 @@ import * as React from "react"; -import { CardList, H5, Switch, SwitchCard } from "@blueprintjs/core"; +import { H5, Switch, SwitchCard } from "@blueprintjs/core"; import { Example, ExampleProps, handleBooleanChange } from "@blueprintjs/docs-theme"; interface SwitchCardExampleState { // TODO: add compact option // compact: boolean; - cardListCompact: boolean; disabled: boolean; - switchChecked: boolean[]; } export class SwitchCardExample extends React.PureComponent { public state: SwitchCardExampleState = { // TODO: add compact option // compact: false, - cardListCompact: false, disabled: false, - switchChecked: [false, true, false, true, true, false], }; public render() { - const { cardListCompact, disabled, switchChecked } = this.state; + const { disabled } = this.state; const sharedProps = { disabled }; return (
- - Wifi - - - Bluetooth - - - NFC - + Wifi + Bluetooth + NFC
- - - - Wifi - - - Bluetooth - - - NFC - -
); } private renderOptions() { - const { cardListCompact, disabled } = this.state; + const { disabled } = this.state; return ( <>
Props
-
CardList Props
- ); } private toggleDisabled = handleBooleanChange(disabled => this.setState({ disabled })); - - private toggleCardListCompact = handleBooleanChange(cardListCompact => this.setState({ cardListCompact })); - - private getSwitchChangeHandler = (index: number) => () => { - const switchChecked = [...this.state.switchChecked]; - switchChecked[index] = !this.state.switchChecked[index]; - - this.setState({ switchChecked }); - }; } diff --git a/packages/docs-app/src/styles/_examples.scss b/packages/docs-app/src/styles/_examples.scss index e8b5fd4f29..10d7914af1 100644 --- a/packages/docs-app/src/styles/_examples.scss +++ b/packages/docs-app/src/styles/_examples.scss @@ -99,6 +99,12 @@ } } +#{example("ControlCardList")} { + .#{$ns}-control-card-label .#{$ns}-icon { + margin-right: $pt-grid-size; + } +} + #{example("EditableText")} { .docs-example { display: block; From c877f590f202645190a65355b98fa6720c657709 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 5 Oct 2023 15:24:20 -0400 Subject: [PATCH 20/21] fix lint --- packages/core/src/components/card/_card-variables.scss | 2 ++ packages/core/src/components/control-card/_control-card.scss | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/components/card/_card-variables.scss b/packages/core/src/components/card/_card-variables.scss index d060924da1..ed22b186b9 100644 --- a/packages/core/src/components/card/_card-variables.scss +++ b/packages/core/src/components/card/_card-variables.scss @@ -11,6 +11,8 @@ $dark-card-background-color: $dark-gray3 !default; $card-list-border-width: 1px !default; +// stylelint-disable max-line-length + // CardList Card item min-height is calculated as height of a button + vertical padding. // We need to add an extra pixel to account for the bottom border. $card-list-item-padding-vertical: $pt-grid-size !default; diff --git a/packages/core/src/components/control-card/_control-card.scss b/packages/core/src/components/control-card/_control-card.scss index 1b940f70f0..0eb2f7036f 100644 --- a/packages/core/src/components/control-card/_control-card.scss +++ b/packages/core/src/components/control-card/_control-card.scss @@ -9,8 +9,8 @@ .#{$ns}-card-list > & { // min-height & padding will be set on the label element so that it can take up the full size of the card and // its entire visual element will be interactive This is partially to work around https://github.com/palantir/blueprint/issues/6251 - padding: 0; min-height: auto; + padding: 0; } } From 4b186f59fa47148f764c0e20dcd8c29671a4ea39 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 5 Oct 2023 15:38:34 -0400 Subject: [PATCH 21/21] fix more lint --- packages/core/test/control-card/controlCardTests.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/test/control-card/controlCardTests.tsx b/packages/core/test/control-card/controlCardTests.tsx index 033bb1f708..e025bb5d75 100644 --- a/packages/core/test/control-card/controlCardTests.tsx +++ b/packages/core/test/control-card/controlCardTests.tsx @@ -17,7 +17,7 @@ import { assert } from "chai"; import { mount } from "enzyme"; import * as React from "react"; -import { spy, SinonSpy } from "sinon"; +import { SinonSpy, spy } from "sinon"; import { SwitchCard } from "../../src";