From 7375512fecfc5260eb2160f9ff2557effa2d5b8e Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 9 Feb 2026 19:26:26 +0200 Subject: [PATCH] refactor: update UniversalSearch components with TypeScript and TextStatus --- .../src/components/TextStatus/index.tsx | 9 +- .../UniversalSearch/UniversalSearch.tsx | 396 +++++++++++++++--- .../UniversalSearchProvider.tsx | 74 +++- .../Purchases/Bills/BillUniversalSearch.tsx | 24 +- .../EstimateUniversalSearch.tsx | 20 +- .../Sales/Invoices/InvoiceUniversalSearch.tsx | 21 +- .../Sales/Receipts/ReceiptUniversalSearch.tsx | 13 +- packages/webapp/src/style/App.scss | 1 - packages/webapp/src/style/_variables.scss | 42 ++ .../src/style/components/UniversalSearch.scss | 200 --------- 10 files changed, 484 insertions(+), 316 deletions(-) delete mode 100644 packages/webapp/src/style/components/UniversalSearch.scss diff --git a/packages/webapp/src/components/TextStatus/index.tsx b/packages/webapp/src/components/TextStatus/index.tsx index 1c3d5fe03..a46a0e81d 100644 --- a/packages/webapp/src/components/TextStatus/index.tsx +++ b/packages/webapp/src/components/TextStatus/index.tsx @@ -10,12 +10,17 @@ const TextStatusRoot = styled.span` ${(props) => props.intent === 'warning' && ` - color: #ec5b0a;`} + color: #c87619;`} + + ${(props) => + props.intent === 'danger' && + ` + color: #f17377;`} ${(props) => props.intent === 'success' && ` - color: #2ba01d;`} + color: #238551;`} ${(props) => props.intent === 'none' && diff --git a/packages/webapp/src/components/UniversalSearch/UniversalSearch.tsx b/packages/webapp/src/components/UniversalSearch/UniversalSearch.tsx index 9d65ef565..5874abba3 100644 --- a/packages/webapp/src/components/UniversalSearch/UniversalSearch.tsx +++ b/packages/webapp/src/components/UniversalSearch/UniversalSearch.tsx @@ -1,7 +1,5 @@ -// @ts-nocheck -import React from 'react'; +import React, { KeyboardEvent, ReactNode } from 'react'; import intl from 'react-intl-universal'; -import classNames from 'classnames'; import { isUndefined } from 'lodash'; import { Overlay, @@ -10,11 +8,14 @@ import { MenuItem, Spinner, Intent, + OverlayProps, + Button, } from '@blueprintjs/core'; -import { QueryList } from '@blueprintjs/select'; -import { CLASSES } from '@/constants/classes'; - -import { Icon, If, ListSelect, FormattedMessage as T } from '@/components'; +import { QueryList, ItemRenderer } from '@blueprintjs/select'; +import { x } from '@xstyled/emotion'; +import { css } from '@emotion/css'; +import { Icon, If, FormattedMessage as T } from '@/components'; +import { Select } from '@blueprintjs-formik/select'; import { UniversalSearchProvider, useUniversalSearchContext, @@ -22,59 +23,297 @@ import { import { filterItemsByResourceType } from './utils'; import { RESOURCES_TYPES } from '@/constants/resourcesTypes'; +// Resource type from RESOURCES_TYPES constant +type ResourceType = string; + +// Search type option item +interface SearchTypeOption { + key: ResourceType; + label: string; +} + +// Universal search item +interface UniversalSearchItem { + id: number | string; + _type: ResourceType; + text: string; + subText?: string; + label?: string; + [key: string]: any; +} + +// CSS styles for complex selectors +const overlayStyles = css` + .bp4-overlay-appear, + .bp4-overlay-enter { + filter: blur(20px); + opacity: 0.2; + } + .bp4-overlay-appear-active, + .bp4-overlay-enter-active { + filter: blur(0); + opacity: 1; + transition: + filter 0.2s cubic-bezier(0.4, 1, 0.75, 0.9), + opacity 0.2s cubic-bezier(0.4, 1, 0.75, 0.9); + } + .bp4-overlay-exit { + filter: blur(0); + opacity: 1; + } + .bp4-overlay-exit-active { + filter: blur(20px); + opacity: 0.2; + transition: + filter 0.2s cubic-bezier(0.4, 1, 0.75, 0.9), + opacity 0.2s cubic-bezier(0.4, 1, 0.75, 0.9); + } +`; + +const containerStyles = css` + position: fixed; + filter: blur(0); + opacity: 1; + background-color: var(--color-universal-search-background); + border-radius: 3px; + box-shadow: + 0 0 0 1px rgba(16, 22, 26, 0.1), + 0 4px 8px rgba(16, 22, 26, 0.2), + 0 18px 46px 6px rgba(16, 22, 26, 0.2); + left: calc(50% - 250px); + top: 20vh; + width: 500px; + z-index: 20; + + .bp4-input-group { + .bp4-icon { + margin: 16px; + color: var(--color-universal-search-icon); + + svg { + stroke: currentColor; + fill: none; + fill-rule: evenodd; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 2; + --text-opacity: 1; + } + } + } + + .bp4-input-group .bp4-input { + border: 0; + box-shadow: 0 0 0 0; + height: 50px; + line-height: 50px; + font-size: 20px; + } + .bp4-input-group.bp4-large .bp4-input:not(:first-child) { + padding-left: 50px !important; + } + .bp4-input-group.bp4-large .bp4-input:not(:last-child) { + padding-right: 130px !important; + } + + .bp4-menu { + border-top: 1px solid var(--color-universal-search-menu-border); + max-height: calc(60vh - 20px); + overflow: auto; + + .bp4-menu-item { + .bp4-text-muted { + font-size: 12px; + + .bp4-icon { + color: var(--bp4-gray-600); + } + } + &.bp4-intent-primary { + &.bp4-active { + background-color: var(--bp4-blue-100); + color: var(--bp4-dark-gray-800); + + .bp4-menu-item-label { + color: var(--bp4-gray-600); + } + } + } + + &-label { + flex-direction: row; + text-align: right; + } + } + } + + .bp4-input-action { + height: 100%; + display: flex; + flex-direction: row; + align-items: center; + } +`; + +const inputRightElementsStyles = css` + display: flex; + margin: 10px; + + .bp4-spinner { + margin-right: 6px; + } +`; + +const footerStyles = css` + padding: 12px 12px; + border-top: 1px solid var(--color-universal-search-footer-divider); +`; + +const actionBaseStyles = css` + &:not(:first-of-type) { + margin-left: 14px; + } + + .bp4-tag { + background: var(--color-universal-search-tag-background); + color: var(--color-universal-search-tag-text); + } +`; + +const actionArrowsStyles = css` + &:not(:first-of-type) { + margin-left: 14px; + } + + .bp4-tag { + background: var(--color-universal-search-tag-background); + color: var(--color-universal-search-tag-text); + padding: 0; + text-align: center; + line-height: 16px; + margin-left: 4px; + + svg { + fill: var(--color-universal-search-tag-text); + height: 100%; + display: block; + width: 100%; + padding: 2px; + } + } +`; + +// UniversalSearchInputRightElements props +interface UniversalSearchInputRightElementsProps { + /** Callback when search type changes */ + onSearchTypeChange?: (option: SearchTypeOption) => void; +} + /** * Universal search input action. */ -function UniversalSearchInputRightElements({ onSearchTypeChange }) { - const { isLoading, searchType, defaultSearchResource, searchTypeOptions } = +function UniversalSearchInputRightElements({ + onSearchTypeChange, +}: UniversalSearchInputRightElementsProps) { + const { isLoading, searchType, searchTypeOptions } = useUniversalSearchContext(); + // Find the currently selected item object. + const selectedItem = searchTypeOptions.find( + (item) => item.key === searchType, + ); + // Handle search type option change. - const handleSearchTypeChange = (option) => { - onSearchTypeChange && onSearchTypeChange(option); + const handleSearchTypeChange = (option: SearchTypeOption) => { + onSearchTypeChange?.(option); + }; + + // Item renderer for the select dropdown. + const itemRenderer: ItemRenderer = ( + item, + { handleClick }, + ) => { + return ; }; return ( -
+ - + - items={searchTypeOptions} + itemRenderer={itemRenderer} onItemSelect={handleSearchTypeChange} + selectedValue={selectedItem?.key} + valueAccessor={'key'} + labelAccessor={'label'} filterable={false} - initialSelectedItem={defaultSearchResource} - selectedItem={searchType} - selectedItemProp={'key'} - textProp={'label'} - // defaultText={intl.get('type')} popoverProps={{ minimal: true, captureDismiss: true, - className: CLASSES.UNIVERSAL_SEARCH_TYPE_SELECT_OVERLAY, - }} - buttonProps={{ - minimal: true, - className: CLASSES.UNIVERSAL_SEARCH_TYPE_SELECT_BTN, }} + input={({ activeItem }) => ( +
+ ); } +// QueryList renderer props +interface QueryListRendererProps { + /** Current query string */ + query: string; + /** Callback when query changes */ + handleQueryChange: (event: React.ChangeEvent) => void; + /** Item list element */ + itemList: ReactNode; + /** Class name */ + className?: string; + /** Handle key down */ + handleKeyDown?: (event: KeyboardEvent) => void; + /** Handle key up */ + handleKeyUp?: (event: KeyboardEvent) => void; +} + +// UniversalSearchQueryList props +interface UniversalSearchQueryListProps { + /** Whether the search is open */ + isOpen: boolean; + /** Whether the search is loading */ + isLoading: boolean; + /** Callback when search type changes */ + onSearchTypeChange?: (option: SearchTypeOption) => void; + /** Current search type */ + searchType: ResourceType; + /** Items to display */ + items: UniversalSearchItem[]; + /** Renderer for items */ + itemRenderer?: ItemRenderer; + /** Callback when an item is selected */ + onItemSelect?: (item: UniversalSearchItem, event?: any) => void; + /** Current query string */ + query: string; + /** Callback when query changes */ + onQueryChange?: (query: string) => void; +} + /** * Universal search query list. */ -function UniversalSearchQueryList(props) { - const { isOpen, isLoading, onSearchTypeChange, searchType, ...restProps } = - props; - +function UniversalSearchQueryList({ + isOpen, + isLoading, + onSearchTypeChange, + ...restProps +}: UniversalSearchQueryListProps) { return ( - + {...(restProps as any)} initialContent={null} - renderer={(listProps) => ( + renderer={(listProps: QueryListRendererProps) => ( -
+ + ENTER - {intl.get('universal_search.enter_text')} -
+ {intl.get('universal_search.enter_text')} + -
+ ESC{' '} - {intl.get('universal_search.close_text')} -
+ {intl.get('universal_search.close_text')} + -
+ - {intl.get('universal_seach.navigate_text')} -
- + {intl.get('universal_seach.navigate_text')} + + ); } +// UniversalSearchBar props +interface UniversalSearchBarProps extends QueryListRendererProps { + /** Whether the search is open */ + isOpen: boolean; + /** Callback when search type changes */ + onSearchTypeChange?: (option: SearchTypeOption) => void; +} + /** * Universal search input bar with items list. */ -function UniversalSearchBar({ isOpen, onSearchTypeChange, ...listProps }) { +function UniversalSearchBar({ + isOpen, + onSearchTypeChange, + ...listProps +}: UniversalSearchBarProps) { const { handleKeyDown, handleKeyUp } = listProps; const handlers = isOpen ? { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp } : {}; return ( -
+ } @@ -155,17 +400,44 @@ function UniversalSearchBar({ isOpen, onSearchTypeChange, ...listProps }) { autoFocus={true} /> {listProps.itemList} -
+ ); } +// UniversalSearch props +export interface UniversalSearchProps { + /** Default search resource type */ + defaultSearchResource?: ResourceType; + /** Controlled search resource type */ + searchResource?: ResourceType; + /** Overlay props */ + overlayProps?: OverlayProps; + /** Whether the search overlay is open */ + isOpen: boolean; + /** Whether the search is loading */ + isLoading: boolean; + /** Callback when search type changes */ + onSearchTypeChange?: (resource: SearchTypeOption) => void; + /** Items to display */ + items: UniversalSearchItem[]; + /** Available search type options */ + searchTypeOptions: SearchTypeOption[]; + /** Renderer for items */ + itemRenderer?: ItemRenderer; + /** Callback when an item is selected */ + onItemSelect?: (item: UniversalSearchItem, event?: any) => void; + /** Current query string */ + query: string; + /** Callback when query changes */ + onQueryChange?: (query: string) => void; +} + /** * Universal search. */ export function UniversalSearch({ defaultSearchResource, searchResource, - overlayProps, isOpen, isLoading, @@ -173,9 +445,9 @@ export function UniversalSearch({ items, searchTypeOptions, ...queryListProps -}) { +}: UniversalSearchProps) { // Search type state. - const [searchType, setSearchType] = React.useState( + const [searchType, setSearchType] = React.useState( defaultSearchResource || RESOURCES_TYPES.CUSTOMER, ); // Handle search resource type controlled mode. @@ -189,9 +461,9 @@ export function UniversalSearch({ }, [searchResource, defaultSearchResource]); // Handle search type change. - const handleSearchTypeChange = (searchTypeResource) => { + const handleSearchTypeChange = (searchTypeResource: SearchTypeOption) => { setSearchType(searchTypeResource.key); - onSearchTypeChange && onSearchTypeChange(searchTypeResource); + onSearchTypeChange?.(searchTypeResource); }; // Filters query list items based on the given search type. const filteredItems = filterItemsByResourceType(items, searchType); @@ -200,7 +472,7 @@ export function UniversalSearch({ -
+ -
+ -
-
+ +
); diff --git a/packages/webapp/src/components/UniversalSearch/UniversalSearchProvider.tsx b/packages/webapp/src/components/UniversalSearch/UniversalSearchProvider.tsx index f7f206593..6cb84b978 100644 --- a/packages/webapp/src/components/UniversalSearch/UniversalSearchProvider.tsx +++ b/packages/webapp/src/components/UniversalSearch/UniversalSearchProvider.tsx @@ -1,30 +1,82 @@ -// @ts-nocheck -import React, { createContext } from 'react'; +import React, { createContext, ReactNode, useContext } from 'react'; -const UniversalSearchContext = createContext(); +// The resource type value from RESOURCES_TYPES constant +type ResourceType = string; + +// Search type option item +interface SearchTypeOption { + key: ResourceType; + label: string; +} + +// Context value type +interface UniversalSearchContextValue { + /** Whether the search is loading */ + isLoading: boolean; + /** Current search type/resource type */ + searchType: ResourceType; + /** Default search resource type */ + defaultSearchResource?: ResourceType; + /** List of available search type options */ + searchTypeOptions: SearchTypeOption[]; +} + +// Create the context with undefined as initial value +const UniversalSearchContext = createContext< + UniversalSearchContextValue | undefined +>(undefined); + +// Provider props interface +interface UniversalSearchProviderProps { + /** Whether the search is loading */ + isLoading: boolean; + /** Default search resource type */ + defaultSearchResource?: ResourceType; + /** Current search type/resource type */ + searchType: ResourceType; + /** List of available search type options */ + searchTypeOptions: SearchTypeOption[]; + /** Child elements */ + children: ReactNode; +} /** * Universal search data provider. */ -function UniversalSearchProvider({ +export function UniversalSearchProvider({ isLoading, defaultSearchResource, searchType, searchTypeOptions, - ...props -}) { + children, +}: UniversalSearchProviderProps) { // Provider payload. - const provider = { + const provider: UniversalSearchContextValue = { isLoading, searchType, defaultSearchResource, searchTypeOptions, }; - return ; + return ( + + {children} + + ); } -const useUniversalSearchContext = () => - React.useContext(UniversalSearchContext); +/** + * Hook to access the universal search context. + * @throws Error if used outside of UniversalSearchProvider + */ +export const useUniversalSearchContext = (): UniversalSearchContextValue => { + const context = useContext(UniversalSearchContext); -export { UniversalSearchProvider, useUniversalSearchContext }; + if (context === undefined) { + throw new Error( + 'useUniversalSearchContext must be used within a UniversalSearchProvider', + ); + } + + return context; +}; diff --git a/packages/webapp/src/containers/Purchases/Bills/BillUniversalSearch.tsx b/packages/webapp/src/containers/Purchases/Bills/BillUniversalSearch.tsx index b54ced67d..cabc7b353 100644 --- a/packages/webapp/src/containers/Purchases/Bills/BillUniversalSearch.tsx +++ b/packages/webapp/src/containers/Purchases/Bills/BillUniversalSearch.tsx @@ -1,10 +1,10 @@ // @ts-nocheck import React from 'react'; import intl from 'react-intl-universal'; -import { MenuItem } from '@blueprintjs/core'; +import { MenuItem, Intent } from '@blueprintjs/core'; import { formattedAmount } from '@/utils'; -import { T, Icon, Choose, If } from '@/components'; +import { T, Icon, Choose, If, TextStatus } from '@/components'; import { RESOURCES_TYPES } from '@/constants/resourcesTypes'; import { AbilitySubject, BillAction } from '@/constants/abilityOption'; @@ -41,35 +41,35 @@ export function BillStatus({ bill }) { return ( - + - + - + {intl.get('overdue_by', { overdue: bill.overdue_days })} - + - + {intl.get('due_in', { due: bill.remaining_days })} - + - + {intl.get('day_partially_paid', { due: formattedAmount(bill.due_amount, bill.currency_code), })} - + - + - + ); diff --git a/packages/webapp/src/containers/Sales/Estimates/EstimatesLanding/EstimateUniversalSearch.tsx b/packages/webapp/src/containers/Sales/Estimates/EstimatesLanding/EstimateUniversalSearch.tsx index fbdda4f4a..ef6e24bd3 100644 --- a/packages/webapp/src/containers/Sales/Estimates/EstimatesLanding/EstimateUniversalSearch.tsx +++ b/packages/webapp/src/containers/Sales/Estimates/EstimatesLanding/EstimateUniversalSearch.tsx @@ -1,9 +1,9 @@ // @ts-nocheck import React from 'react'; import intl from 'react-intl-universal'; -import { MenuItem } from '@blueprintjs/core'; +import { MenuItem, Intent } from '@blueprintjs/core'; -import { Choose, T, Icon } from '@/components'; +import { Choose, T, Icon, TextStatus } from '@/components'; import { RESOURCES_TYPES } from '@/constants/resourcesTypes'; import { AbilitySubject, SaleEstimateAction } from '@/constants/abilityOption'; @@ -37,28 +37,28 @@ export const EstimateUniversalSearchSelect = withDrawerActions( export const EstimateStatus = ({ estimate }) => ( - + - + - + - + - + - + - + - + ); diff --git a/packages/webapp/src/containers/Sales/Invoices/InvoiceUniversalSearch.tsx b/packages/webapp/src/containers/Sales/Invoices/InvoiceUniversalSearch.tsx index aa76f2481..879ccf2d8 100644 --- a/packages/webapp/src/containers/Sales/Invoices/InvoiceUniversalSearch.tsx +++ b/packages/webapp/src/containers/Sales/Invoices/InvoiceUniversalSearch.tsx @@ -1,9 +1,9 @@ // @ts-nocheck import React from 'react'; import intl from 'react-intl-universal'; -import { MenuItem } from '@blueprintjs/core'; +import { MenuItem, Intent } from '@blueprintjs/core'; -import { T, Choose, Icon } from '@/components'; +import { T, Choose, Icon, TextStatus } from '@/components'; import { highlightText } from '@/utils'; import { RESOURCES_TYPES } from '@/constants/resourcesTypes'; @@ -39,29 +39,29 @@ function InvoiceStatus({ customer }) { return ( - + - + - + {intl.get('overdue_by', { overdue: customer.overdue_days })} - + - + {intl.get('due_in', { due: customer.remaining_days })} - + - + - + ); @@ -94,7 +94,6 @@ export function InvoiceUniversalSearchItem( } onClick={handleClick} - className={'universal-search__item--invoice'} /> ); } diff --git a/packages/webapp/src/containers/Sales/Receipts/ReceiptUniversalSearch.tsx b/packages/webapp/src/containers/Sales/Receipts/ReceiptUniversalSearch.tsx index 7b7c645d8..9c1aca8be 100644 --- a/packages/webapp/src/containers/Sales/Receipts/ReceiptUniversalSearch.tsx +++ b/packages/webapp/src/containers/Sales/Receipts/ReceiptUniversalSearch.tsx @@ -1,9 +1,8 @@ // @ts-nocheck import React from 'react'; import intl from 'react-intl-universal'; -import { MenuItem } from '@blueprintjs/core'; - -import { Icon, Choose, T } from '@/components'; +import { MenuItem, Intent } from '@blueprintjs/core'; +import { Icon, Choose, T, TextStatus } from '@/components'; import { RESOURCES_TYPES } from '@/constants/resourcesTypes'; import { AbilitySubject, SaleReceiptAction } from '@/constants/abilityOption'; import { withDrawerActions } from '@/containers/Drawer/withDrawerActions'; @@ -39,15 +38,15 @@ function ReceiptStatus({ receipt }) { return ( - + - + - + - + ); diff --git a/packages/webapp/src/style/App.scss b/packages/webapp/src/style/App.scss index e6f046584..ecebc0c5c 100644 --- a/packages/webapp/src/style/App.scss +++ b/packages/webapp/src/style/App.scss @@ -31,7 +31,6 @@ @import 'components/Overlay'; @import 'components/Menu'; @import 'components/SidebarOverlay'; -@import 'components/UniversalSearch'; // Pages @import 'pages/view-form'; diff --git a/packages/webapp/src/style/_variables.scss b/packages/webapp/src/style/_variables.scss index 40df32901..9cf20f6a7 100644 --- a/packages/webapp/src/style/_variables.scss +++ b/packages/webapp/src/style/_variables.scss @@ -7,6 +7,27 @@ $ns: bp4; --color-primary: #8abbff; --color-danger: red; + // Green colors + --color-green-500: #165a36; + --color-green-400: #1c6e42; + --color-green-300: #238551; + --color-green-200: #32a467; + --color-green-100: #72ca9b; + + // Red colors + --color-red-500: #8e292c; + --color-red-400: #ac2f33; + --color-red-300: #cd4246; + --color-red-200: #e76a6e; + --color-red-100: #fa999c; + + // Orange colors + --color-orange-500: #77450d; + --color-orange-400: #935610; + --color-orange-300: #c87619; + --color-orange-200: #ec9a3c; + --color-orange-100: #fbb360; + --color-dark-gray5: #404854; --color-dark-gray4: #383e47; --color-dark-gray3: #2f343c; @@ -301,6 +322,27 @@ body.bp4-dark { --color-primary: #8abbff; --color-danger: rgb(213, 103, 103); + // Green colors (dark mode - lighter variants) + --color-green-500: #72ca9b; + --color-green-400: #32a467; + --color-green-300: #238551; + --color-green-200: #1c6e42; + --color-green-100: #165a36; + + // Red colors (dark mode - lighter variants) + --color-red-500: #fa999c; + --color-red-400: #e76a6e; + --color-red-300: #cd4246; + --color-red-200: #ac2f33; + --color-red-100: #8e292c; + + // Orange colors (dark mode - lighter variants) + --color-orange-500: #fbb360; + --color-orange-400: #ec9a3c; + --color-orange-300: #c87619; + --color-orange-200: #935610; + --color-orange-100: #77450d; + --color-dark-gray5: #404854; --color-dark-gray4: #383e47; --color-dark-gray3: #2f343c; diff --git a/packages/webapp/src/style/components/UniversalSearch.scss b/packages/webapp/src/style/components/UniversalSearch.scss deleted file mode 100644 index a9562e096..000000000 --- a/packages/webapp/src/style/components/UniversalSearch.scss +++ /dev/null @@ -1,200 +0,0 @@ -.universal-search { - position: fixed; - filter: blur(0); - opacity: 1; - background-color: var(--color-universal-search-background); - border-radius: 3px; - box-shadow: 0 0 0 1px rgba(16, 22, 26, 0.1), - 0 4px 8px rgba(16, 22, 26, 0.2), - 0 18px 46px 6px rgba(16, 22, 26, 0.2); - left: calc(50% - 250px); - top: 20vh; - width: 500px; - z-index: 20; - - &.bp4-overlay-appear, - &.bp4-overlay-enter { - filter: blur(20px); - opacity: 0.2; - } - &.bp4-overlay-appear-active, - &.bp4-overlay-enter-active { - filter: blur(0); - opacity: 1; - transition-delay: 0; - transition-duration: 0.2s; - transition-property: filter, opacity; - transition-timing-function: cubic-bezier(0.4, 1, 0.75, 0.9); - } - &.bp4-overlay-exit { - filter: blur(0); - opacity: 1; - } - &.bp4-overlay-exit-active { - filter: blur(20px); - opacity: 0.2; - transition-delay: 0; - transition-duration: 0.2s; - transition-property: filter, opacity; - transition-timing-function: cubic-bezier(0.4, 1, 0.75, 0.9); - } - - &__omnibar { - .bp4-input-group { - .bp4-icon { - svg { - stroke: currentColor; - fill: none; - fill-rule: evenodd; - stroke-linecap: round; - stroke-linejoin: round; - } - } - } - - .bp4-input-group .bp4-input { - border: 0; - box-shadow: 0 0 0 0; - height: 50px; - line-height: 50px; - font-size: 20px; - } - .bp4-input-group.bp4-large .bp4-input:not(:first-child) { - padding-left: 50px !important; - } - .bp4-input-group.bp4-large .bp4-input:not(:last-child) { - padding-right: 130px !important; - } - - .bp4-input-group { - .bp4-icon { - margin: 16px; - color: var(--color-universal-search-icon); - - svg { - stroke-width: 2; - --text-opacity: 1; - } - } - } - - .bp4-menu { - border-top: 1px solid var(--color-universal-search-menu-border); - max-height: calc(60vh - 20px); - overflow: auto; - - .bp4-menu-item { - .bp4-text-muted { - font-size: 12px; - - .bp4-icon { - color: #8499a7; - } - } - &.bp4-intent-primary { - &.bp4-active { - background-color: rgb(235, 241, 246); - color: #252b30; - - .bp4-menu-item-label { - color: #5c7080; - } - } - } - - &-label { - flex-direction: row; - text-align: right; - } - } - } - - .bp4-input-action { - height: 100%; - display: flex; - flex-direction: row; - align-items: center; - } - } - - &__type-select-overlay { - .bp4-button { - margin: 0 !important; - } - } - - &__footer { - padding: 12px 12px; - border-top: 1px solid var(--color-universal-search-footer-divider); - } - - &__actions { - display: flex; - } - - &__action { - &:not(:first-of-type) { - margin-left: 14px; - } - - .bp4-tag { - background: var(--color-universal-search-tag-background); - color: var(--color-universal-search-tag-text); - } - &--arrows { - .bp4-tag { - padding: 0; - text-align: center; - line-height: 16px; - margin-left: 4px; - - svg { - fill: var(--color-universal-search-tag-text); - height: 100%; - display: block; - width: 100%; - padding: 2px; - } - } - } - - .text { - margin-left: 6px; - } - } - - &__footer { - } - - &-input-right-elements { - display: flex; - margin: 10px; - - .bp4-spinner { - margin-right: 6px; - } - } - - &__item { - &--invoice, - &--estimate, - &--bill, - &--receipt { - .amount { - color: #252b30; - } - - .status { - font-size: 13px; - - &.status-warning { - color: rgb(236, 91, 10); - } - - &.status-success { - color: #249017; - } - } - } - } -}