import { Checkbox, Tag } from '@blueprintjs/core'; import { useStore } from '@tanstack/react-form'; import type { PageSizeName, Unit } from '@zakodium/nmrium-core'; import { units } from '@zakodium/nmrium-core'; import { memo, useMemo } from 'react'; import { FormGroup, assertUnreachable, withFieldGroup } from 'react-science/ui'; import type { z } from 'zod/v4'; import { convertToPixels } from '../../../../elements/export/units.js'; import { useExportConfigurer } from '../../../../elements/export/useExportConfigurer.js'; import { getExportDefaultOptionsByMode, getExportOptions, } from '../../../../elements/export/utilities/getExportOptions.js'; import { pageSizes } from '../../../../elements/print/pageSize.js'; import { exportSettingsValidation } from '../validation/export_tab_validation.js'; import { defaultGeneralSettingsFormValues } from '../validation.js'; type Mode = 'basic' | 'advance'; type Layout = 'portrait' | 'landscape'; interface SelectItem { label: string; value: Value; } const pageSizeItems: Record>> = { portrait: pageSizes.map((item) => ({ value: item.name, label: `${item.name} (${item.portrait.width} cm x ${item.portrait.height} cm)`, })), landscape: pageSizes.map((item) => ({ value: item.name, label: `${item.name} (${item.landscape.width} cm x ${item.landscape.height} cm)`, })), }; const modeItems: Array> = [ { label: 'Basic', value: 'basic' }, { label: 'Advanced', value: 'advance' }, ]; const unitItems: Array> = units.map((u) => ({ label: u.name, value: u.unit, })); const layoutItems: Array> = [ { value: 'portrait', label: 'Portrait' }, { value: 'landscape', label: 'Landscape' }, ]; function safeNumber(value: number) { if (Number.isNaN(value)) value = 1; else if (value <= 0) value = 1; return value; } function safeStringNumber(str: string, onInvalid: (str: string) => void) { let isInvalid = false; let value = Number(str); if (Number.isNaN(value)) { value = 1; isInvalid = true; } if (value <= 0) { value = 1; isInvalid = true; } if (!isInvalid) { const result = String(value); onInvalid(result); return result; } return str; } export const ExportFields = withFieldGroup({ defaultValues: defaultGeneralSettingsFormValues.export.png, render: function ExportFields({ group }) { const inputValues = useStore(group.store, (s) => s.values); const outputValues = useMemo(() => { return exportSettingsValidation.decode(inputValues); }, [inputValues]); const advancedTransforms = useExportConfigurer(outputValues); function onModeChange({ value }: { value: Mode }) { const newOptions = getExportDefaultOptionsByMode(value); for (const [key, value] of Object.entries(newOptions)) { group.setFieldValue(key as keyof typeof newOptions, value, { dontRunListeners: true, }); } } function onChangeUnit({ value }: { value: Unit }) { const { width, height } = advancedTransforms.changeUnit({ unit: value }); group.setFieldValue('width', String(safeNumber(width)), { dontRunListeners: true, }); group.setFieldValue('height', String(safeNumber(height)), { dontRunListeners: true, }); } function onWidthChange({ value }: { value: string }) { value = safeStringNumber(value, (width) => group.setFieldValue('width', width, { dontRunListeners: true }), ); const height = advancedTransforms.changeSize( Number(value), 'height', 'width', ); if (!advancedTransforms.isAspectRatioEnabled) { return; } group.setFieldValue('height', String(safeNumber(height)), { dontRunListeners: true, }); } function onHeightChange({ value }: { value: string }) { value = safeStringNumber(value, (height) => group.setFieldValue('height', height, { dontRunListeners: true }), ); const width = advancedTransforms.changeSize( Number(value), 'width', 'height', ); if (!advancedTransforms.isAspectRatioEnabled) { return; } group.setFieldValue('width', String(safeNumber(width)), { dontRunListeners: true, }); } function onDPIChange({ value }: { value: string }) { if (inputValues.mode !== 'advance') return; if (inputValues.unit !== 'px') return; value = safeStringNumber(value, (dpi) => group.setFieldValue('dpi', dpi, { dontRunListeners: true }), ); const { width, height } = advancedTransforms.changeDPI(Number(value)); group.setFieldValue('width', String(safeNumber(width)), { dontRunListeners: true, }); group.setFieldValue('height', String(safeNumber(height)), { dontRunListeners: true, }); } const { AppField, Subscribe } = group; return ( <> {({ RadioGroup }) => } { const mode = state.values.mode; switch (state.values.mode) { case 'basic': return { mode: state.values.mode, layout: state.values.layout }; case 'advance': return { mode: state.values.mode, unit: state.values.unit }; default: assertUnreachable(mode as never); } }} > {({ mode, layout, unit }) => { switch (mode) { case 'basic': return ( <> {({ Select }) => ( )} { advancedTransforms.enableAspectRatio( event.currentTarget.checked, ); }} /> {({ NumericInput }) => ( {unit}} /> )} {({ NumericInput }) => ( {unit}} /> )} ); default: assertUnreachable(mode); } }} {({ NumericInput }) => } {({ Checkbox }) => ( )} ); }, }); type DescriptionPreviewProps = z.output; const DescriptionPreview = memo(function DescriptionPreview( props: DescriptionPreviewProps, ) { const { width, height, dpi, unit } = getExportOptions(props); const convertOptions = { precision: 0 }; const widthInPixel = convertToPixels(width, unit, dpi, convertOptions); const heightInPixel = convertToPixels(height, unit, dpi, convertOptions); return ( {`${widthInPixel} px x ${heightInPixel} px @ ${dpi}DPI`} ); });