@itwin/presentation-components
Version:
React components based on iTwin.js Presentation library
173 lines • 10.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PresentationInstanceFilterDialog = PresentationInstanceFilterDialog;
const jsx_runtime_1 = require("react/jsx-runtime");
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module InstancesFilter
*/
require("./PresentationInstanceFilterDialog.scss");
const react_1 = require("react");
const react_error_boundary_1 = require("react-error-boundary");
const components_react_1 = require("@itwin/components-react");
const itwinui_illustrations_react_1 = require("@itwin/itwinui-illustrations-react");
const itwinui_react_1 = require("@itwin/itwinui-react");
const Utils_js_1 = require("../common/Utils.js");
const InstanceFilterBuilder_js_1 = require("./InstanceFilterBuilder.js");
const PresentationInstanceFilter_js_1 = require("./PresentationInstanceFilter.js");
const Utils_js_2 = require("./Utils.js");
/**
* Dialog component that renders [[InstanceFilterBuilder]] inside.
* @public
*/
function PresentationInstanceFilterDialog(props) {
const { isOpen, title, ...restProps } = props;
return ((0, jsx_runtime_1.jsxs)(itwinui_react_1.Dialog, { className: "presentation-instance-filter-dialog", isOpen: isOpen, onClose: props.onClose, closeOnEsc: false, preventDocumentScroll: true, trapFocus: true, isDraggable: true, isResizable: true, portal: true, children: [(0, jsx_runtime_1.jsx)(itwinui_react_1.Dialog.Backdrop, {}), (0, jsx_runtime_1.jsxs)(itwinui_react_1.Dialog.Main, { className: "presentation-instance-filter-dialog-content-container", children: [(0, jsx_runtime_1.jsx)(itwinui_react_1.Dialog.TitleBar, { className: "presentation-instance-filter-title", titleText: title ? title : (0, Utils_js_1.translate)("instance-filter-builder.filter") }), (0, jsx_runtime_1.jsx)(react_error_boundary_1.ErrorBoundary, { fallback: (0, jsx_runtime_1.jsx)(ErrorState, {}), children: (0, jsx_runtime_1.jsx)(FilterDialogContent, { ...restProps }) })] })] }));
}
function FilterDialogContent({ propertiesSource, ...restProps }) {
const { propertiesSource: loadedPropertiesSource, isLoading } = useDelayLoadedPropertiesSource(propertiesSource);
if (isLoading) {
return (0, jsx_runtime_1.jsx)(DelayedCenteredProgressRadial, {});
}
if (!loadedPropertiesSource) {
return null;
}
return (0, jsx_runtime_1.jsx)(LoadedFilterDialogContent, { ...restProps, descriptor: loadedPropertiesSource.descriptor, descriptorInputKeys: loadedPropertiesSource.inputKeys });
}
function useDelayLoadedPropertiesSource(sourceOrGetter) {
const [{ source, isLoading }, setState] = (0, react_1.useState)(() => typeof sourceOrGetter === "function"
? {
source: undefined,
isLoading: false,
}
: {
source: sourceOrGetter,
isLoading: false,
});
(0, react_1.useEffect)(() => {
let disposed = false;
if (typeof sourceOrGetter !== "function") {
setState({ source: sourceOrGetter, isLoading: false });
return;
}
const updateState = (...params) => {
if (!disposed) {
setState(...params);
}
};
updateState({ source: undefined, isLoading: true });
void (async () => {
try {
const newDescriptor = await sourceOrGetter();
updateState({
source: newDescriptor,
isLoading: false,
});
}
catch (error) {
updateState(() => {
// throw error in setSate callback for it to be caught by ErrorBoundary
throw error;
});
}
})();
return () => {
disposed = true;
};
}, [sourceOrGetter]);
return { propertiesSource: source, isLoading };
}
function LoadedFilterDialogContent(props) {
const { initialFilter, descriptor, imodel, filterResultsCountRenderer, descriptorInputKeys, onApply, onReset, onClose, toolbarButtonsRenderer } = props;
const initialFilterInfo = useInitialFilter(descriptor, initialFilter);
const [initialPropertyFilter] = (0, react_1.useState)(() => {
if (!initialFilterInfo?.filter) {
return undefined;
}
return PresentationInstanceFilter_js_1.PresentationInstanceFilter.toComponentsPropertyFilter(descriptor, initialFilterInfo.filter);
});
const { rootGroup, actions, buildFilter } = (0, components_react_1.usePropertyFilterBuilder)({
initialFilter: initialPropertyFilter,
ruleValidator: Utils_js_2.filterRuleValidator,
});
const filteringProps = (0, InstanceFilterBuilder_js_1.usePresentationInstanceFilteringProps)(descriptor, imodel, initialFilterInfo?.usedClasses);
const getFilterInfo = (0, react_1.useCallback)((options) => {
const filter = buildFilter(options);
if (!filter && filteringProps.selectedClasses.length === 0) {
return undefined;
}
return {
filter: filter ? PresentationInstanceFilter_js_1.PresentationInstanceFilter.fromComponentsPropertyFilter(descriptor, filter) : undefined,
usedClasses: filteringProps.selectedClasses,
};
}, [buildFilter, descriptor, filteringProps.selectedClasses]);
const handleReset = () => {
filteringProps.selectedClasses = [];
filteringProps.onSelectedClassesChanged([]);
actions.removeAllItems();
onReset && onReset();
};
const onSelectedClassesChanged = (classIds) => {
filteringProps.onSelectedClassesChanged(classIds);
actions.removeAllItems();
};
const throwError = useThrowError();
const handleApply = () => {
try {
const result = getFilterInfo();
// do not invoke apply if filter was not built and it is non empty.
if (result?.filter === undefined && (0, Utils_js_2.isFilterNonEmpty)(rootGroup)) {
return;
}
onApply(result);
}
catch (error) {
throwError(error);
}
};
const handleClose = () => {
onClose && onClose();
};
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(itwinui_react_1.Dialog.Content, { className: "presentation-instance-filter-content", children: (0, jsx_runtime_1.jsx)(InstanceFilterBuilder_js_1.InstanceFilterBuilder, { ...filteringProps, onSelectedClassesChanged: onSelectedClassesChanged, rootGroup: rootGroup, actions: actions, imodel: imodel, descriptor: descriptor, descriptorInputKeys: descriptorInputKeys }) }), (0, jsx_runtime_1.jsxs)("div", { className: "presentation-instance-filter-dialog-bottom-container", children: [(0, jsx_runtime_1.jsx)("div", { children: filterResultsCountRenderer ? (0, jsx_runtime_1.jsx)(ResultsRenderer, { buildFilter: getFilterInfo, renderer: filterResultsCountRenderer }) : null }), (0, jsx_runtime_1.jsx)(itwinui_react_1.Dialog.ButtonBar, { className: "presentation-instance-filter-button-bar", children: toolbarButtonsRenderer ? (toolbarButtonsRenderer({ handleApply, handleReset, handleClose })) : ((0, jsx_runtime_1.jsx)(ToolbarButtonsRenderer, { handleApply: handleApply, handleReset: handleReset, handleClose: handleClose })) })] })] }));
}
function useInitialFilter(descriptor, initialFilter) {
const initializedFilter = (0, react_1.useRef)();
if (initializedFilter.current === undefined) {
initializedFilter.current = { filterInfo: typeof initialFilter === "function" ? initialFilter(descriptor) : initialFilter };
}
return initializedFilter.current.filterInfo;
}
function ToolbarButtonsRenderer({ handleApply, handleClose, handleReset }) {
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(itwinui_react_1.Button, { className: "presentation-instance-filter-dialog-apply-button", styleType: "high-visibility", onClick: handleApply, children: (0, Utils_js_1.translate)("instance-filter-builder.apply") }), (0, jsx_runtime_1.jsx)(itwinui_react_1.Button, { className: "presentation-instance-filter-dialog-close-button", onClick: handleClose, children: (0, Utils_js_1.translate)("instance-filter-builder.cancel") }), (0, jsx_runtime_1.jsx)(itwinui_react_1.Button, { className: "presentation-instance-filter-dialog-reset-button", onClick: handleReset, children: (0, Utils_js_1.translate)("instance-filter-builder.reset") })] }));
}
function ResultsRenderer({ buildFilter, renderer }) {
const filter = (0, react_1.useMemo)(() => buildFilter({ ignoreErrors: true }), [buildFilter]);
if (!filter) {
return null;
}
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: renderer(filter) });
}
function DelayedCenteredProgressRadial() {
const show = (0, Utils_js_1.useDelay)();
if (!show) {
return null;
}
return ((0, jsx_runtime_1.jsx)("div", { className: "presentation-instance-filter-dialog-progress", children: (0, jsx_runtime_1.jsx)(itwinui_react_1.ProgressRadial, { indeterminate: true, size: "large" }) }));
}
function ErrorState() {
return ((0, jsx_runtime_1.jsx)("div", { style: { width: "100%", height: "100%", position: "relative", overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)(itwinui_react_1.NonIdealState, { svg: (0, jsx_runtime_1.jsx)(itwinui_illustrations_react_1.SvgError, {}), heading: (0, Utils_js_1.translate)("general.error"), description: (0, Utils_js_1.translate)("general.generic-error-description") }) }));
}
// ErrorBoundary only catches errors that are thrown in React lifecycle methods. For event handlers and
// async function errors can be rethrown from `setState` callback.
function useThrowError() {
const [_, setSate] = (0, react_1.useState)({});
return (error) => {
setSate(() => {
throw error;
});
};
}
//# sourceMappingURL=PresentationInstanceFilterDialog.js.map