TemplateModal.tsx 3.8 KB

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