UNPKG

@itwin/presentation-components

Version:

React components based on iTwin.js Presentation library

173 lines 10.3 kB
"use strict"; 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