TemplateModal.tsx 4.6 KB

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