UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

191 lines (190 loc) 11 kB
import * as React from 'react'; import { useState, useRef } from 'react'; import { Flex, Box, Text } from 'rebass'; import Dialog from '../../components/Dialog'; import SimpleButton from '../../components/SimpleButton'; import useProperty from '../../components/utils/useProperty'; import { isMacLike } from '../../Utilities/isMacLike'; import { KeyHint } from '../KeyHint'; import { Icon } from '../../components/icons'; import { useResizeObserver } from '../../components/ResizeObserver'; import { NamedQueryContext, } from '../../components/ExpressionEditor/NamedQueryContext'; import { useKeyboardNavigation } from './useKeyboardNavigation'; export const SummaryTag = (props) => (React.createElement(Text, { ...props })); export const SummaryText = (props) => (React.createElement(Text, { fontSize: 2, mb: 3, ...props })); export const FormDescriptionText = (props) => React.createElement(Text, { fontSize: 2, mt: 1, ...props }); export const OnePageWizardContext = React.createContext({ data: null, sections: [], setCurrentSection: (index) => { }, }); export function useOnePageWizardContext() { return React.useContext(OnePageWizardContext); } export const OnePageWizard = (props) => { let defaultCurrentIndex = 0; const [currentSection, setCurrentSection] = useProperty(props, 'currentIndex', defaultCurrentIndex, { onChange: (index) => { props.onSectionChange?.(index); }, }); const contextValue = { data: props.data, sections: props.sections, setCurrentSection, }; const visibleSections = React.useMemo(() => { return props.sections .filter(Boolean) .filter((section) => section === '-' || section.isVisible == undefined || section.isVisible(props.data, contextValue)); }, [props.sections]); contextValue.sections = visibleSections; if (props.defaultCurrentSectionName) { const candidate = visibleSections.findIndex((section) => section !== '-' && section?.title === props.defaultCurrentSectionName); if (candidate >= 0) { defaultCurrentIndex = candidate; } } const [namedQuery, setNamedQuery] = useState(false); const [navIndexMap] = useState(() => new Map()); const handleClickFinish = () => { props.onFinish?.(props.data); }; const renderSection = (index) => { const section = visibleSections[index]; if (section === '-') { return React.createElement(React.Fragment, { key: index }); } return (React.createElement(Flex, { flexDirection: "column", key: index, "data-name": `section-${index}`, style: { minHeight: '100%' }, mr: 2 }, React.createElement(Box, { py: 3, paddingLeft: 2, fontSize: 4, className: "ab-OnePageWizard__details" }, section.details), React.createElement(Box, { flex: 1, className: "ab-OnePageWizard__section" }, section.render(props.data, index)))); }; const selectedNodeRef = useRef(null); const selectedFeedback = (React.createElement("div", { ref: selectedNodeRef, className: "ab-OnePageWizard__selected-title-overlay", style: { position: 'absolute', background: 'var(--ab-cmp-one-page-wizard-selected-title__background)', pointerEvents: 'none', borderRadius: 'var(--ab__border-radius)', transition: 'top 0.2s', } })); const sizeOwnerRef = useRef(null); const [width, setWidth] = useState(0); useResizeObserver(sizeOwnerRef, ({ width }) => { setWidth(width); }); React.useEffect(() => { const node = selectedNodeRef.current; const parent = node.parentNode; const activeElement = parent.children[currentSection]; node.style.top = `${activeElement.offsetTop}px`; node.style.left = `${activeElement.offsetLeft}px`; node.style.height = `${activeElement.offsetHeight}px`; node.style.width = `${activeElement.offsetWidth}px`; }, [currentSection, width]); let navIndex = 0; const dialogRef = useRef(null); let invalidCount = 0; let firstErrorMessage = null; const validSectionsMap = visibleSections.reduce((acc, section, index) => { if (section === '-') { acc.set(index, true); return acc; } const valid = section.isValid ? section.isValid(props.data, contextValue) : true; //!active && canFinish !== true; if (valid !== true) { invalidCount++; if (firstErrorMessage == null) { firstErrorMessage = valid; } } acc.set(index, valid); return acc; }, new Map()); useKeyboardNavigation(setCurrentSection, visibleSections); const canFinish = !invalidCount; return (React.createElement(NamedQueryContext.Provider, { value: { namedQuery, setNamedQuery } }, React.createElement(OnePageWizardContext.Provider, { value: contextValue }, React.createElement(Dialog, { modal: true, isOpen: true, showCloseButton: false, focusOnBrowserVisible: true, style: { borderRadius: 'var(--ab__border-radius)', overflow: 'hidden', height: '90vh', }, ref: dialogRef, onDismiss: () => props.onHide?.(), onKeyDown: (event) => { if (event.metaKey || event.ctrlKey) { const { key } = event; if (!isNaN(Number(key))) { const num = Number(key); if (navIndexMap.has(num)) { const index = navIndexMap.get(num); setCurrentSection(index); event.preventDefault(); event.stopPropagation(); requestAnimationFrame(() => { dialogRef?.current?.bringToFront(); }); } } } } }, React.createElement(Flex, { className: "ab-OnePageWizard", flexDirection: "column", "data-name": props.name, style: { height: '100%', width: '90vw', maxWidth: 1200, ...props.style, } }, React.createElement(Flex, { flexDirection: "row", alignItems: "stretch", flex: 1, style: { overflow: 'auto' } }, React.createElement(Flex, { flexDirection: "column", padding: 3, className: "ab-OnePageWizard__section-title-container", ref: sizeOwnerRef, style: { overflow: 'auto', position: 'relative', flex: 'none', ...props.titleContainerStyle, } }, visibleSections.map((section, index) => { if (section === '-') { return (React.createElement(Box, { as: "hr", mt: 2, className: "ab-OnePageWizard__section-separator", key: `${index}-`, style: { width: '100%', border: 'none', borderTop: `1px solid var(--ab-color-inputborder)`, } })); } navIndex++; navIndexMap.set(navIndex, index); const active = index === currentSection; const disabled = false; //!active && !currentSectionValid; return (React.createElement(Flex, { className: "ab-OnePageWizard__section-title", "data-name": section.title, flexDirection: "row", style: { cursor: disabled ? 'auto' : 'pointer', zIndex: 10, transition: 'color 0.2s', opacity: disabled ? 0.5 : 1, }, key: section.title, color: active ? 'var(--ab-cmp-one-page-wizard-section-title__color)' : '', px: 2, py: 1, mt: index ? 2 : 0, onClick: () => { if (disabled) { return; } setCurrentSection(index); } }, React.createElement(KeyHint, { mr: 2, style: { display: 'inline-block' } }, navIndex), React.createElement("div", { style: { flex: 1 } }, section.title), React.createElement(Icon, { name: "error", style: { marginLeft: 'var(--ab-space-2)', visibility: validSectionsMap.get(index) !== true ? 'visible' : 'hidden', } }))); }), selectedFeedback, React.createElement(Box, { flex: 1 }), React.createElement(KeyHint, { style: { lineHeight: 1.5 }, className: "ab-OnePageWizard__key-hint" }, isMacLike() ? 'Cmd' : 'Ctrl', " + #", React.createElement("br", null), "to navigate")), React.createElement(Flex, { flex: 1, flexDirection: "column", className: "ab-OnePageWizard__section-container" }, renderSection(currentSection))), React.createElement(Flex, { flexDirection: "row", padding: 2, alignItems: "center", className: "ab-WizardDialog__footer ab-OnePageWizard__footer" }, React.createElement(SimpleButton, { tone: "neutral", variant: "text", "data-name": "close", onClick: () => props.onHide?.(), tooltip: props.closeTooltip ?? 'Close wizard', accessLevel: 'Full' }, props.closeText ?? 'CLOSE'), React.createElement(KeyHint, { ml: 2 }, "Esc"), React.createElement(Text, { fontSize: 2, mr: 3, className: "ab-OnePageWizard__error", style: { flex: 1, color: 'var(--ab-color-error)', textAlign: 'end', } }, firstErrorMessage), React.createElement(SimpleButton, { tone: "accent", "data-name": "finish", variant: "raised", disabled: canFinish !== true, onClick: () => handleClickFinish(), icon: 'check', accessLevel: 'Full' }, props.finishText ?? 'Finish'))))))); };