From 1287bc18e8f7d6073c1f905cd3564f257508b563 Mon Sep 17 00:00:00 2001 From: hamed-musallam <35760236+hamed-musallam@users.noreply.github.com> Date: Fri, 19 Jul 2024 13:11:01 +0200 Subject: [PATCH] feat: print spectra (#2786) * feat: print spectra feat: prepare NMRium structure for printing fix: trigger printing after the NMRium viewer is rendered and rescaled to an A4 page * feat: create print content component * refactor: use new print component * fix: print page config * feat: improve print content page options modal * fix: return the window size to origin after the print is finished * chore: fix prettier and eslint * chore: fix prettier and eslint * fix: print padding * feat: improve print * feat: save print page settings in the workspace * fix: type and page padding * fix: print in firefox * refactor: change label from 'margin' to 'Margin' * fix: display overlay div to prevent hover effects close #3139 --- package-lock.json | 10 +- package.json | 4 +- src/component/2d/Viewer2D.tsx | 8 +- src/component/context/ChartContext.tsx | 17 +- src/component/elements/print/PrintContent.tsx | 490 ++++++++++++++++++ .../elements/print/PrintProvider.tsx | 44 ++ src/component/elements/print/index.ts | 2 + src/component/main/InnerNMRiumContents.tsx | 59 ++- src/component/main/NMRiumViewer.tsx | 65 +++ .../actions/changePrintPageSettings.ts | 16 + .../reducer/preferences/preferencesReducer.ts | 11 +- .../workspaces/workspaceDefaultProperties.ts | 1 + src/demo/Sidebar.tsx | 1 + src/demo/index.css | 14 + src/demo/layouts/Admin.tsx | 1 + 15 files changed, 709 insertions(+), 34 deletions(-) create mode 100644 src/component/elements/print/PrintContent.tsx create mode 100644 src/component/elements/print/PrintProvider.tsx create mode 100644 src/component/elements/print/index.ts create mode 100644 src/component/main/NMRiumViewer.tsx create mode 100644 src/component/reducer/preferences/actions/changePrintPageSettings.ts diff --git a/package-lock.json b/package-lock.json index e2776a3c59..5676dbda8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "ml-tree-similarity": "^2.2.0", "multiplet-analysis": "^2.1.2", "nmr-correlation": "^2.3.3", - "nmr-load-save": "^0.34.2", + "nmr-load-save": "^0.35.0", "nmr-processing": "^12.8.0", "nmredata": "^0.9.11", "numeral": "^2.0.6", @@ -9551,9 +9551,9 @@ } }, "node_modules/nmr-load-save": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/nmr-load-save/-/nmr-load-save-0.34.2.tgz", - "integrity": "sha512-8L1NXJ+75HK1AB4ooSu9pKiFBwJmom2OMcoJAsNRFX5W6tX7AUidt47k13xUtAu3LRvqKGXIwAi/9PWW4ZBgpA==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/nmr-load-save/-/nmr-load-save-0.35.0.tgz", + "integrity": "sha512-uur725neK10hdrbyVJYArG4wSUSBT1raXQCgByWvGyirDLyJ330vsOn/ilRv1kKIV4i4twX84aJPZA2Eyh0F6w==", "dependencies": { "@lukeed/uuid": "^2.0.1", "@types/lodash.merge": "^4.6.9", @@ -13820,4 +13820,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index d5f4dc52fa..7ac09272ad 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "ml-tree-similarity": "^2.2.0", "multiplet-analysis": "^2.1.2", "nmr-correlation": "^2.3.3", - "nmr-load-save": "^0.34.2", + "nmr-load-save": "^0.35.0", "nmr-processing": "^12.8.0", "nmredata": "^0.9.11", "numeral": "^2.0.6", @@ -153,4 +153,4 @@ "vite": "^5.3.3", "vitest": "^2.0.2" } -} +} \ No newline at end of file diff --git a/src/component/2d/Viewer2D.tsx b/src/component/2d/Viewer2D.tsx index eb00db674d..b66476d52c 100644 --- a/src/component/2d/Viewer2D.tsx +++ b/src/component/2d/Viewer2D.tsx @@ -15,6 +15,7 @@ import { MouseTracker } from '../EventsTrackers/MouseTracker'; import { useChartData } from '../context/ChartContext'; import { useDispatch } from '../context/DispatchContext'; import { useMapKeyModifiers } from '../context/KeyModifierContext'; +import { usePrintPage } from '../elements/print'; import Spinner from '../loader/Spinner'; import { options } from '../toolbar/ToolTypes'; @@ -284,10 +285,13 @@ interface ViewerResponsiveWrapperProps { export function ViewerResponsiveWrapper(props: ViewerResponsiveWrapperProps) { const dispatch = useDispatch(); const { width, height, children } = props; + const printOptions = usePrintPage(); useEffect(() => { - dispatch({ type: 'SET_DIMENSIONS', payload: { width, height } }); - }, [width, height, dispatch]); + if (!printOptions) { + dispatch({ type: 'SET_DIMENSIONS', payload: { width, height } }); + } + }, [width, height, dispatch, printOptions]); return children; } diff --git a/src/component/context/ChartContext.tsx b/src/component/context/ChartContext.tsx index 7592fe7de5..8630bf7171 100644 --- a/src/component/context/ChartContext.tsx +++ b/src/component/context/ChartContext.tsx @@ -1,10 +1,25 @@ +import { produce } from 'immer'; import { createContext, useContext } from 'react'; +import { usePrintPage } from '../elements/print'; import { initialState, State } from '../reducer/Reducer'; export const ChartContext = createContext(initialState); export const ChartDataProvider = ChartContext.Provider; export function useChartData() { - return useContext(ChartContext); + const data = useContext(ChartContext); + + const printOptions = usePrintPage(); + + if (!printOptions) return data; + + const { width, height } = printOptions; + + const updatedData = produce(data, (draft) => { + draft.width = width; + draft.height = height; + }); + + return updatedData; } diff --git a/src/component/elements/print/PrintContent.tsx b/src/component/elements/print/PrintContent.tsx new file mode 100644 index 0000000000..27fcdcd016 --- /dev/null +++ b/src/component/elements/print/PrintContent.tsx @@ -0,0 +1,490 @@ +/** @jsxImportSource @emotion/react */ +import { + Dialog, + DialogBody, + DialogFooter, + Radio, + RadioGroup, + Tag, +} from '@blueprintjs/core'; +import { css } from '@emotion/react'; +import { PageSizeName, PrintPageOptions } from 'nmr-load-save'; +import { + CSSProperties, + ReactNode, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; +import { createPortal } from 'react-dom'; +import { Controller, useForm } from 'react-hook-form'; + +import ActionButtons from '../ActionButtons'; +import Label, { LabelStyle } from '../Label'; +import { NumberInput2Controller } from '../NumberInput2Controller'; +import { Select2Controller } from '../Select2Controller'; + +import { PrintProvider } from './PrintProvider'; + +const isFirefox = navigator.userAgent.toLowerCase().includes('firefox'); + +type Layout = 'portrait' | 'landscape'; + +interface BasePrintProps { + onPrint: (options: PrintPageOptions) => void; + defaultPrintPageOptions: Partial; +} +interface InnerPrintFrameProps { + children: ReactNode; + onAfterPrint?: () => void; + onBeforePrint?: () => void; + printPageOptions?: Partial; +} +interface PrintFrameProps + extends InnerPrintFrameProps, + Partial {} + +interface PageSize { + name: PageSizeName; + portrait: { + width: number; + height: number; + }; + landscape: { + width: number; + height: number; + }; +} + +const pageSizes: PageSize[] = [ + { + name: 'Letter', + portrait: { width: 21.59, height: 27.94 }, + landscape: { width: 27.94, height: 21.59 }, + }, + { + name: 'Legal', + portrait: { width: 21.59, height: 35.56 }, + landscape: { width: 35.56, height: 21.59 }, + }, + { + name: 'Tabloid', + portrait: { width: 27.94, height: 43.18 }, + landscape: { width: 43.18, height: 27.94 }, + }, + { + name: 'Executive', + portrait: { width: 26.67, height: 18.42 }, + landscape: { width: 18.42, height: 26.67 }, + }, + { + name: 'Statement', + portrait: { width: 21.59, height: 13.97 }, + landscape: { width: 13.97, height: 21.59 }, + }, + { + name: 'Folio', + portrait: { width: 33.02, height: 21.59 }, + landscape: { width: 21.59, height: 33.02 }, + }, + { + name: 'A3', + portrait: { width: 29.7, height: 42 }, + landscape: { width: 42, height: 29.7 }, + }, + { + name: 'A4', + portrait: { width: 21, height: 29.7 }, + landscape: { width: 29.7, height: 21 }, + }, + { + name: 'A5', + portrait: { width: 14.8, height: 21 }, + landscape: { width: 21, height: 14.8 }, + }, + { + name: 'B4', + portrait: { width: 25.7, height: 36.4 }, + landscape: { width: 36.4, height: 25.7 }, + }, + { + name: 'B5', + portrait: { width: 18.2, height: 25.7 }, + landscape: { width: 25.7, height: 18.2 }, + }, +]; + +function getSizesList(layout: Layout) { + const output: Array<{ label: string; value: string }> = []; + + for (const item of pageSizes) { + const { name, ...otherKeys } = item; + const { width, height } = otherKeys[layout]; + output.push({ label: `${name} (${width} cm x ${height} cm)`, value: name }); + } + + return output; +} + +export function PrintContent(props: PrintFrameProps) { + const [isPageOptionModalOpened, togglePageOptionDialog] = + useState(false); + const [pageOptions, setPageOptions] = + useState | null>(); + + const { + onBeforePrint, + onAfterPrint, + children, + printPageOptions, + defaultPrintPageOptions, + onPrint, + } = props; + + useEffect(() => { + function handleKeyDow(event: KeyboardEvent) { + if ((event.ctrlKey || event.metaKey) && event.key === 'p') { + event.preventDefault(); + if (!printPageOptions) { + togglePageOptionDialog(true); + } else { + setPageOptions(printPageOptions); + } + } + } + + window.addEventListener('keydown', handleKeyDow); + + return () => { + window.removeEventListener('keydown', handleKeyDow); + }; + }, [printPageOptions]); + + if (!pageOptions) { + return ( + { + togglePageOptionDialog(false); + }} + onPrint={(options) => { + togglePageOptionDialog(false); + onPrint?.(options); + setPageOptions(options); + }} + defaultPrintPageOptions={defaultPrintPageOptions || {}} + /> + ); + } + return ( + <> +
+ { + setPageOptions(null); + onAfterPrint?.(); + }} + onBeforePrint={() => { + onBeforePrint?.(); + }} + > + {children} + + + ); +} + +export function InnerPrintFrame(props: InnerPrintFrameProps) { + const { + children, + onAfterPrint, + onBeforePrint, + printPageOptions = {}, + } = props; + + const { + size = 'A4', + margin = 0, + layout = 'landscape', + } = printPageOptions || {}; + + const frameRef = useRef(null); + const [content, setContent] = useState(); + const { width = 0, height = 0 } = + pageSizes.find((pageItem) => pageItem.name === size)?.[layout] || {}; + + const handleAfterPrint = useCallback(() => { + onAfterPrint?.(); + }, [onAfterPrint]); + const handleBeforePrint = useCallback(() => { + onBeforePrint?.(); + }, [onBeforePrint]); + + const load = useCallback(() => { + const contentWindow = frameRef.current?.contentWindow; + if (!contentWindow) return; + const document = contentWindow.document; + + setContent(document.body); + + transferStyles(document); + appendPrintPageStyle(document, { size, layout, margin }); + + contentWindow.addEventListener('afterprint', handleAfterPrint); + contentWindow.addEventListener('beforeprint', handleBeforePrint); + + return contentWindow; + }, [handleAfterPrint, handleBeforePrint, layout, margin, size]); + + useEffect(() => { + const contentWindow = frameRef.current?.contentWindow; + + if (!isFirefox) { + load(); + } + + return () => { + if (!contentWindow) return; + + contentWindow.removeEventListener('afterprint', handleAfterPrint); + contentWindow.removeEventListener('beforeprint', handleBeforePrint); + }; + }, [ + onBeforePrint, + onAfterPrint, + handleBeforePrint, + handleAfterPrint, + size, + layout, + margin, + load, + ]); + + return ( + + + + ); +} + +function RenderContainer(props: { + onRenderComplete: () => void; + children: ReactNode; + style?: CSSProperties; +}) { + const { onRenderComplete, style, children } = props; + + useEffect(() => { + const handleRenderComplete = () => { + setTimeout(() => { + onRenderComplete(); + }, 250); + }; + + const animationFrameId = requestAnimationFrame(handleRenderComplete); + + return () => { + cancelAnimationFrame(animationFrameId); + }; + }, [onRenderComplete]); + + return
{children}
; +} + +interface Style extends Pick { + layout?: 'portrait' | 'landscape'; + size?: string; +} +function appendPrintPageStyle(document: Document, style: Style = {}) { + const { layout = 'landscape', size = 'A4' } = style; + const styleElement = document.createElement('style'); + styleElement.textContent = ` + @media print { + + @page { + size: ${size} ${layout}; + padding:0; + margin:0; + } +} + `; + document.head.append(styleElement); +} + +function transferStyles(targetDocument: Document) { + // Copy the style from the main page and inject it inside the iframe + const styleSheets = Array.from(document.styleSheets); + const targetHead = targetDocument.head; + + for (const styleSheet of styleSheets) { + try { + if (styleSheet.cssRules) { + const newStyleEl = document.createElement('style'); + const cssRules = Array.from(styleSheet.cssRules); + const cssText = cssRules.map((rule) => rule.cssText).join('\n'); + + newStyleEl.append(document.createTextNode(cssText)); + targetHead.append(newStyleEl); + } + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error transferring styles:', error); + } + } +} + +interface InnerPrintOptionsModalProps extends BasePrintProps { + onCloseDialog: () => void; +} +interface PrintOptionsModalProps extends InnerPrintOptionsModalProps { + isOpen: boolean; +} + +function PrintPageOptionsModal(props: PrintOptionsModalProps) { + const { isOpen, ...otherProps } = props; + + if (!isOpen) return; + + return ; +} + +const labelStyle: LabelStyle = { + label: { + flex: 4, + color: '#232323', + }, + wrapper: { + flex: 8, + display: 'flex', + justifyContent: 'flex-start', + }, + container: { margin: '5px 0' }, +}; + +interface PrintPagOptions { + size: PageSizeName; + layout: Layout; + margin: number; +} + +const INITIAL_VALUE: PrintPagOptions = { + size: 'A4', + layout: 'landscape', + margin: 0, +}; + +function InnerPrintOptionsModal(props: InnerPrintOptionsModalProps) { + const { onCloseDialog, onPrint, defaultPrintPageOptions } = props; + + function submitHandler(values) { + onPrint(values); + onCloseDialog?.(); + } + + const { handleSubmit, control, watch } = useForm({ + defaultValues: { ...INITIAL_VALUE, ...defaultPrintPageOptions }, + }); + + const layout = watch('layout'); + const sizesList = getSizesList(layout); + return ( + + + + + + + + + { + void handleSubmit(submitHandler)(); + }} + doneLabel="Print" + onCancel={() => onCloseDialog?.()} + /> + + + ); +} diff --git a/src/component/elements/print/PrintProvider.tsx b/src/component/elements/print/PrintProvider.tsx new file mode 100644 index 0000000000..f6fd115094 --- /dev/null +++ b/src/component/elements/print/PrintProvider.tsx @@ -0,0 +1,44 @@ +import { createContext, ReactNode, useContext, useMemo } from 'react'; + +interface PrintPagContextProps { + width: number; + height: number; +} + +const PrintContext = createContext(null); + +export function usePrintPage() { + return useContext(PrintContext); +} + +interface PrintProviderProps extends PrintPagContextProps { + children: ReactNode; + margin: number; +} +/** + * Converts centimetre to pixels. + * @param cm - The value in centimetre. + * @param ppi - Pixels per inch (default is 96). + * @returns The value in pixels. + */ +function cmToPx(cm: number, margin: number, ppi = 96) { + const inches = (cm - margin * 2) / 2.54; + return Math.round(inches * ppi); +} + +export function PrintProvider(props: PrintProviderProps) { + const { children, width, height, margin } = props; + + const state = useMemo(() => { + const w = cmToPx(width, margin); + const h = cmToPx(height, margin); + return { + width: w, + height: h, + }; + }, [height, margin, width]); + + return ( + {children} + ); +} diff --git a/src/component/elements/print/index.ts b/src/component/elements/print/index.ts new file mode 100644 index 0000000000..ee12b2f5c1 --- /dev/null +++ b/src/component/elements/print/index.ts @@ -0,0 +1,2 @@ +export * from './PrintContent'; +export * from './PrintProvider'; diff --git a/src/component/main/InnerNMRiumContents.tsx b/src/component/main/InnerNMRiumContents.tsx index be0d5b7a3d..35e019818c 100644 --- a/src/component/main/InnerNMRiumContents.tsx +++ b/src/component/main/InnerNMRiumContents.tsx @@ -1,20 +1,20 @@ /** @jsxImportSource @emotion/react */ import { Global, css } from '@emotion/react'; -import { MouseEvent, RefObject, useCallback } from 'react'; +import { PrintPageOptions } from 'nmr-load-save'; +import { MouseEvent, ReactNode, RefObject, useCallback } from 'react'; import { useFullscreen } from 'react-science/ui'; import checkModifierKeyActivated from '../../data/utilities/checkModifierKeyActivated'; -import Viewer1D from '../1d/Viewer1D'; -import FloatMoleculeStructures from '../1d-2d/components/FloatMoleculeStructures'; -import Viewer2D from '../2d/Viewer2D'; import KeysListenerTracker from '../EventsTrackers/KeysListenerTracker'; -import { useChartData } from '../context/ChartContext'; +import { usePreferences } from '../context/PreferencesContext'; +import { PrintContent } from '../elements/print/PrintContent'; import Header from '../header/Header'; import DropZone from '../loader/DropZone'; import Panels from '../panels/Panels'; import ToolBar from '../toolbar/ToolBar'; +import { NMRiumViewer } from './NMRiumViewer'; import { SplitPaneWrapper } from './SplitPaneWrapper'; import { StateError } from './StateError'; @@ -81,7 +81,6 @@ export function InnerNMRiumContents(props: InnerNMRiumContentsProps) { const { emptyText, mainDivRef, elementsWrapperRef, rootRef, viewerRef } = props; - const { displayerMode } = useChartData(); const { isFullScreen } = useFullscreen(); const preventContextMenuHandler = useCallback( @@ -134,23 +133,8 @@ export function InnerNMRiumContents(props: InnerNMRiumContentsProps) {
-
- - {displayerMode === '1D' ? ( - - ) : ( - - )} -
+ +
@@ -172,7 +156,36 @@ export function InnerNMRiumContents(props: InnerNMRiumContentsProps) {
+
+ + +
); } + +interface PrintWrapperProps { + children: ReactNode; +} + +function PrintWrapper(props: PrintWrapperProps) { + const { children } = props; + const { + current: { printPageOptions }, + dispatch, + } = usePreferences(); + + function handleSavePrintOptions(options: PrintPageOptions) { + dispatch({ type: 'CHANGE_PRINT_PAGE_SETTINGS', payload: options }); + } + + return ( + + {children} + + ); +} diff --git a/src/component/main/NMRiumViewer.tsx b/src/component/main/NMRiumViewer.tsx new file mode 100644 index 0000000000..1cc3a074ad --- /dev/null +++ b/src/component/main/NMRiumViewer.tsx @@ -0,0 +1,65 @@ +import { CSSProperties, RefObject, useDeferredValue, useEffect } from 'react'; + +import Viewer1D from '../1d/Viewer1D'; +import FloatMoleculeStructures from '../1d-2d/components/FloatMoleculeStructures'; +import Viewer2D from '../2d/Viewer2D'; +import { useChartData } from '../context/ChartContext'; + +import { NMRiumProps } from './NMRium'; + +interface NMRiumViewerProps { + emptyText: NMRiumProps['emptyText']; + viewerRef: RefObject; + style?: CSSProperties; + onRender?: () => void; +} + +export function NMRiumViewer(props: NMRiumViewerProps) { + const { emptyText, viewerRef, onRender, style = {} } = props; + const { displayerMode, width, height } = useChartData(); + const renderDimension = useDeferredValue({ width, height }); + useEffect(() => { + function handleRenderComplete() { + if (typeof onRender !== 'function') { + return; + } + + setTimeout(() => { + if ( + renderDimension.width !== width || + renderDimension.height !== height + ) { + onRender(); + } + }, 0); + } + + const animationFrameId = requestAnimationFrame(handleRenderComplete); + + return () => { + cancelAnimationFrame(animationFrameId); + }; + }, [onRender, width, height, renderDimension.width, renderDimension.height]); + + return ( +
+ + {displayerMode === '1D' ? ( + + ) : ( + + )} +
+ ); +} diff --git a/src/component/reducer/preferences/actions/changePrintPageSettings.ts b/src/component/reducer/preferences/actions/changePrintPageSettings.ts new file mode 100644 index 0000000000..4b231ff8de --- /dev/null +++ b/src/component/reducer/preferences/actions/changePrintPageSettings.ts @@ -0,0 +1,16 @@ +import { Draft } from 'immer'; + +import { + PreferencesState, + ChangePrintPageSettingsAction, +} from '../preferencesReducer'; +import { getActiveWorkspace } from '../utilities/getActiveWorkspace'; + +export function changePrintPageSettings( + draft: Draft, + action: ChangePrintPageSettingsAction, +) { + const currentWorkspacePreferences = getActiveWorkspace(draft); + const pageSettings = action.payload || {}; + currentWorkspacePreferences.printPageOptions = pageSettings; +} diff --git a/src/component/reducer/preferences/preferencesReducer.ts b/src/component/reducer/preferences/preferencesReducer.ts index 6b7e795fab..6837b79527 100644 --- a/src/component/reducer/preferences/preferencesReducer.ts +++ b/src/component/reducer/preferences/preferencesReducer.ts @@ -3,6 +3,7 @@ import { CURRENT_EXPORT_VERSION, migrateSettings, MultipleSpectraAnalysisPreferences, + PrintPageOptions, Workspace, WorkSpaceSource, } from 'nmr-load-save'; @@ -23,6 +24,7 @@ import { } from './actions/analyzeSpectra'; import { applyGeneralPreferences } from './actions/applyGeneralPreferences'; import { changeInformationBlockPosition } from './actions/changeInformationBlockPosition'; +import { changePrintPageSettings } from './actions/changePrintPageSettings'; import { initPreferences } from './actions/initPreferences'; import { setMatrixGenerationOptions, @@ -122,6 +124,10 @@ export type ToggleInformationBlock = ActionType< visible?: boolean; } >; +export type ChangePrintPageSettingsAction = ActionType< + 'CHANGE_PRINT_PAGE_SETTINGS', + PrintPageOptions +>; type PreferencesActions = | InitPreferencesAction @@ -138,7 +144,8 @@ type PreferencesActions = | MatrixGenerationActions | SetVerticalSplitterPositionAction | ChangeInformationBlockPosition - | ToggleInformationBlock; + | ToggleInformationBlock + | ChangePrintPageSettingsAction; export const WORKSPACES: Array<{ key: NMRiumWorkspace; @@ -289,6 +296,8 @@ function innerPreferencesReducer( return changeInformationBlockPosition(draft, action); case 'TOGGLE_INFORMATION_BLOCK': return toggleInformationBlock(draft, action); + case 'CHANGE_PRINT_PAGE_SETTINGS': + return changePrintPageSettings(draft, action); default: return draft; diff --git a/src/component/workspaces/workspaceDefaultProperties.ts b/src/component/workspaces/workspaceDefaultProperties.ts index 7d52cb525b..b54981255e 100644 --- a/src/component/workspaces/workspaceDefaultProperties.ts +++ b/src/component/workspaces/workspaceDefaultProperties.ts @@ -235,4 +235,5 @@ export const workspaceDefaultProperties: Required = { }, ], }, + printPageOptions: {}, }; diff --git a/src/demo/Sidebar.tsx b/src/demo/Sidebar.tsx index c8abf61e95..ca20e16d2e 100644 --- a/src/demo/Sidebar.tsx +++ b/src/demo/Sidebar.tsx @@ -102,6 +102,7 @@ function Sidebar(props) { return (
div { + width: 100% !important; + margin: 0 !important; + padding: 0 !important; + } + + .demo-side-bar, + .demo-main-container > div > div:first-child { + display: none !important; + } +} + .rc-menu { border: none !important; box-shadow: none !important; diff --git a/src/demo/layouts/Admin.tsx b/src/demo/layouts/Admin.tsx index d3bd4f7168..1d6bfb6fa4 100644 --- a/src/demo/layouts/Admin.tsx +++ b/src/demo/layouts/Admin.tsx @@ -71,6 +71,7 @@ export function Dashboard(props: DashboardProps) { onMenuToggle={toggleMenu} />