|
|
@@ -30,6 +30,10 @@ import Preview from '../PageEditor/Preview';
|
|
|
|
|
|
import { useFormatter } from './use-formatter';
|
|
|
|
|
|
+
|
|
|
+import styles from './TemplateModal.module.scss';
|
|
|
+
|
|
|
+
|
|
|
const logger = loggerFactory('growi:components:TemplateModal');
|
|
|
|
|
|
|
|
|
@@ -39,8 +43,7 @@ function constructTemplateId(templateSummary: TemplateSummary): string {
|
|
|
return `${defaultTemplate.pluginId ?? ''}_${defaultTemplate.id}`;
|
|
|
}
|
|
|
|
|
|
-type TemplateItemProps = {
|
|
|
- templateId: string,
|
|
|
+type TemplateSummaryItemProps = {
|
|
|
templateSummary: TemplateSummary,
|
|
|
selectedLocale?: string,
|
|
|
onClick?: () => void,
|
|
|
@@ -48,8 +51,7 @@ type TemplateItemProps = {
|
|
|
usersDefaultLang?: Lang,
|
|
|
}
|
|
|
|
|
|
-const TemplateItem: React.FC<TemplateItemProps> = ({
|
|
|
- templateId,
|
|
|
+const TemplateListGroupItem: React.FC<TemplateSummaryItemProps> = ({
|
|
|
templateSummary,
|
|
|
onClick,
|
|
|
isSelected,
|
|
|
@@ -62,7 +64,6 @@ const TemplateItem: React.FC<TemplateItemProps> = ({
|
|
|
|
|
|
return (
|
|
|
<a
|
|
|
- key={templateId}
|
|
|
className={`list-group-item list-group-item-action ${isSelected ? 'active' : ''}`}
|
|
|
onClick={onClick}
|
|
|
aria-current="true"
|
|
|
@@ -76,37 +77,29 @@ const TemplateItem: React.FC<TemplateItemProps> = ({
|
|
|
);
|
|
|
};
|
|
|
|
|
|
-type TemplateMenuProps = {
|
|
|
- templateSummaries: TemplateSummary[],
|
|
|
- onClickHandler: (templateSummary: TemplateSummary) => void,
|
|
|
- usersDefaultLang?: Lang,
|
|
|
- selectedTemplateSummary?: TemplateSummary,
|
|
|
-}
|
|
|
|
|
|
-const TemplateMenu: React.FC<TemplateMenuProps> = ({
|
|
|
- templateSummaries,
|
|
|
- onClickHandler,
|
|
|
+const TemplateDropdownItem: React.FC<TemplateSummaryItemProps> = ({
|
|
|
+ templateSummary,
|
|
|
+ onClick,
|
|
|
usersDefaultLang,
|
|
|
- selectedTemplateSummary,
|
|
|
}) => {
|
|
|
+
|
|
|
+ const localizedTemplate = getLocalizedTemplate(templateSummary, usersDefaultLang);
|
|
|
+ const templateLocales = extractSupportedLocales(templateSummary);
|
|
|
+
|
|
|
+ assert(localizedTemplate?.isValid);
|
|
|
+
|
|
|
return (
|
|
|
- <>
|
|
|
- {templateSummaries.map((templateSummary) => {
|
|
|
- const templateId = constructTemplateId(templateSummary);
|
|
|
- const isSelected = selectedTemplateSummary != null && constructTemplateId(selectedTemplateSummary) === templateId;
|
|
|
-
|
|
|
- return (
|
|
|
- <TemplateItem
|
|
|
- key={templateId}
|
|
|
- templateId={templateId}
|
|
|
- templateSummary={templateSummary}
|
|
|
- onClick={() => onClickHandler(templateSummary)}
|
|
|
- isSelected={isSelected}
|
|
|
- usersDefaultLang={usersDefaultLang}
|
|
|
- />
|
|
|
- );
|
|
|
- })}
|
|
|
- </>
|
|
|
+ <DropdownItem
|
|
|
+ onClick={onClick}
|
|
|
+ className="px-4 py-3"
|
|
|
+ >
|
|
|
+ <h4 className="mb-1 text-wrap">{localizedTemplate.title}</h4>
|
|
|
+ <p className="mb-1 text-wrap">{localizedTemplate.desc}</p>
|
|
|
+ { templateLocales != null && Array.from(templateLocales).map(locale => (
|
|
|
+ <span key={locale} className="badge border rounded-pill text-muted mr-1">{locale}</span>
|
|
|
+ ))}
|
|
|
+ </DropdownItem>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
@@ -122,7 +115,7 @@ const TemplateModalSubstance = (props: TemplateModalSubstanceProps): JSX.Element
|
|
|
|
|
|
const { data: personalSettingsInfo } = usePersonalSettings();
|
|
|
const { data: rendererOptions } = usePreviewOptions();
|
|
|
- const { data: templateSummaries } = useSWRxTemplates();
|
|
|
+ const { data: templateSummaries, isLoading } = useSWRxTemplates();
|
|
|
|
|
|
const [selectedTemplateSummary, setSelectedTemplateSummary] = useState<TemplateSummary>();
|
|
|
const [selectedTemplateLocale, setSelectedTemplateLocale] = useState<string>();
|
|
|
@@ -175,12 +168,8 @@ const TemplateModalSubstance = (props: TemplateModalSubstanceProps): JSX.Element
|
|
|
}
|
|
|
}, [templateModalStatus.isOpened]);
|
|
|
|
|
|
- if (templateSummaries == null) {
|
|
|
- return <></>;
|
|
|
- }
|
|
|
-
|
|
|
return (
|
|
|
- <>
|
|
|
+ <div data-testid='template-modal'>
|
|
|
<ModalHeader tag="h4" toggle={close} className="bg-primary text-light">
|
|
|
{t('template.modal_label.Select template')}
|
|
|
</ModalHeader>
|
|
|
@@ -188,32 +177,59 @@ const TemplateModalSubstance = (props: TemplateModalSubstanceProps): JSX.Element
|
|
|
<div className="row">
|
|
|
{/* List Group */}
|
|
|
<div className="d-none d-lg-block col-lg-4">
|
|
|
+
|
|
|
+ { isLoading && (
|
|
|
+ <div className='h-100 d-flex justify-content-center align-items-center'>
|
|
|
+ <i className="fa fa-2x fa-spinner fa-pulse text-muted mx-auto"></i>
|
|
|
+ </div>
|
|
|
+ ) }
|
|
|
+
|
|
|
<div className="list-group">
|
|
|
- <TemplateMenu
|
|
|
- templateSummaries={templateSummaries}
|
|
|
- onClickHandler={onClickHandler}
|
|
|
- usersDefaultLang={usersDefaultLang}
|
|
|
- selectedTemplateSummary={selectedTemplateSummary}
|
|
|
- />
|
|
|
+ { templateSummaries != null && templateSummaries.map((templateSummary) => {
|
|
|
+ const templateId = constructTemplateId(templateSummary);
|
|
|
+ const isSelected = selectedTemplateSummary != null && constructTemplateId(selectedTemplateSummary) === templateId;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <TemplateListGroupItem
|
|
|
+ key={templateId}
|
|
|
+ templateSummary={templateSummary}
|
|
|
+ onClick={() => onClickHandler(templateSummary)}
|
|
|
+ isSelected={isSelected}
|
|
|
+ usersDefaultLang={usersDefaultLang}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ }) }
|
|
|
</div>
|
|
|
</div>
|
|
|
{/* Dropdown */}
|
|
|
<div className='d-lg-none col mb-3'>
|
|
|
<UncontrolledDropdown>
|
|
|
- <DropdownToggle caret type="button" outline className='w-100 text-right'>
|
|
|
+ <DropdownToggle caret type="button" outline className='w-100 text-right' disabled={isLoading}>
|
|
|
<span className="float-left">
|
|
|
- {selectedLocalizedTemplate != null && selectedLocalizedTemplate.isValid
|
|
|
- ? selectedLocalizedTemplate.title
|
|
|
- : t('Select template')}
|
|
|
+ { (() => {
|
|
|
+ if (isLoading) {
|
|
|
+ return 'Loading..';
|
|
|
+ }
|
|
|
+
|
|
|
+ return selectedLocalizedTemplate != null && selectedLocalizedTemplate.isValid
|
|
|
+ ? selectedLocalizedTemplate.title
|
|
|
+ : t('Select template');
|
|
|
+ })() }
|
|
|
</span>
|
|
|
</DropdownToggle>
|
|
|
- <DropdownMenu role="menu" className='p-0'>
|
|
|
- <TemplateMenu
|
|
|
- templateSummaries={templateSummaries}
|
|
|
- onClickHandler={onClickHandler}
|
|
|
- usersDefaultLang={usersDefaultLang}
|
|
|
- selectedTemplateSummary={selectedTemplateSummary}
|
|
|
- />
|
|
|
+ <DropdownMenu role="menu" className={`p-0 ${styles['dm-templates']}`}>
|
|
|
+ { templateSummaries != null && templateSummaries.map((templateSummary) => {
|
|
|
+ const templateId = constructTemplateId(templateSummary);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <TemplateDropdownItem
|
|
|
+ key={templateId}
|
|
|
+ templateSummary={templateSummary}
|
|
|
+ onClick={() => onClickHandler(templateSummary)}
|
|
|
+ usersDefaultLang={usersDefaultLang}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ }) }
|
|
|
</DropdownMenu>
|
|
|
</UncontrolledDropdown>
|
|
|
</div>
|
|
|
@@ -263,7 +279,7 @@ const TemplateModalSubstance = (props: TemplateModalSubstanceProps): JSX.Element
|
|
|
{t('commons:Insert')}
|
|
|
</button>
|
|
|
</ModalFooter>
|
|
|
- </>
|
|
|
+ </div>
|
|
|
);
|
|
|
};
|
|
|
|