TemplateModal.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import React, { useCallback, useState } from 'react';
  2. import { ITemplate } from '@growi/core';
  3. import { useTranslation } from 'next-i18next';
  4. import {
  5. Modal,
  6. ModalHeader,
  7. ModalBody,
  8. ModalFooter,
  9. } from 'reactstrap';
  10. import { usePreviewOptions } from '~/stores/renderer';
  11. import { useTemplates } from '~/stores/template';
  12. import Preview from './PageEditor/Preview';
  13. type TemplateRadioButtonProps = {
  14. template: ITemplate,
  15. onChange: (selectedTemplate: ITemplate) => void,
  16. isSelected?: boolean,
  17. }
  18. const TemplateRadioButton = ({ template, onChange, isSelected }: TemplateRadioButtonProps): JSX.Element => {
  19. const radioButtonId = `rb-${template.id}`;
  20. return (
  21. <div key={template.id} className="custom-control custom-radio mb-2">
  22. <input
  23. id={radioButtonId}
  24. type="radio"
  25. className="custom-control-input"
  26. checked={isSelected}
  27. onChange={() => onChange(template)}
  28. />
  29. <label className="custom-control-label" htmlFor={radioButtonId}>
  30. {template.name}
  31. </label>
  32. </div>
  33. );
  34. };
  35. type Props = {
  36. isOpen: boolean,
  37. onClose: () => void,
  38. onSubmit?: (markdown: string) => void,
  39. }
  40. export const TemplateModal = (props: Props): JSX.Element => {
  41. const { t } = useTranslation();
  42. const { isOpen, onClose, onSubmit } = props;
  43. const { data: rendererOptions } = usePreviewOptions();
  44. const { data: templates } = useTemplates();
  45. const [selectedTemplate, setSelectedTemplate] = useState<ITemplate>();
  46. const submitHandler = useCallback((template?: ITemplate) => {
  47. if (onSubmit == null || template == null) {
  48. onClose();
  49. return;
  50. }
  51. onSubmit(template.markdown);
  52. onClose();
  53. }, [onClose, onSubmit]);
  54. if (templates == null) {
  55. return <></>;
  56. }
  57. return (
  58. <Modal className="link-edit-modal" isOpen={isOpen} toggle={onClose} size="lg" autoFocus={false}>
  59. <ModalHeader tag="h4" toggle={onClose} className="bg-primary text-light">
  60. Template
  61. </ModalHeader>
  62. <ModalBody className="container">
  63. <div className="row">
  64. <div className="col-12">
  65. { templates.map(template => (
  66. <TemplateRadioButton
  67. key={template.id}
  68. template={template}
  69. onChange={t => setSelectedTemplate(t)}
  70. isSelected={template.id === selectedTemplate?.id}
  71. />
  72. )) }
  73. </div>
  74. </div>
  75. { rendererOptions != null && (
  76. <>
  77. <hr />
  78. <h3>Preview</h3>
  79. <div className='card'>
  80. <div className="card-body" style={{ maxHeight: '60vh', overflowY: 'auto' }}>
  81. <Preview rendererOptions={rendererOptions} markdown={selectedTemplate?.markdown}/>
  82. </div>
  83. </div>
  84. </>
  85. ) }
  86. </ModalBody>
  87. <ModalFooter>
  88. <button type="button" className="btn btn-sm btn-outline-secondary mx-1" onClick={onClose}>
  89. {t('Cancel')}
  90. </button>
  91. <button type="submit" className="btn btn-sm btn-primary mx-1" onClick={() => submitHandler(selectedTemplate)} disabled={selectedTemplate == null}>
  92. {t('Update')}
  93. </button>
  94. </ModalFooter>
  95. </Modal>
  96. );
  97. };