mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
Merge branch 'feature/landed-cost'
This commit is contained in:
@@ -4,6 +4,7 @@ import { setLocale } from 'yup';
|
||||
import intl from 'react-intl-universal';
|
||||
import { find } from 'lodash';
|
||||
import rtlDetect from 'rtl-detect';
|
||||
import { AppIntlProvider } from './AppIntlProvider';
|
||||
import DashboardLoadingIndicator from 'components/Dashboard/DashboardLoadingIndicator';
|
||||
|
||||
const SUPPORTED_LOCALES = [
|
||||
@@ -40,16 +41,14 @@ function loadYupLocales(currentLocale) {
|
||||
/**
|
||||
* Modifies the html document direction to RTl if it was rtl-language.
|
||||
*/
|
||||
function useDocumentDirectionModifier(locale) {
|
||||
function useDocumentDirectionModifier(locale, isRTL) {
|
||||
React.useEffect(() => {
|
||||
const isRTL = rtlDetect.isRtlLang(locale);
|
||||
|
||||
if (isRTL) {
|
||||
const htmlDocument = document.querySelector('html');
|
||||
htmlDocument.setAttribute('dir', 'rtl');
|
||||
htmlDocument.setAttribute('lang', locale);
|
||||
}
|
||||
}, []);
|
||||
}, [isRTL, locale]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,8 +58,10 @@ export default function AppIntlLoader({ children }) {
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
const currentLocale = getCurrentLocal();
|
||||
|
||||
const isRTL = rtlDetect.isRtlLang(currentLocale);
|
||||
|
||||
// Modifies the html document direction
|
||||
useDocumentDirectionModifier(currentLocale);
|
||||
useDocumentDirectionModifier(currentLocale, isRTL);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Lodas the locales data file.
|
||||
@@ -86,10 +87,12 @@ export default function AppIntlLoader({ children }) {
|
||||
})
|
||||
.then(() => {});
|
||||
}, [currentLocale]);
|
||||
|
||||
|
||||
return (
|
||||
<DashboardLoadingIndicator isLoading={isLoading}>
|
||||
{children}
|
||||
</DashboardLoadingIndicator>
|
||||
<AppIntlProvider currentLocale={currentLocale} isRTL={isRTL}>
|
||||
<DashboardLoadingIndicator isLoading={isLoading}>
|
||||
{children}
|
||||
</DashboardLoadingIndicator>
|
||||
</AppIntlProvider>
|
||||
);
|
||||
}
|
||||
|
||||
24
client/src/components/AppIntlProvider.js
Normal file
24
client/src/components/AppIntlProvider.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import React, { createContext } from 'react';
|
||||
|
||||
const AppIntlContext = createContext();
|
||||
|
||||
/**
|
||||
* Application intl provider.
|
||||
*/
|
||||
function AppIntlProvider({ currentLocale, isRTL, children }) {
|
||||
const provider = {
|
||||
currentLocale,
|
||||
isRTL,
|
||||
isLTR: !isRTL,
|
||||
};
|
||||
|
||||
return (
|
||||
<AppIntlContext.Provider value={provider}>
|
||||
{children}
|
||||
</AppIntlContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const useAppIntlContext = () => React.useContext(AppIntlContext);
|
||||
|
||||
export { AppIntlProvider, useAppIntlContext };
|
||||
5
client/src/components/Card.js
Normal file
5
client/src/components/Card.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Card({ children }) {
|
||||
return <div class="card">{children}</div>;
|
||||
}
|
||||
@@ -1,58 +1,57 @@
|
||||
import React, { useMemo, useCallback, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { MenuItem, Button } from '@blueprintjs/core';
|
||||
import { omit } from 'lodash';
|
||||
import intl from 'react-intl-universal';
|
||||
import MultiSelect from 'components/MultiSelect';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { safeInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Contacts multi-select component.
|
||||
*/
|
||||
export default function ContactsMultiSelect({
|
||||
contacts,
|
||||
defaultText = <T id={'all_customers'} />,
|
||||
buttonProps,
|
||||
|
||||
onCustomerSelected: onContactSelected,
|
||||
...selectProps
|
||||
onContactSelect,
|
||||
contactsSelected = [],
|
||||
...multiSelectProps
|
||||
}) {
|
||||
const [selectedContacts, setSelectedContacts] = useState({});
|
||||
const [localSelected, setLocalSelected] = useState([ ...contactsSelected]);
|
||||
|
||||
const isContactSelect = useCallback(
|
||||
(id) => typeof selectedContacts[id] !== 'undefined',
|
||||
[selectedContacts],
|
||||
// Detarmines the given id is selected.
|
||||
const isItemSelected = useCallback(
|
||||
(id) => localSelected.some(s => s === id),
|
||||
[localSelected],
|
||||
);
|
||||
|
||||
// Contact item renderer.
|
||||
const contactRenderer = useCallback(
|
||||
(contact, { handleClick }) => (
|
||||
<MenuItem
|
||||
icon={isContactSelect(contact.id) ? 'tick' : 'blank'}
|
||||
icon={isItemSelected(contact.id) ? 'tick' : 'blank'}
|
||||
text={contact.display_name}
|
||||
key={contact.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
),
|
||||
[isContactSelect],
|
||||
[isItemSelected],
|
||||
);
|
||||
|
||||
const countSelected = useMemo(
|
||||
() => Object.values(selectedContacts).length,
|
||||
[selectedContacts],
|
||||
);
|
||||
// Count selected items.
|
||||
const countSelected = localSelected.length;
|
||||
|
||||
const onContactSelect = useCallback(
|
||||
// Handle item selected.
|
||||
const handleItemSelect = useCallback(
|
||||
({ id }) => {
|
||||
const selected = {
|
||||
...(isContactSelect(id)
|
||||
? {
|
||||
...omit(selectedContacts, [id]),
|
||||
}
|
||||
: {
|
||||
...selectedContacts,
|
||||
[id]: true,
|
||||
}),
|
||||
};
|
||||
setSelectedContacts({ ...selected });
|
||||
onContactSelected && onContactSelected(selected);
|
||||
const selected = isItemSelected(id)
|
||||
? localSelected.filter(s => s !== id)
|
||||
: [...localSelected, id];
|
||||
|
||||
setLocalSelected([ ...selected ]);
|
||||
safeInvoke(onContactSelect, selected);
|
||||
},
|
||||
[setSelectedContacts, selectedContacts, isContactSelect, onContactSelected],
|
||||
[setLocalSelected, localSelected, isItemSelected, onContactSelect],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -62,7 +61,8 @@ export default function ContactsMultiSelect({
|
||||
itemRenderer={contactRenderer}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={true}
|
||||
onItemSelect={onContactSelect}
|
||||
onItemSelect={handleItemSelect}
|
||||
{...multiSelectProps}
|
||||
>
|
||||
<Button
|
||||
text={
|
||||
|
||||
41
client/src/components/DataTableCells/CheckBoxFieldCell.js
Normal file
41
client/src/components/DataTableCells/CheckBoxFieldCell.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Classes, Checkbox, FormGroup, Intent } from '@blueprintjs/core';
|
||||
|
||||
const CheckboxEditableCell = ({
|
||||
row: { index },
|
||||
column: { id },
|
||||
cell: { value: initialValue },
|
||||
payload,
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(initialValue);
|
||||
|
||||
const onChange = (e) => {
|
||||
setValue(e.target.value);
|
||||
};
|
||||
const onBlur = () => {
|
||||
payload.updateData(index, id, value);
|
||||
};
|
||||
React.useEffect(() => {
|
||||
setValue(initialValue);
|
||||
}, [initialValue]);
|
||||
|
||||
const error = payload.errors?.[index]?.[id];
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
// intent={error ? Intent.DANGER : null}
|
||||
className={classNames(Classes.FILL)}
|
||||
>
|
||||
<Checkbox
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
minimal={true}
|
||||
className="ml2"
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxEditableCell;
|
||||
@@ -6,6 +6,7 @@ import ItemsListCell from './ItemsListCell';
|
||||
import PercentFieldCell from './PercentFieldCell';
|
||||
import { DivFieldCell, EmptyDiv } from './DivFieldCell';
|
||||
import NumericInputCell from './NumericInputCell';
|
||||
import CheckBoxFieldCell from './CheckBoxFieldCell'
|
||||
|
||||
export {
|
||||
AccountsListFieldCell,
|
||||
@@ -16,5 +17,6 @@ export {
|
||||
PercentFieldCell,
|
||||
DivFieldCell,
|
||||
EmptyDiv,
|
||||
NumericInputCell
|
||||
NumericInputCell,
|
||||
CheckBoxFieldCell
|
||||
};
|
||||
|
||||
@@ -4,14 +4,26 @@ import { CLASSES } from 'common/classes';
|
||||
import { DataTable, If } from 'components';
|
||||
import 'style/components/DataTable/DataTableEditable.scss';
|
||||
|
||||
/**
|
||||
* Editable datatable.
|
||||
*/
|
||||
export default function DatatableEditable({
|
||||
totalRow = false,
|
||||
actions,
|
||||
name,
|
||||
className,
|
||||
...tableProps
|
||||
}) {
|
||||
return (
|
||||
<div className={classNames(CLASSES.DATATABLE_EDITOR, className)}>
|
||||
<div
|
||||
className={classNames(
|
||||
CLASSES.DATATABLE_EDITOR,
|
||||
{
|
||||
[`${CLASSES.DATATABLE_EDITOR}--${name}`]: name,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<DataTable {...tableProps} />
|
||||
|
||||
<If condition={actions}>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useContext } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { If } from 'components';
|
||||
import { Skeleton } from 'components';
|
||||
import { useAppIntlContext } from 'components/AppIntlProvider';
|
||||
import TableContext from './TableContext';
|
||||
import { isCellLoading } from './utils';
|
||||
|
||||
@@ -26,6 +27,9 @@ export default function TableCell({
|
||||
const isExpandColumn = expandToggleColumn === index;
|
||||
const { skeletonWidthMax = 100, skeletonWidthMin = 40 } = {};
|
||||
|
||||
// Application intl context.
|
||||
const { isRTL } = useAppIntlContext();
|
||||
|
||||
// Detarmines whether the current cell is loading.
|
||||
const cellLoading = isCellLoading(
|
||||
cellsLoading,
|
||||
@@ -46,8 +50,6 @@ export default function TableCell({
|
||||
);
|
||||
}
|
||||
|
||||
const isRTL = true;
|
||||
|
||||
return (
|
||||
<div
|
||||
{...cell.getCellProps({
|
||||
|
||||
29
client/src/components/Details/index.js
Normal file
29
client/src/components/Details/index.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import className from 'classname';
|
||||
|
||||
/**
|
||||
* Details menu.
|
||||
*/
|
||||
export function DetailsMenu({ children, vertical = false }) {
|
||||
return (
|
||||
<div
|
||||
className={className('details-menu', {
|
||||
'is-vertical': vertical,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail item.
|
||||
*/
|
||||
export function DetailItem({ label, children }) {
|
||||
return (
|
||||
<div class="detail-item">
|
||||
<div class="detail-item__label">{label}</div>
|
||||
<div class="detail-item__content">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import KeyboardShortcutsDialog from 'containers/Dialogs/keyboardShortcutsDialog'
|
||||
import ContactDuplicateDialog from 'containers/Dialogs/ContactDuplicateDialog';
|
||||
import QuickPaymentReceiveFormDialog from 'containers/Dialogs/QuickPaymentReceiveFormDialog';
|
||||
import QuickPaymentMadeFormDialog from 'containers/Dialogs/QuickPaymentMadeFormDialog';
|
||||
import AllocateLandedCostDialog from 'containers/Dialogs/AllocateLandedCostDialog';
|
||||
|
||||
/**
|
||||
* Dialogs container.
|
||||
@@ -32,6 +33,7 @@ export default function DialogsContainer() {
|
||||
<ContactDuplicateDialog dialogName={'contact-duplicate'} />
|
||||
<QuickPaymentReceiveFormDialog dialogName={'quick-payment-receive'} />
|
||||
<QuickPaymentMadeFormDialog dialogName={'quick-payment-made'} />
|
||||
<AllocateLandedCostDialog dialogName={'allocate-landed-cost'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import React from 'react';
|
||||
import { Position, Drawer } from '@blueprintjs/core';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import 'style/components/Drawer.scss';
|
||||
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Drawer component.
|
||||
*/
|
||||
function DrawerComponent(props) {
|
||||
const { name, children, onClose, closeDrawer } = props;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import PaymentReceiveDrawer from 'containers/Sales/PaymentReceives/PaymentDetail
|
||||
import AccountDrawer from 'containers/Drawers/AccountDrawer';
|
||||
import ManualJournalDrawer from 'containers/Drawers/ManualJournalDrawer';
|
||||
import ExpenseDrawer from 'containers/Drawers/ExpenseDrawer';
|
||||
import BillDrawer from 'containers/Drawers/BillDrawer';
|
||||
|
||||
export default function DrawersContainer() {
|
||||
return (
|
||||
@@ -17,6 +18,7 @@ export default function DrawersContainer() {
|
||||
<AccountDrawer name={'account-drawer'} />
|
||||
<ManualJournalDrawer name={'journal-drawer'} />
|
||||
<ExpenseDrawer name={'expense-drawer'} />
|
||||
<BillDrawer name={'bill-drawer'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import React from 'react';
|
||||
import className from 'classnames';
|
||||
import 'style/containers/FinancialStatements/FinancialSheet.scss';
|
||||
|
||||
export default function FinancialStatements({ children }) {
|
||||
return <div class="financial-statement">{children}</div>;
|
||||
export default function FinancialStatements({ name, children }) {
|
||||
return (
|
||||
<div
|
||||
className={className('financial-statement', {
|
||||
[`financial-statement--${name}`]: name,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
77
client/src/components/Items/ItemsMultiSelect.js
Normal file
77
client/src/components/Items/ItemsMultiSelect.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { MenuItem, Button } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import MultiSelect from 'components/MultiSelect';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { safeInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Items multi-select.
|
||||
*/
|
||||
export function ItemsMultiSelect({
|
||||
items,
|
||||
defaultText = <T id={'All items'} />,
|
||||
buttonProps,
|
||||
|
||||
selectedItems = [],
|
||||
onItemSelect,
|
||||
...multiSelectProps
|
||||
}) {
|
||||
const [localSelected, setLocalSelected] = useState([...selectedItems]);
|
||||
|
||||
// Detarmines the given id is selected.
|
||||
const isItemSelected = useCallback(
|
||||
(id) => localSelected.some((s) => s === id),
|
||||
[localSelected],
|
||||
);
|
||||
|
||||
// Contact item renderer.
|
||||
const itemRenderer = useCallback(
|
||||
(item, { handleClick }) => (
|
||||
<MenuItem
|
||||
icon={isItemSelected(item.id) ? 'tick' : 'blank'}
|
||||
text={item.name}
|
||||
key={item.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
),
|
||||
[isItemSelected],
|
||||
);
|
||||
|
||||
// Count selected items.
|
||||
const countSelected = localSelected.length;
|
||||
|
||||
// Handle item selected.
|
||||
const handleItemSelect = useCallback(
|
||||
({ id }) => {
|
||||
const selected = isItemSelected(id)
|
||||
? localSelected.filter((s) => s !== id)
|
||||
: [...localSelected, id];
|
||||
|
||||
setLocalSelected([...selected]);
|
||||
safeInvoke(onItemSelect, selected);
|
||||
},
|
||||
[setLocalSelected, localSelected, isItemSelected, onItemSelect],
|
||||
);
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
items={items}
|
||||
noResults={<MenuItem disabled={true} text={<T id={'No items'} />} />}
|
||||
itemRenderer={itemRenderer}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={true}
|
||||
onItemSelect={handleItemSelect}
|
||||
{...multiSelectProps}
|
||||
>
|
||||
<Button
|
||||
text={
|
||||
countSelected === 0
|
||||
? defaultText
|
||||
: intl.get('Selected items ({count})', { count: countSelected })
|
||||
}
|
||||
{...buttonProps}
|
||||
/>
|
||||
</MultiSelect>
|
||||
);
|
||||
}
|
||||
1
client/src/components/Items/index.js
Normal file
1
client/src/components/Items/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export * from './ItemsMultiSelect';
|
||||
@@ -13,18 +13,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from "classnames";
|
||||
import * as React from "react";
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Classes,
|
||||
DISPLAYNAME_PREFIX,
|
||||
Popover,
|
||||
Position,
|
||||
Utils,
|
||||
} from "@blueprintjs/core";
|
||||
import {
|
||||
QueryList,
|
||||
} from '@blueprintjs/select';
|
||||
} from '@blueprintjs/core';
|
||||
import { QueryList } from '@blueprintjs/select';
|
||||
|
||||
// export interface IMultiSelectProps<T> extends IListItemsProps<T> {
|
||||
// /**
|
||||
@@ -63,114 +61,124 @@ import {
|
||||
// }
|
||||
|
||||
export default class MultiSelect extends React.Component {
|
||||
static get displayName() {
|
||||
return `${DISPLAYNAME_PREFIX}.MultiSelect`;
|
||||
}
|
||||
static get displayName() {
|
||||
return `${DISPLAYNAME_PREFIX}.MultiSelect`;
|
||||
}
|
||||
|
||||
static get defaultProps() {
|
||||
return {
|
||||
fill: false,
|
||||
placeholder: "Search...",
|
||||
};
|
||||
static get defaultProps() {
|
||||
return {
|
||||
fill: false,
|
||||
placeholder: 'Search...',
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { isOpen: (this.props.popoverProps && this.props.popoverProps.isOpen) || false };
|
||||
this.input = null;
|
||||
this.queryList = null;
|
||||
this.listRef = React.createRef();
|
||||
this.state = {
|
||||
isOpen:
|
||||
(this.props.popoverProps && this.props.popoverProps.isOpen) || false,
|
||||
};
|
||||
this.input = null;
|
||||
this.queryList = null;
|
||||
this.listRef = React.createRef();
|
||||
|
||||
this.refHandlers = {
|
||||
queryList: (ref) => {
|
||||
this.queryList = ref;
|
||||
},
|
||||
};
|
||||
this.refHandlers = {
|
||||
queryList: (ref) => {
|
||||
this.queryList = ref;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
// omit props specific to this component, spread the rest.
|
||||
const { openOnKeyDown, popoverProps, tagInputProps, ...restProps } =
|
||||
this.props;
|
||||
|
||||
return (
|
||||
<QueryList
|
||||
{...restProps}
|
||||
onItemSelect={this.handleItemSelect.bind(this)}
|
||||
onQueryChange={this.handleQueryChange.bind(this)}
|
||||
ref={this.refHandlers.queryList}
|
||||
renderer={this.renderQueryList.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderQueryList(listProps) {
|
||||
const { fill, tagInputProps = {}, popoverProps = {} } = this.props;
|
||||
const { handleKeyDown, handleKeyUp } = listProps;
|
||||
|
||||
if (fill) {
|
||||
popoverProps.fill = true;
|
||||
tagInputProps.fill = true;
|
||||
}
|
||||
|
||||
render() {
|
||||
// omit props specific to this component, spread the rest.
|
||||
const { openOnKeyDown, popoverProps, tagInputProps, ...restProps } = this.props;
|
||||
|
||||
return (
|
||||
<QueryList
|
||||
{...restProps}
|
||||
onItemSelect={this.handleItemSelect.bind(this)}
|
||||
onQueryChange={this.handleQueryChange.bind(this)}
|
||||
ref={this.refHandlers.queryList}
|
||||
renderer={this.renderQueryList.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
// add our own inputProps.className so that we can reference it in event handlers
|
||||
const { inputProps = {} } = tagInputProps;
|
||||
inputProps.className = classNames(
|
||||
inputProps.className,
|
||||
Classes.MULTISELECT_TAG_INPUT_INPUT,
|
||||
);
|
||||
|
||||
renderQueryList(listProps) {
|
||||
const { fill, tagInputProps = {}, popoverProps = {} } = this.props;
|
||||
const { handleKeyDown, handleKeyUp } = listProps;
|
||||
|
||||
if (fill) {
|
||||
popoverProps.fill = true;
|
||||
tagInputProps.fill = true;
|
||||
}
|
||||
|
||||
// add our own inputProps.className so that we can reference it in event handlers
|
||||
const { inputProps = {} } = tagInputProps;
|
||||
inputProps.className = classNames(inputProps.className, Classes.MULTISELECT_TAG_INPUT_INPUT);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
autoFocus={false}
|
||||
canEscapeKeyClose={true}
|
||||
enforceFocus={false}
|
||||
isOpen={this.state.isOpen}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
{...popoverProps}
|
||||
className={classNames(listProps.className, popoverProps.className)}
|
||||
onInteraction={this.handlePopoverInteraction.bind(this)}
|
||||
popoverClassName={classNames(Classes.MULTISELECT_POPOVER, popoverProps.popoverClassName)}
|
||||
onOpened={this.handlePopoverOpened.bind(this)}
|
||||
return (
|
||||
<Popover
|
||||
autoFocus={false}
|
||||
canEscapeKeyClose={true}
|
||||
enforceFocus={false}
|
||||
isOpen={this.state.isOpen}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
{...popoverProps}
|
||||
className={classNames(listProps.className, popoverProps.className)}
|
||||
onInteraction={this.handlePopoverInteraction.bind(this)}
|
||||
popoverClassName={classNames(
|
||||
Classes.MULTISELECT_POPOVER,
|
||||
popoverProps.popoverClassName,
|
||||
)}
|
||||
onOpened={this.handlePopoverOpened.bind(this)}
|
||||
>
|
||||
<div
|
||||
onKeyDown={
|
||||
this.state.isOpen ? handleKeyDown : this.handleTargetKeyDown
|
||||
}
|
||||
onKeyUp={this.state.isOpen ? handleKeyUp : undefined}
|
||||
>
|
||||
<div
|
||||
onKeyDown={this.state.isOpen ? handleKeyDown : this.handleTargetKeyDown}
|
||||
onKeyUp={this.state.isOpen ? handleKeyUp : undefined}
|
||||
>
|
||||
{this.props.children}
|
||||
</div>
|
||||
<div onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} ref={this.listRef}>
|
||||
{listProps.itemList}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
{this.props.children}
|
||||
</div>
|
||||
<div onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} ref={this.listRef}>
|
||||
{listProps.itemList}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
handleItemSelect(item, evt) {
|
||||
if (this.input != null) {
|
||||
this.input.focus();
|
||||
}
|
||||
Utils.safeInvoke(this.props.onItemSelect, item, evt);
|
||||
};
|
||||
|
||||
handleQueryChange(query, evt) {
|
||||
this.setState({ isOpen: query.length > 0 || !this.props.openOnKeyDown });
|
||||
Utils.safeInvoke(this.props.onQueryChange, query, evt);
|
||||
};
|
||||
|
||||
handlePopoverInteraction = (isOpen, e) => {
|
||||
if (e && this.listRef.current && this.listRef.current.contains(e.target)) {
|
||||
this.setState({ isOpen: true })
|
||||
} else {
|
||||
this.setState({ isOpen });
|
||||
}
|
||||
Utils.safeInvokeMember(this.props.popoverProps, "onInteraction", isOpen);
|
||||
handleItemSelect(item, evt) {
|
||||
if (this.input != null) {
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
Utils.safeInvoke(this.props.onItemSelect, item, evt);
|
||||
}
|
||||
|
||||
handlePopoverOpened(node) {
|
||||
if (this.queryList != null) {
|
||||
// scroll active item into view after popover transition completes and all dimensions are stable.
|
||||
this.queryList.scrollActiveItemIntoView();
|
||||
}
|
||||
Utils.safeInvokeMember(this.props.popoverProps, "onOpened", node);
|
||||
};
|
||||
handleQueryChange(query, evt) {
|
||||
this.setState({ isOpen: query.length > 0 || !this.props.openOnKeyDown });
|
||||
Utils.safeInvoke(this.props.onQueryChange, query, evt);
|
||||
}
|
||||
|
||||
handlePopoverInteraction = (isOpen, e) => {
|
||||
if (e && this.listRef.current && this.listRef.current.contains(e.target)) {
|
||||
this.setState({ isOpen: true });
|
||||
} else {
|
||||
this.setState({ isOpen });
|
||||
}
|
||||
Utils.safeInvokeMember(this.props.popoverProps, 'onInteraction', isOpen);
|
||||
};
|
||||
|
||||
handlePopoverOpened(node) {
|
||||
if (this.queryList != null) {
|
||||
// scroll active item into view after popover transition completes and all dimensions are stable.
|
||||
this.queryList.scrollActiveItemIntoView();
|
||||
}
|
||||
Utils.safeInvokeMember(this.props.popoverProps, 'onOpened', node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@ import DrawerHeaderContent from './Drawer/DrawerHeaderContent';
|
||||
import Postbox from './Postbox';
|
||||
import AccountsSuggestField from './AccountsSuggestField';
|
||||
import MaterialProgressBar from './MaterialProgressBar';
|
||||
import { MoneyFieldCell } from './DataTableCells';
|
||||
import Card from './Card';
|
||||
|
||||
import { ItemsMultiSelect } from './Items';
|
||||
|
||||
const Hint = FieldHint;
|
||||
|
||||
@@ -123,4 +127,7 @@ export {
|
||||
Postbox,
|
||||
AccountsSuggestField,
|
||||
MaterialProgressBar,
|
||||
MoneyFieldCell,
|
||||
ItemsMultiSelect,
|
||||
Card
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user