import React, { useCallback, useEffect, useState, } from 'react'; import assert from 'assert'; import type { Lang } from '@growi/core'; import { useTemplateModal, type TemplateModalStatus } from '@growi/editor/dist/client'; import { extractSupportedLocales, getLocalizedTemplate, type TemplateSummary, } from '@growi/pluginkit/dist/v4'; import { LoadingSpinner } from '@growi/ui/dist/components'; import { useTranslation } from 'next-i18next'; import { Modal, ModalHeader, ModalBody, ModalFooter, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem, } from 'reactstrap'; import { useSWRxTemplate, useSWRxTemplates } from '~/features/templates/stores'; import { usePersonalSettings } from '~/stores/personal-settings'; import { usePreviewOptions } from '~/stores/renderer'; import loggerFactory from '~/utils/logger'; import Preview from '../PageEditor/Preview'; import { useFormatter } from './use-formatter'; import styles from './TemplateModal.module.scss'; const logger = loggerFactory('growi:components:TemplateModal'); function constructTemplateId(templateSummary: TemplateSummary): string { const defaultTemplate = templateSummary.default; return `${defaultTemplate.pluginId ?? ''}_${defaultTemplate.id}`; } type TemplateSummaryItemProps = { templateSummary: TemplateSummary, selectedLocale?: string, onClick?: () => void, isSelected?: boolean, usersDefaultLang?: Lang, } const TemplateListGroupItem: React.FC = ({ templateSummary, onClick, isSelected, usersDefaultLang, }) => { const localizedTemplate = getLocalizedTemplate(templateSummary, usersDefaultLang); const templateLocales = extractSupportedLocales(templateSummary); assert(localizedTemplate?.isValid); return (

{localizedTemplate.title} {localizedTemplate.pluginId != null ? extension : ''}

{localizedTemplate.desc}

{ templateLocales != null && Array.from(templateLocales).map(locale => ( {locale} ))}
); }; const TemplateDropdownItem: React.FC = ({ templateSummary, onClick, usersDefaultLang, }) => { const localizedTemplate = getLocalizedTemplate(templateSummary, usersDefaultLang); const templateLocales = extractSupportedLocales(templateSummary); assert(localizedTemplate?.isValid); return (

{localizedTemplate.title} {localizedTemplate.pluginId != null ? extension : ''}

{localizedTemplate.desc}

{ templateLocales != null && Array.from(templateLocales).map(locale => ( {locale} ))}
); }; type TemplateModalSubstanceProps = { templateModalStatus: TemplateModalStatus, close: () => void, } const TemplateModalSubstance = (props: TemplateModalSubstanceProps): JSX.Element => { const { templateModalStatus, close } = props; const { t } = useTranslation(['translation', 'commons']); const { data: personalSettingsInfo } = usePersonalSettings(); const { data: rendererOptions } = usePreviewOptions(); const { data: templateSummaries, isLoading } = useSWRxTemplates(); const [selectedTemplateSummary, setSelectedTemplateSummary] = useState(); const [selectedTemplateLocale, setSelectedTemplateLocale] = useState(); const { data: selectedTemplateMarkdown } = useSWRxTemplate(selectedTemplateSummary, selectedTemplateLocale); const { format } = useFormatter(); const usersDefaultLang = personalSettingsInfo?.lang; const selectedLocalizedTemplate = getLocalizedTemplate(selectedTemplateSummary, usersDefaultLang); const selectedTemplateLocales = extractSupportedLocales(selectedTemplateSummary); const submitHandler = useCallback((markdown?: string) => { if (markdown == null) { return; } if (templateModalStatus.onSubmit == null) { close(); return; } templateModalStatus.onSubmit(format(selectedTemplateMarkdown)); close(); }, [close, format, selectedTemplateMarkdown, templateModalStatus]); const onClickHandler = useCallback(( templateSummary: TemplateSummary, ) => { let localeToSet: string | Lang | undefined; if (selectedTemplateLocale != null && selectedTemplateLocale in templateSummary) { localeToSet = selectedTemplateLocale; } else if (usersDefaultLang != null && usersDefaultLang in templateSummary) { localeToSet = usersDefaultLang; } else { localeToSet = undefined; } setSelectedTemplateLocale(localeToSet); setSelectedTemplateSummary(templateSummary); }, [selectedTemplateLocale, usersDefaultLang]); useEffect(() => { if (!templateModalStatus.isOpened) { setSelectedTemplateSummary(undefined); setSelectedTemplateLocale(undefined); } }, [templateModalStatus.isOpened]); return (
{t('template.modal_label.Select template')}
{/* List Group */}
{ isLoading && (
) }
{ templateSummaries != null && templateSummaries.map((templateSummary) => { const templateId = constructTemplateId(templateSummary); const isSelected = selectedTemplateSummary != null && constructTemplateId(selectedTemplateSummary) === templateId; return ( onClickHandler(templateSummary)} isSelected={isSelected} usersDefaultLang={usersDefaultLang} /> ); }) }
{/* Dropdown */}
{ (() => { if (isLoading) { return 'Loading..'; } return selectedLocalizedTemplate != null && selectedLocalizedTemplate.isValid ? selectedLocalizedTemplate.title : t('Select template'); })() } { templateSummaries != null && templateSummaries.map((templateSummary) => { const templateId = constructTemplateId(templateSummary); return ( onClickHandler(templateSummary)} usersDefaultLang={usersDefaultLang} /> ); }) }

{t('preview')}

{selectedTemplateLocale != null ? selectedTemplateLocale : t('Language')} { selectedTemplateLocales != null && Array.from(selectedTemplateLocales).map((locale) => { return ( setSelectedTemplateLocale(locale)} > {locale} ); }) }
{ rendererOptions != null && selectedTemplateSummary != null && ( ) }
); }; export const TemplateModal = (): JSX.Element => { const { data: templateModalStatus, close } = useTemplateModal(); if (templateModalStatus == null) { return <>; } return ( { templateModalStatus.isOpened && ( ) } ); };