TemplateModal.tsx 3.3 KB

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