From a5ab535d3bc541f3150bb4fc18d2078f3a60f784 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Fri, 22 Mar 2024 00:05:10 +0200 Subject: [PATCH] feat(webapp): import preview page --- packages/webapp/src/components/Icon/index.tsx | 13 +- .../webapp/src/components/Section/Section.tsx | 248 ++++++++++++++++++ .../src/components/Section/SectionCard.tsx | 41 +++ .../webapp/src/components/Section/index.ts | 2 + packages/webapp/src/constants/classes.tsx | 77 ++++-- .../Import/ImportFileMappingForm.tsx | 13 +- .../Import/ImportFilePreview.module.scss | 42 +++ .../containers/Import/ImportFilePreview.tsx | 96 +++++-- .../Import/ImportFileUploadStep.module.scss | 1 + packages/webapp/src/static/json/icons.tsx | 14 +- packages/webapp/src/style/App.scss | 1 + .../webapp/src/style/objects/typography.scss | 2 +- packages/webapp/src/style/section.scss | 117 +++++++++ 13 files changed, 619 insertions(+), 48 deletions(-) create mode 100644 packages/webapp/src/components/Section/Section.tsx create mode 100644 packages/webapp/src/components/Section/SectionCard.tsx create mode 100644 packages/webapp/src/components/Section/index.ts create mode 100644 packages/webapp/src/containers/Import/ImportFilePreview.module.scss create mode 100644 packages/webapp/src/style/section.scss diff --git a/packages/webapp/src/components/Icon/index.tsx b/packages/webapp/src/components/Icon/index.tsx index 83ff5b2ae..beb5c9ba2 100644 --- a/packages/webapp/src/components/Icon/index.tsx +++ b/packages/webapp/src/components/Icon/index.tsx @@ -17,11 +17,20 @@ import classNames from 'classnames'; import * as React from 'react'; -import { Classes } from '@blueprintjs/core'; +import { Classes, Props } from '@blueprintjs/core'; import IconSvgPaths from '@/static/json/icons'; import PropTypes from 'prop-types'; +export interface IconProps extends Props { + color?: string; + htmlTitle?: string; + icon: IconName | MaybeElement; + iconSize?: number; + style?: object; + tagName?: keyof JSX.IntrinsicElements; + title?: string; +} -export class Icon extends React.Component { +export class Icon extends React.Component { static displayName = `af.Icon`; static SIZE_STANDARD = 16; diff --git a/packages/webapp/src/components/Section/Section.tsx b/packages/webapp/src/components/Section/Section.tsx new file mode 100644 index 000000000..e46578a3d --- /dev/null +++ b/packages/webapp/src/components/Section/Section.tsx @@ -0,0 +1,248 @@ +import classNames from 'classnames'; +import React from 'react'; +import { + Card, + Collapse, + type CollapseProps, + Elevation, + Utils, + DISPLAYNAME_PREFIX, + type HTMLDivProps, + type MaybeElement, + type Props, + IconName, +} from '@blueprintjs/core'; +import { H6 } from '@blueprintjs/core'; +import { CLASSES } from '@/constants'; +import { Icon } from '../Icon'; + +/** + * Subset of {@link Elevation} options which are visually supported by the {@link Section} component. + * + * Note that an elevation greater than 1 creates too much visual clutter/noise in the UI, especially when + * multiple Sections are shown on a single page. + */ +export type SectionElevation = typeof Elevation.ZERO | typeof Elevation.ONE; + +export interface SectionCollapseProps + extends Pick< + CollapseProps, + 'className' | 'isOpen' | 'keepChildrenMounted' | 'transitionDuration' + > { + /** + * Whether the component is initially open or closed. + * + * This prop has no effect if `collapsible={false}` or the component is in controlled mode, + * i.e. when `isOpen` is **not** `undefined`. + * + * @default true + */ + defaultIsOpen?: boolean; + + /** + * Whether the component is open or closed. + * + * Passing a boolean value to `isOpen` will enabled controlled mode for the component. + */ + isOpen?: boolean; + + /** + * Callback invoked in controlled mode when the collapse toggle element is clicked. + */ + onToggle?: () => void; +} + +export interface SectionProps extends Props, Omit { + /** + * Whether this section's contents should be collapsible. + * + * @default false + */ + collapsible?: boolean; + + /** + * Subset of props to forward to the underlying {@link Collapse} component, with the addition of a + * `defaultIsOpen` option which sets the default open state of the component when in uncontrolled mode. + */ + collapseProps?: SectionCollapseProps; + + /** + * Whether this section should use compact styles. + * + * @default false + */ + compact?: boolean; + + /** + * Visual elevation of this container element. + * + * @default Elevation.ZERO + */ + elevation?: SectionElevation; + + /** + * 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 `title` is provided. + */ + icon?: IconName | MaybeElement; + + /** + * Element to render on the right side of the section header. + * Note that the header will only be rendered if `title` is provided. + */ + rightElement?: JSX.Element; + + /** + * Sub-title of the section. + * Note that the header will only be rendered if `title` is provided. + */ + subtitle?: JSX.Element | string; + + /** + * Title of the section. + * Note that the header will only be rendered if `title` is provided. + */ + title?: JSX.Element | string; + + /** + * Optional title renderer function. If provided, it is recommended to include a Blueprint `
` element + * as part of the title. The render function is supplied with `className` and `id` attributes which you must + * forward to the DOM. The `title` prop is also passed along to this renderer via `props.children`. + * + * @default H6 + */ + titleRenderer?: React.FC>; +} + +/** + * Section component. + * + * @see https://blueprintjs.com/docs/#core/components/section + */ +export const Section: React.FC = React.forwardRef( + (props, ref) => { + const { + children, + className, + collapseProps, + collapsible, + compact, + elevation, + icon, + rightElement, + subtitle, + title, + titleRenderer = H6, + ...htmlProps + } = props; + // Determine whether to use controlled or uncontrolled state. + const isControlled = collapseProps?.isOpen != null; + + // The initial useState value is negated in order to conform to the `isCollapsed` expectation. + const [isCollapsedUncontrolled, setIsCollapsed] = React.useState( + !(collapseProps?.defaultIsOpen ?? true), + ); + + const isCollapsed = isControlled + ? !collapseProps?.isOpen + : isCollapsedUncontrolled; + + const toggleIsCollapsed = React.useCallback(() => { + if (isControlled) { + collapseProps?.onToggle?.(); + } else { + setIsCollapsed(!isCollapsed); + } + }, [collapseProps, isCollapsed, isControlled]); + + const isHeaderRightContainerVisible = rightElement != null || collapsible; + + const sectionId = Utils.uniqueId('section'); + const sectionTitleId = title ? Utils.uniqueId('section-title') : undefined; + + return ( + + {title && ( +
+
+ {/* {icon && ( + + )} */} +
+ {React.createElement( + titleRenderer, + { + className: CLASSES.SECTION_HEADER_TITLE, + id: sectionTitleId, + }, + title, + )} + {subtitle && ( +
+ {subtitle} +
+ )} +
+
+ {isHeaderRightContainerVisible && ( +
+ {rightElement} + {collapsible && + (isCollapsed ? ( + + ) : ( + + ))} +
+ )} +
+ )} + {collapsible ? ( + // @ts-ignore + + {children} + + ) : ( + children + )} +
+ ); + }, +); +Section.defaultProps = { + compact: false, + elevation: Elevation.ZERO, +}; +Section.displayName = `${DISPLAYNAME_PREFIX}.Section`; diff --git a/packages/webapp/src/components/Section/SectionCard.tsx b/packages/webapp/src/components/Section/SectionCard.tsx new file mode 100644 index 000000000..9ff52e480 --- /dev/null +++ b/packages/webapp/src/components/Section/SectionCard.tsx @@ -0,0 +1,41 @@ +import classNames from 'classnames'; +import * as React from 'react'; +import { DISPLAYNAME_PREFIX, HTMLDivProps, Props } from '@blueprintjs/core'; +import { CLASSES } from '@/constants'; + +export interface SectionCardProps + extends Props, + HTMLDivProps, + React.RefAttributes { + /** + * Whether to apply visual padding inside the content container element. + * + * @default true + */ + padded?: boolean; +} + +/** + * Section card component. + * + * @see https://blueprintjs.com/docs/#core/components/section.section-card + */ +export const SectionCard: React.FC = React.forwardRef( + (props, ref) => { + const { className, children, padded, ...htmlProps } = props; + const classes = classNames( + CLASSES.SECTION_CARD, + { [CLASSES.PADDED]: padded }, + className, + ); + return ( +
+ {children} +
+ ); + }, +); +SectionCard.defaultProps = { + padded: true, +}; +SectionCard.displayName = `${DISPLAYNAME_PREFIX}.SectionCard`; diff --git a/packages/webapp/src/components/Section/index.ts b/packages/webapp/src/components/Section/index.ts new file mode 100644 index 000000000..ca7c2dece --- /dev/null +++ b/packages/webapp/src/components/Section/index.ts @@ -0,0 +1,2 @@ +export * from './Section'; +export * from './SectionCard'; \ No newline at end of file diff --git a/packages/webapp/src/constants/classes.tsx b/packages/webapp/src/constants/classes.tsx index 778829263..c708cd453 100644 --- a/packages/webapp/src/constants/classes.tsx +++ b/packages/webapp/src/constants/classes.tsx @@ -1,6 +1,21 @@ // @ts-nocheck import { Classes } from '@blueprintjs/core'; +export const NS = 'bp4'; + +export const SECTION = `${NS}-section`; +export const SECTION_COLLAPSED = `${SECTION}-collapsed`; +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_DIVIDER = `${SECTION_HEADER}-divider`; +export const SECTION_HEADER_TABS = `${SECTION_HEADER}-tabs`; +export const SECTION_HEADER_RIGHT = `${SECTION_HEADER}-right`; +export const SECTION_CARD = `${SECTION}-card`; + +export const PADDED = `${NS}-padded`; + const CLASSES = { DASHBOARD_PAGE: 'dashboard__page', DASHBOARD_DATATABLE: 'dashboard__datatable', @@ -16,7 +31,7 @@ const CLASSES = { DASHBOARD_CONTENT_PREFERENCES: 'dashboard-content--preferences', DASHBOARD_CONTENT_PANE: 'Pane2', DASHBOARD_CENTERED_EMPTY_STATUS: 'dashboard__centered-empty-status', - + PAGE_FORM: 'page-form', PAGE_FORM_HEADER: 'page-form__header', PAGE_FORM_HEADER_PRIMARY: 'page-form__primary-section', @@ -40,9 +55,9 @@ const CLASSES = { PAGE_FORM_ITEM: 'page-form--item', PAGE_FORM_MAKE_JOURNAL: 'page-form--make-journal-entries', PAGE_FORM_EXPENSE: 'page-form--expense', - PAGE_FORM_CREDIT_NOTE:'page-form--credit-note', - PAGE_FORM_VENDOR_CREDIT_NOTE:'page-form--vendor-credit-note', - PAGE_FORM_WAREHOUSE_TRANSFER:'page-form--warehouse-transfer', + PAGE_FORM_CREDIT_NOTE: 'page-form--credit-note', + PAGE_FORM_VENDOR_CREDIT_NOTE: 'page-form--vendor-credit-note', + PAGE_FORM_WAREHOUSE_TRANSFER: 'page-form--warehouse-transfer', FORM_GROUP_LIST_SELECT: 'form-group--select-list', @@ -66,31 +81,42 @@ const CLASSES = { PREFERENCES_TOPBAR: 'preferences-topbar', PREFERENCES_PAGE_INSIDE_CONTENT: 'preferences-page__inside-content', - PREFERENCES_PAGE_INSIDE_CONTENT_GENERAL: 'preferences-page__inside-content--general', - PREFERENCES_PAGE_INSIDE_CONTENT_USERS: 'preferences-page__inside-content--users', - PREFERENCES_PAGE_INSIDE_CONTENT_CURRENCIES: 'preferences-page__inside-content--currencies', - PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT: 'preferences-page__inside-content--accountant', - PREFERENCES_PAGE_INSIDE_CONTENT_SMS_INTEGRATION: 'preferences-page__inside-content--sms-integration', - PREFERENCES_PAGE_INSIDE_CONTENT_ROLES_FORM: 'preferences-page__inside-content--roles-form', - PREFERENCES_PAGE_INSIDE_CONTENT_BRANCHES: 'preferences-page__inside-content--branches', - PREFERENCES_PAGE_INSIDE_CONTENT_WAREHOUSES: 'preferences-page__inside-content--warehouses', + PREFERENCES_PAGE_INSIDE_CONTENT_GENERAL: + 'preferences-page__inside-content--general', + PREFERENCES_PAGE_INSIDE_CONTENT_USERS: + 'preferences-page__inside-content--users', + PREFERENCES_PAGE_INSIDE_CONTENT_CURRENCIES: + 'preferences-page__inside-content--currencies', + PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT: + 'preferences-page__inside-content--accountant', + PREFERENCES_PAGE_INSIDE_CONTENT_SMS_INTEGRATION: + 'preferences-page__inside-content--sms-integration', + PREFERENCES_PAGE_INSIDE_CONTENT_ROLES_FORM: + 'preferences-page__inside-content--roles-form', + PREFERENCES_PAGE_INSIDE_CONTENT_BRANCHES: + 'preferences-page__inside-content--branches', + PREFERENCES_PAGE_INSIDE_CONTENT_WAREHOUSES: + 'preferences-page__inside-content--warehouses', FINANCIAL_REPORT_INSIDER: 'dashboard__insider--financial-report', - UNIVERSAL_SEARCH: 'universal-search', UNIVERSAL_SEARCH_OMNIBAR: 'universal-search__omnibar', UNIVERSAL_SEARCH_OVERLAY: 'universal-search-overlay', UNIVERSAL_SEARCH_INPUT: 'universal-search__input', - UNIVERSAL_SEARCH_INPUT_RIGHT_ELEMENTS: 'universal-search-input-right-elements', + UNIVERSAL_SEARCH_INPUT_RIGHT_ELEMENTS: + 'universal-search-input-right-elements', UNIVERSAL_SEARCH_TYPE_SELECT_OVERLAY: 'universal-search__type-select-overlay', UNIVERSAL_SEARCH_TYPE_SELECT_BTN: 'universal-search__type-select-btn', UNIVERSAL_SEARCH_FOOTER: 'universal-search__footer', UNIVERSAL_SEARCH_ACTIONS: 'universal-search__actions', - UNIVERSAL_SEARCH_ACTION_SELECT: 'universal-search__action universal-search__action--select', - UNIVERSAL_SEARCH_ACTION_CLOSE: 'universal-search__action universal-search__action--close', - UNIVERSAL_SEARCH_ACTION_ARROWS: 'universal-search__action universal-search__action--arrows', + UNIVERSAL_SEARCH_ACTION_SELECT: + 'universal-search__action universal-search__action--select', + UNIVERSAL_SEARCH_ACTION_CLOSE: + 'universal-search__action universal-search__action--close', + UNIVERSAL_SEARCH_ACTION_ARROWS: + 'universal-search__action universal-search__action--arrows', DIALOG_PDF_PREVIEW: 'dialog--pdf-preview-dialog', @@ -98,8 +124,19 @@ const CLASSES = { CARD: 'card', ALIGN_RIGHT: 'align-right', FONT_BOLD: 'font-bold', + + NS, + PADDED, + SECTION, + SECTION_COLLAPSED, + SECTION_HEADER, + SECTION_HEADER_LEFT, + SECTION_HEADER_TITLE, + SECTION_HEADER_SUB_TITLE, + SECTION_HEADER_DIVIDER, + SECTION_HEADER_TABS, + SECTION_HEADER_RIGHT, + SECTION_CARD, }; -export { - CLASSES, -} +export { CLASSES }; diff --git a/packages/webapp/src/containers/Import/ImportFileMappingForm.tsx b/packages/webapp/src/containers/Import/ImportFileMappingForm.tsx index 29e4a3c54..ebe8e2f16 100644 --- a/packages/webapp/src/containers/Import/ImportFileMappingForm.tsx +++ b/packages/webapp/src/containers/Import/ImportFileMappingForm.tsx @@ -1,11 +1,11 @@ // @ts-nocheck +import { Intent } from '@blueprintjs/core'; import { useImportFileMapping } from '@/hooks/query/import'; import { Form, Formik, FormikHelpers } from 'formik'; import { useImportFileContext } from './ImportFileProvider'; import { useMemo } from 'react'; import { isEmpty } from 'lodash'; - -const validationSchema = null; +import { AppToaster } from '@/components'; interface ImportFileMappingFormProps { children: React.ReactNode; @@ -33,7 +33,13 @@ export function ImportFileMappingForm({ setSubmitting(false); setStep(2); }) - .catch((error) => { + .catch(({ response: { data } }) => { + if (data.errors.find(e => e.type === "DUPLICATED_FROM_MAP_ATTR")) { + AppToaster.show({ + message: 'Selected the same sheet columns to multiple fields.', + intent: Intent.DANGER + }) + } setSubmitting(false); }); }; @@ -42,7 +48,6 @@ export function ImportFileMappingForm({
{children}
diff --git a/packages/webapp/src/containers/Import/ImportFilePreview.module.scss b/packages/webapp/src/containers/Import/ImportFilePreview.module.scss new file mode 100644 index 000000000..f24ce4472 --- /dev/null +++ b/packages/webapp/src/containers/Import/ImportFilePreview.module.scss @@ -0,0 +1,42 @@ + +.previewList { + list-style: none; + margin-top: 14px; + + :global(li) { + border-top: 1px solid #d9d9da; + padding: 6px 0; + + &:last-child{ + padding-bottom: 0; + } + } +} + +.unmappedList { + padding-left: 2rem; +} + +.skippedTable { + width: 100%; + + thead{ + th{ + padding-top: 0; + padding-bottom: 8px; + color: #738091; + font-weight: 500; + } + } + + tbody{ + tr td { + vertical-align: middle; + padding: 7px; + } + + tr:hover td{ + background: #F6F7F9; + } + } +} \ No newline at end of file diff --git a/packages/webapp/src/containers/Import/ImportFilePreview.tsx b/packages/webapp/src/containers/Import/ImportFilePreview.tsx index 17aa8476d..326d72f73 100644 --- a/packages/webapp/src/containers/Import/ImportFilePreview.tsx +++ b/packages/webapp/src/containers/Import/ImportFilePreview.tsx @@ -7,10 +7,13 @@ import { useImportFilePreviewBootContext, } from './ImportFilePreviewBoot'; import { useImportFileContext } from './ImportFileProvider'; -import { AppToaster, Card, Group } from '@/components'; import { useImportFileProcess } from '@/hooks/query/import'; +import { AppToaster, Box, Group, Stack } from '@/components'; import { CLASSES } from '@/constants'; import { ImportStepperStep } from './_types'; +import { ImportFileContainer } from './ImportFileContainer'; +import { SectionCard, Section } from '@/components/Section'; +import styles from './ImportFilePreview.module.scss'; export function ImportFilePreview() { const { importId } = useImportFileContext(); @@ -26,23 +29,68 @@ function ImportFilePreviewContent() { const { importPreview } = useImportFilePreviewBootContext(); return ( -
- - {importPreview.createdCount} of {importPreview.totalCount} Items in your - file are ready to be imported. - + + + + + {importPreview.createdCount} of {importPreview.totalCount} Items in + your file are ready to be imported. + - + + + + + + + + ); +} + +function ImportFilePreviewImported() { + const { importPreview } = useImportFilePreviewBootContext(); + + return ( +
+ Items that are ready to be imported - {importPreview.createdCount} -
    -
  • Items to be created: ({importPreview.createdCount})
  • -
  • Items to be skipped: ({importPreview.skippedCount})
  • -
  • Items have errors: ({importPreview.errorsCount})
  • +
      +
    • + Items to be created: ({importPreview.createdCount}) +
    • +
    • + Items to be skipped: ({importPreview.skippedCount}) +
    • +
    • + Items have errors: ({importPreview.errorsCount}) +
    + +
+ ); +} - +function ImportFilePreviewSkipped() { + const { importPreview } = useImportFilePreviewBootContext(); + + return ( +
+ +
@@ -64,20 +112,28 @@ function ImportFilePreviewContent() { ))}
#
+ + + ); +} - - Unmapped Sheet Columns - ({importPreview?.unmappedColumnsCount}) - +function ImportFilePreviewUnmapped() { + const { importPreview } = useImportFilePreviewBootContext(); -
    + return ( +
    + +
      {importPreview.unmappedColumns?.map((column, key) => (
    • {column}
    • ))}
    - - - -
+ + ); } diff --git a/packages/webapp/src/containers/Import/ImportFileUploadStep.module.scss b/packages/webapp/src/containers/Import/ImportFileUploadStep.module.scss index 61fc5c3fd..95bfd50fd 100644 --- a/packages/webapp/src/containers/Import/ImportFileUploadStep.module.scss +++ b/packages/webapp/src/containers/Import/ImportFileUploadStep.module.scss @@ -5,6 +5,7 @@ .content { flex: 1; padding: 32px 20px; + padding-bottom: 80px; min-width: 660px; max-width: 760px; width: 75%; diff --git a/packages/webapp/src/static/json/icons.tsx b/packages/webapp/src/static/json/icons.tsx index 9e3a457cb..95cdbd46a 100644 --- a/packages/webapp/src/static/json/icons.tsx +++ b/packages/webapp/src/static/json/icons.tsx @@ -588,5 +588,17 @@ export default { 'M480-336 288-528l51-51 105 105v-342h72v342l105-105 51 51-192 192ZM263.717-192Q234-192 213-213.15T192-264v-72h72v72h432v-72h72v72q0 29.7-21.162 50.85Q725.676-192 695.96-192H263.717Z' ], viewBox: '0 -960 960 960' - } + }, + 'chevron-up': { + path: [ + 'M12.71,9.29l-4-4C8.53,5.11,8.28,5,8,5S7.47,5.11,7.29,5.29l-4,4C3.11,9.47,3,9.72,3,10c0,0.55,0.45,1,1,1c0.28,0,0.53-0.11,0.71-0.29L8,7.41l3.29,3.29C11.47,10.89,11.72,11,12,11c0.55,0,1-0.45,1-1C13,9.72,12.89,9.47,12.71,9.29' + ], + viewBox: '0 0 16 16', + }, + 'chevron-down': { + path: [ + 'M12,5c-0.28,0-0.53,0.11-0.71,0.29L8,8.59L4.71,5.29C4.53,5.11,4.28,5,4,5C3.45,5,3,5.45,3,6c0,0.28,0.11,0.53,0.29,0.71l4,4C7.47,10.89,7.72,11,8,11s0.53-0.11,0.71-0.29l4-4C12.89,6.53,13,6.28,13,6C13,5.45,12.55,5,12,5z' + ], + viewBox: '0 0 16 16', + }, }; diff --git a/packages/webapp/src/style/App.scss b/packages/webapp/src/style/App.scss index f18606673..b03ce5d27 100644 --- a/packages/webapp/src/style/App.scss +++ b/packages/webapp/src/style/App.scss @@ -36,6 +36,7 @@ // fonts @import 'pages/fonts'; +@import "section"; .App { min-width: 960px; diff --git a/packages/webapp/src/style/objects/typography.scss b/packages/webapp/src/style/objects/typography.scss index 9bd78c5f2..b4c97ce76 100644 --- a/packages/webapp/src/style/objects/typography.scss +++ b/packages/webapp/src/style/objects/typography.scss @@ -9,7 +9,7 @@ body{ } .#{$ns}-heading{ - font-weight: 400; + font-weight: 600; } .divider{ diff --git a/packages/webapp/src/style/section.scss b/packages/webapp/src/style/section.scss new file mode 100644 index 000000000..eab732844 --- /dev/null +++ b/packages/webapp/src/style/section.scss @@ -0,0 +1,117 @@ +@use "sass:math"; +@import './_base.scss'; + +$section-min-height: $pt-grid-size * 5 !default; +$section-padding-vertical: $pt-grid-size !default; +$section-padding-horizontal: $pt-grid-size * 2 !default; +$section-card-padding: $pt-grid-size * 2 !default; + +$section-min-height-compact: $pt-grid-size * 4 !default; +$section-padding-compact-vertical: 7px !default; +$section-padding-compact-horizontal: 15px !default; +$section-card-padding-compact: $pt-grid-size * 1.5 !default; + +.#{$ns}-section { + overflow: hidden; + width: 100%; + + &, + &.#{$ns}-compact { + // override Card compact styles here + padding: 0; + } + + &-header { + align-items: center; + border-bottom: 1px solid $pt-divider-black; + display: flex; + gap: $pt-grid-size * 2; + justify-content: space-between; + min-height: $section-min-height; + padding: 0 $section-padding-horizontal; + position: relative; + width: 100%; + + &.#{$ns}-dark, + .#{$ns}-dark & { + border-color: $pt-dark-divider-white; + } + + &-left { + align-items: center; + display: flex; + gap: $pt-grid-size; + padding: $section-padding-vertical 0; + } + + &-title { + margin-bottom: 0; + } + + &-sub-title { + margin-top: 2px; + } + + &-right { + align-items: center; + display: flex; + gap: $pt-grid-size; + margin-left: auto; + } + + &-divider { + align-self: stretch; + margin: $pt-grid-size * 1.5 0; + } + + &.#{$ns}-interactive { + cursor: pointer; + + &:hover, + &:active { + background: $light-gray5; + + &.#{$ns}-dark, + .#{$ns}-dark & { + background: $dark-gray4; + } + } + } + } + + &-card { + &.#{$ns}-padded { + padding: $section-card-padding; + } + + &:not(:last-child) { + border-bottom: 1px solid $pt-divider-black; + + &.#{$ns}-dark, + .#{$ns}-dark & { + border-color: $pt-dark-divider-white; + } + } + } + + &.#{$ns}-section-collapsed { + .#{$ns}-section-header { + border: none; + } + } + + &.#{$ns}-compact { + .#{$ns}-section-header { + min-height: $section-min-height-compact; + padding: 0 $section-padding-compact-horizontal; + + &-left { + padding: $section-padding-compact-vertical 0; + } + } + + .#{$ns}-section-card.#{$ns}-padded { + padding: $section-card-padding-compact; + } + } +} \ No newline at end of file