@@ -44,23 +44,40 @@ export default function DetailsPane({
4444 onVariantChange,
4545 mapxPath,
4646 idmlPath,
47+ hasUnsavedChanges = false ,
48+ onSaveLabels,
49+ onRevertLabels,
4750} ) {
4851 const [ localIsApproved , setLocalIsApproved ] = useState ( isApproved ) ;
4952 const [ localRenderings , setLocalRenderings ] = useState ( renderings ) ;
5053 const [ showTemplateInfo , setShowTemplateInfo ] = useState ( false ) ;
5154 const [ templateData , setTemplateData ] = useState ( { } ) ;
5255 const [ showExportDialog , setShowExportDialog ] = useState ( false ) ;
53- const [ selectedExportFormat , setSelectedExportFormat ] = useState ( 'idml-txt' ) ;
56+ const [ selectedExportFormat , setSelectedExportFormat ] = useState ( 'idml-full' ) ;
57+ const [ lastUsedExportFormat , setLastUsedExportFormat ] = useState ( null ) ;
5458
55- // Reset export format if selected format is unavailable
59+ // Set export format based on last-used or precedence order when dialog opens
5660 useEffect ( ( ) => {
57- if ( selectedExportFormat === 'mapx-full' && ! mapxPath ) {
58- setSelectedExportFormat ( 'idml-txt' ) ;
59- }
60- if ( selectedExportFormat === 'idml-full' && ! idmlPath ) {
61- setSelectedExportFormat ( 'idml-txt' ) ;
61+ if ( ! showExportDialog ) return ;
62+
63+ // Determine available options
64+ const availableFormats = [ ] ;
65+ if ( idmlPath ) availableFormats . push ( 'idml-full' ) ;
66+ if ( mapxPath ) availableFormats . push ( 'mapx-full' ) ;
67+ if ( templateData . formats && templateData . formats . includes ( 'mapx' ) ) availableFormats . push ( 'mapx-txt' ) ;
68+
69+ // If last-used format is available, use it
70+ if ( lastUsedExportFormat && availableFormats . includes ( lastUsedExportFormat ) ) {
71+ setSelectedExportFormat ( lastUsedExportFormat ) ;
72+ } else {
73+ // Otherwise, use precedence order: idml-full > mapx-full > mapx-txt
74+ if ( availableFormats . length > 0 ) {
75+ setSelectedExportFormat ( availableFormats [ 0 ] ) ;
76+ } else {
77+ setSelectedExportFormat ( null ) ;
78+ }
6279 }
63- } , [ mapxPath , idmlPath , selectedExportFormat ] ) ;
80+ } , [ showExportDialog , mapxPath , idmlPath , templateData . formats , lastUsedExportFormat ] ) ;
6481
6582 // Load template data when mapDef.template changes
6683 useEffect ( ( ) => {
@@ -204,30 +221,18 @@ export default function DetailsPane({
204221 }
205222 } ;
206223
207- // Helper function to generate USFM from the current map state // TODO: compare with usfmFromMap(). Could probably be consolidated.
224+ // Helper function to generate USFM from the current map state
225+ // USFM now only contains the \fig field - labels are stored in .idml.txt files
208226 const generateUsfm = ( ) => {
209- console . log ( 'Converting map to USFM:' , mapDef ) ;
210- // Reconstruct USFM string from current map state
211- let usfm = `\\zdiagram-s |template="${ mapDef . template } "\\*\n` ;
212-
213- // Always include the \fig line if present, and ensure it is in correct USFM format
227+ console . log ( 'Converting map to USFM (only \\fig field):' , mapDef ) ;
228+ // Only return the \fig...\fig* field
229+ let usfm = '' ;
214230 if ( mapDef . fig && ! / ^ \\ f i g / . test ( mapDef . fig ) ) {
215- usfm + = `\\fig ${ mapDef . fig } \\fig*\n ` ;
231+ usfm = `\\fig ${ mapDef . fig } \\fig*` ;
216232 } else if ( mapDef . fig ) {
217- usfm += ` ${ mapDef . fig } \n` ;
233+ usfm = mapDef . fig ;
218234 }
219-
220- // Add each label as a \zlabel entry
221- locations . forEach ( label => {
222- usfm += `\\zlabel-s |key="${ label . mergeKey } " termid="${ label . termId } " gloss="${ inLang (
223- label . gloss ,
224- lang
225- ) } "\\*${ label . vernLabel || '' } \\zlabel-e\\*\n`;
226- } ) ;
227-
228- usfm += '\\zdiagram-e \\*' ;
229- // Remove unnecessary escaping for output
230- return usfm . replace ( / \\ / g, '\\' ) ;
235+ return usfm ;
231236 } ;
232237
233238 const handleOk = ( ) => {
@@ -579,6 +584,85 @@ export default function DetailsPane({
579584 📂
580585 </ span >
581586 </ button >
587+ < button
588+ onClick = { onRevertLabels }
589+ disabled = { ! hasUnsavedChanges }
590+ style = { {
591+ background : 'none' ,
592+ border : 'none' ,
593+ cursor : hasUnsavedChanges ? 'pointer' : 'not-allowed' ,
594+ padding : 0 ,
595+ marginLeft : 1 ,
596+ opacity : hasUnsavedChanges ? 1 : 0.3 ,
597+ } }
598+ title = { inLang ( uiStr . revertChanges , lang ) }
599+ >
600+ { /* Revert icon: curved arrow going left */ }
601+ < svg
602+ width = "22"
603+ height = "22"
604+ viewBox = "0 0 22 22"
605+ fill = "none"
606+ xmlns = "http://www.w3.org/2000/svg"
607+ >
608+ < path
609+ d = "M8 11h8M8 11l3 3m-3-3l3-3M16 11a5 5 0 1 1-5-5"
610+ stroke = { hasUnsavedChanges ? '#ff9800' : '#999' }
611+ strokeWidth = "1.8"
612+ strokeLinecap = "round"
613+ strokeLinejoin = "round"
614+ />
615+ </ svg >
616+ </ button >
617+ < button
618+ onClick = { onSaveLabels }
619+ disabled = { ! hasUnsavedChanges }
620+ style = { {
621+ background : 'none' ,
622+ border : 'none' ,
623+ cursor : hasUnsavedChanges ? 'pointer' : 'not-allowed' ,
624+ padding : 0 ,
625+ marginLeft : 1 ,
626+ opacity : hasUnsavedChanges ? 1 : 0.3 ,
627+ } }
628+ title = { inLang ( uiStr . saveChanges , lang ) }
629+ >
630+ { /* Save icon: floppy disk */ }
631+ < svg
632+ width = "22"
633+ height = "22"
634+ viewBox = "0 0 22 22"
635+ fill = "none"
636+ xmlns = "http://www.w3.org/2000/svg"
637+ >
638+ < rect
639+ x = "4"
640+ y = "3"
641+ width = "14"
642+ height = "16"
643+ rx = "2"
644+ stroke = { hasUnsavedChanges ? '#4caf50' : '#999' }
645+ strokeWidth = "1.5"
646+ fill = "none"
647+ />
648+ < rect
649+ x = "7"
650+ y = "3"
651+ width = "8"
652+ height = "5"
653+ fill = { hasUnsavedChanges ? '#4caf50' : '#999' }
654+ />
655+ < rect
656+ x = "6"
657+ y = "12"
658+ width = "10"
659+ height = "7"
660+ fill = "none"
661+ stroke = { hasUnsavedChanges ? '#4caf50' : '#999' }
662+ strokeWidth = "1.2"
663+ />
664+ </ svg >
665+ </ button >
582666 < button
583667 onClick = { handleExportDataMerge }
584668 style = { {
@@ -774,19 +858,6 @@ export default function DetailsPane({
774858 </ div >
775859 ) }
776860
777- { /* IDML Data Merge Export */ }
778- < label style = { { display : 'block' , marginBottom : 8 , cursor : 'pointer' } } >
779- < input
780- type = "radio"
781- name = "exportFormat"
782- value = "idml-txt"
783- checked = { selectedExportFormat === 'idml-txt' }
784- onChange = { e => setSelectedExportFormat ( e . target . value ) }
785- style = { { marginRight : 8 } }
786- />
787- InDesign data merge file (.IDML.TXT)
788- </ label >
789-
790861 { /* MAPX Full Export */ }
791862 { mapxPath ? (
792863 < label style = { { display : 'block' , marginBottom : 8 , cursor : 'pointer' } } >
@@ -853,16 +924,19 @@ export default function DetailsPane({
853924 </ button >
854925 < button
855926 onClick = { async ( ) => {
927+ if ( ! selectedExportFormat ) return ;
928+ setLastUsedExportFormat ( selectedExportFormat ) ;
856929 setShowExportDialog ( false ) ;
857930 await handleExportWithFormat ( selectedExportFormat ) ;
858931 } }
932+ disabled = { ! selectedExportFormat }
859933 style = { {
860934 padding : '8px 16px' ,
861935 borderRadius : 4 ,
862- border : '1px solid #1976d2' ,
863- background : '#1976d2' ,
864- color : 'white' ,
865- cursor : 'pointer' ,
936+ border : selectedExportFormat ? '1px solid #1976d2' : '1px solid #ccc ',
937+ background : selectedExportFormat ? '#1976d2' : '#e0e0e0 ',
938+ color : selectedExportFormat ? 'white' : '#999 ',
939+ cursor : selectedExportFormat ? 'pointer' : 'not-allowed ',
866940 } }
867941 >
868942 { inLang ( uiStr . ok , lang ) }
0 commit comments