1- import {
2- Button ,
3- DialogFooter ,
4- Radio ,
5- RadioGroup ,
6- SegmentedControl ,
7- Tag ,
8- } from '@blueprintjs/core' ;
9- import { yupResolver } from '@hookform/resolvers/yup' ;
10- import type {
11- AdvanceExportSettings ,
12- BasicExportSettings ,
13- ExportSettings ,
14- } from '@zakodium/nmrium-core' ;
15- import { useEffect , useState } from 'react' ;
16- import { Controller , useForm } from 'react-hook-form' ;
1+ import { DialogFooter } from '@blueprintjs/core' ;
2+ import { useMemo } from 'react' ;
3+ import { Button , Form , useForm } from 'react-science/ui' ;
4+ import { z } from 'zod/v4' ;
175
18- import ActionButtons from '../ActionButtons.js' ;
19- import { CheckController } from '../CheckController.js' ;
20- import type { LabelStyle } from '../Label.js' ;
21- import Label from '../Label.js' ;
22- import { NumberInput2Controller } from '../NumberInput2Controller.js' ;
23- import { Select2Controller } from '../Select2Controller.js' ;
6+ import { ExportFields } from '../../modal/setting/tanstack_general_settings/tabs/export_tab.fields.tsx' ;
7+ import { exportSettingsValidation } from '../../modal/setting/tanstack_general_settings/validation/export_tab_validation.ts' ;
248import { StandardDialog } from '../StandardDialog.tsx' ;
25- import { StyledDialogBody } from '../StyledDialogBody.js' ;
26- import type { SizeItem } from '../print/pageSize.js' ;
27- import { getSizesList } from '../print/pageSize.js' ;
9+ import { StyledDialogBody } from '../StyledDialogBody.tsx' ;
2810
2911import type { BaseExportProps } from './ExportContent.js' ;
30- import { units } from './units.js' ;
31- import { useExportConfigurer } from './useExportConfigurer.js' ;
32- import { exportOptionValidationSchema } from './utilities/exportOptionValidationSchema.js' ;
33- import {
34- getExportDefaultOptions ,
35- getExportDefaultOptionsByMode ,
36- } from './utilities/getExportOptions.js' ;
37- import type { Mode } from './utilities/getModes.js' ;
38- import { MODES } from './utilities/getModes.js' ;
12+ import { getExportDefaultOptions } from './utilities/getExportOptions.ts' ;
3913
4014interface InnerExportOptionsModalProps extends BaseExportProps {
4115 onCloseDialog : ( ) => void ;
42- confirmButtonText ? : string ;
16+ confirmButtonText : string ;
4317}
4418interface ExportOptionsModalProps extends InnerExportOptionsModalProps {
4519 isOpen : boolean ;
@@ -53,101 +27,36 @@ export function ExportOptionsModal(props: ExportOptionsModalProps) {
5327 return < InnerExportOptionsModal { ...otherProps } /> ;
5428}
5529
56- const labelStyle : LabelStyle = {
57- label : {
58- color : '#232323' ,
59- width : '80px' ,
60- } ,
61- wrapper : {
62- display : 'flex' ,
63- justifyContent : 'flex-start' ,
64- } ,
65- container : { margin : '5px 0' } ,
66- } ;
30+ const validator = z . object ( {
31+ values : exportSettingsValidation ,
32+ } ) ;
6733
6834function InnerExportOptionsModal ( props : InnerExportOptionsModalProps ) {
6935 const {
7036 onCloseDialog,
71- onExportOptionsChange,
7237 confirmButtonText,
7338 defaultExportOptions,
39+ onExportOptionsChange,
7440 } = props ;
75- const defaultValues = getExportDefaultOptions ( defaultExportOptions ) ;
76-
77- const [ mode , setMode ] = useState < Mode > ( defaultValues . mode ) ;
7841
79- const methods = useForm < ExportSettings > ( {
80- defaultValues,
81- resolver : yupResolver ( exportOptionValidationSchema as any ) ,
42+ const values = useMemo ( ( ) => {
43+ return exportSettingsValidation . encode (
44+ getExportDefaultOptions ( defaultExportOptions ) ,
45+ ) ;
46+ } , [ defaultExportOptions ] ) ;
47+
48+ const form = useForm ( {
49+ defaultValues : {
50+ values,
51+ } ,
52+ validators : {
53+ onDynamic : validator ,
54+ } ,
55+ onSubmit : ( { value : { values } } ) => {
56+ const parsedValues = exportSettingsValidation . parse ( values ) ;
57+ onExportOptionsChange ( parsedValues ) ;
58+ } ,
8259 } ) ;
83- const {
84- handleSubmit,
85- control,
86- watch,
87- setValue,
88- formState : { isValid, errors } ,
89- reset,
90- setFocus,
91- } = methods ;
92-
93- const watchSettings = watch ( ) ;
94- const {
95- unit,
96- dpi = 0 ,
97- layout,
98- } = watchSettings as AdvanceExportSettings & BasicExportSettings ;
99- const {
100- widthInPixel,
101- heightInPixel,
102- isAspectRatioEnabled,
103- changeDPI,
104- enableAspectRatio,
105- changeSize,
106- changeUnit,
107- } = useExportConfigurer ( watchSettings ) ;
108-
109- let sizesList : SizeItem [ ] = [ ] ;
110-
111- if ( layout ) {
112- sizesList = getSizesList ( layout ) ;
113- }
114-
115- function handleChangeMode ( mode : any ) {
116- const options = defaultValues ;
117- setMode ( mode ) ;
118- if ( options . mode === mode ) {
119- reset ( defaultValues ) ;
120- } else {
121- reset ( getExportDefaultOptionsByMode ( mode ) ) ;
122- }
123- }
124-
125- useEffect ( ( ) => {
126- const handleRenderComplete = ( ) => {
127- setTimeout ( ( ) => {
128- if ( mode === 'advance' ) {
129- setFocus ( 'width' ) ;
130- } else {
131- setFocus ( 'dpi' ) ;
132- }
133- } , 0 ) ;
134- } ;
135-
136- const handleKeyDown = ( event : globalThis . KeyboardEvent ) => {
137- if ( event . key === 'Enter' ) {
138- void handleSubmit ( onExportOptionsChange ) ( ) ;
139- }
140- } ;
141-
142- globalThis . addEventListener ( 'keydown' , handleKeyDown ) ;
143-
144- const animationFrameId = requestAnimationFrame ( handleRenderComplete ) ;
145-
146- return ( ) => {
147- cancelAnimationFrame ( animationFrameId ) ;
148- globalThis . removeEventListener ( 'keydown' , handleKeyDown ) ;
149- } ;
150- } , [ handleSubmit , mode , onExportOptionsChange , setFocus ] ) ;
15160
15261 return (
15362 < StandardDialog
@@ -158,165 +67,36 @@ function InnerExportOptionsModal(props: InnerExportOptionsModalProps) {
15867 canEscapeKeyClose
15968 autoFocus
16069 >
161- < StyledDialogBody >
162- < div
163- style = { {
164- display : 'flex' ,
165- justifyContent : 'center' ,
166- marginBottom : '15px' ,
70+ < form . AppForm >
71+ < Form
72+ layout = "inline"
73+ noValidate
74+ onSubmit = { ( event ) => {
75+ event . preventDefault ( ) ;
76+ void form . handleSubmit ( ) ;
16777 } }
16878 >
169- < SegmentedControl
170- defaultValue = "list"
171- inline
172- options = { MODES }
173- value = { mode }
174- onValueChange = { handleChangeMode }
175- />
176- </ div >
177-
178- < Label style = { labelStyle } title = "Description:" >
179- < Tag
180- intent = {
181- ! isValid && Object . keys ( errors ) . length > 0 ? 'danger' : 'none'
79+ < StyledDialogBody >
80+ < ExportFields form = { form } fields = "values" />
81+ </ StyledDialogBody >
82+ < DialogFooter
83+ actions = {
84+ < >
85+ < Button
86+ variant = "outlined"
87+ intent = "danger"
88+ onClick = { ( ) => onCloseDialog ?.( ) }
89+ >
90+ Cancel
91+ </ Button >
92+ < form . SubmitButton intent = "success" >
93+ { confirmButtonText }
94+ </ form . SubmitButton >
95+ </ >
18296 }
183- > { `${ widthInPixel } px x ${ heightInPixel } px @ ${ dpi } DPI` } </ Tag >
184- </ Label >
185- { mode === 'basic' && (
186- < >
187- < Label style = { labelStyle } title = "Size" >
188- < Select2Controller
189- control = { control }
190- name = "size"
191- items = { sizesList }
192- />
193- </ Label >
194- < Label style = { labelStyle } title = "Layout" >
195- < Controller
196- name = "layout"
197- control = { control }
198- render = { ( { field } ) => {
199- const { value, ref, ...otherFieldProps } = field ;
200- return (
201- < RadioGroup
202- inline
203- selectedValue = { value }
204- { ...otherFieldProps }
205- >
206- < Radio label = "Portrait" value = "portrait" />
207- < Radio label = "Landscape" value = "landscape" />
208- </ RadioGroup >
209- ) ;
210- } }
211- />
212- </ Label >
213- </ >
214- ) }
215- { mode === 'advance' && (
216- < >
217- < Label style = { labelStyle } title = "Size" >
218- < div style = { { display : 'flex' , flexDirection : 'row' } } >
219- < NumberInput2Controller
220- name = "width"
221- control = { control }
222- style = { { width : 100 } }
223- rightElement = { < Tag > { unit } </ Tag > }
224- controllerProps = { { rules : { required : true } } }
225- transformValue = { ( value ) => {
226- const newHeight = changeSize ( value , 'height' , 'width' ) ;
227- setValue ( 'height' , newHeight ) ;
228- return value ;
229- } }
230- debounceTime = { 250 }
231- placeholder = "width"
232- />
233- < div style = { { padding : '0px 5px' } } >
234- < Button
235- icon = "link"
236- variant = "minimal"
237- active = { isAspectRatioEnabled }
238- onClick = { ( ) => {
239- enableAspectRatio ( ( prevFlag ) => ! prevFlag ) ;
240- } }
241- />
242- </ div >
243- < NumberInput2Controller
244- name = "height"
245- control = { control }
246- style = { { width : 100 } }
247- rightElement = { < Tag > { unit } </ Tag > }
248- controllerProps = { { rules : { required : true } } }
249- transformValue = { ( value ) => {
250- const newWidth = changeSize ( value , 'width' , 'height' ) ;
251- setValue ( 'width' , newWidth ) ;
252- return value ;
253- } }
254- debounceTime = { 250 }
255- placeholder = "height"
256- />
257- </ div >
258- </ Label >
259- < Label style = { labelStyle } title = "Units" >
260- < Select2Controller
261- control = { control }
262- name = "unit"
263- itemTextKey = "name"
264- itemValueKey = "unit"
265- items = { units }
266- onItemSelect = { ( { unit } ) => {
267- const newSize = changeUnit ( { unit } ) ;
268- setValue ( 'width' , newSize . width ) ;
269- setValue ( 'height' , newSize . height ) ;
270- } }
271- />
272- </ Label >
273- </ >
274- ) }
275- < Label style = { labelStyle } title = "DPI" >
276- < NumberInput2Controller
277- name = "dpi"
278- control = { control }
279- style = { { width : 100 } }
280- controllerProps = { { rules : { required : true } } }
281- transformValue = { ( value ) => {
282- if ( unit === 'px' ) {
283- const convertedValue = changeDPI ( value ) ;
284- setValue ( 'width' , convertedValue . width ) ;
285- setValue ( 'height' , convertedValue . height ) ;
286- }
287-
288- return value ;
289- } }
290- debounceTime = { 250 }
291- placeholder = "DPI"
292- />
293- </ Label >
294- </ StyledDialogBody >
295- < DialogFooter >
296- < div
297- style = { {
298- display : 'flex' ,
299- justifyContent : 'space-between' ,
300- alignItems : 'baseline' ,
301- } }
302- >
303- < CheckController
304- control = { control }
305- name = "useDefaultSettings"
306- style = { { fontSize : '12px' } }
307- label = "Don't show options dialog next time and use those settings"
308- />
309-
310- < ActionButtons
311- style = { { flexDirection : 'row-reverse' , margin : 0 } }
312- onDone = { ( ) => {
313- void handleSubmit ( onExportOptionsChange ) ( ) ;
314- } }
315- doneLabel = { confirmButtonText }
316- onCancel = { ( ) => onCloseDialog ?.( ) }
31797 />
318- </ div >
319- </ DialogFooter >
98+ </ Form >
99+ </ form . AppForm >
320100 </ StandardDialog >
321101 ) ;
322102}
0 commit comments