fix: 优化附件显示 (#1545)
Co-authored-by: sealday <zhanglin@daoyoucloud.com> Reviewed-on: daoyoucloud/tachybase#1545 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: wjh <wwwjh0710@163.com> Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
parent
2b698c6c3d
commit
2e15125445
@ -18,6 +18,7 @@ import { i18n } from '../i18n';
|
|||||||
import { CSSVariableProvider } from '../style/css-variable';
|
import { CSSVariableProvider } from '../style/css-variable';
|
||||||
import { AntdAppProvider, GlobalThemeProvider } from '../style/theme';
|
import { AntdAppProvider, GlobalThemeProvider } from '../style/theme';
|
||||||
import { AppSchemaComponentProvider } from './AppSchemaComponentProvider';
|
import { AppSchemaComponentProvider } from './AppSchemaComponentProvider';
|
||||||
|
import { AttachmentPreviewManager, PluginAttachmentItemsOptions } from './AttachmentPreviewManager';
|
||||||
import { AppComponent, BlankComponent, defaultAppComponents } from './components';
|
import { AppComponent, BlankComponent, defaultAppComponents } from './components';
|
||||||
import { NoticeManager } from './NoticesManager';
|
import { NoticeManager } from './NoticesManager';
|
||||||
import type { Plugin } from './Plugin';
|
import type { Plugin } from './Plugin';
|
||||||
@ -63,6 +64,7 @@ export interface ApplicationOptions {
|
|||||||
devDynamicImport?: DevDynamicImport;
|
devDynamicImport?: DevDynamicImport;
|
||||||
dataSourceManager?: DataSourceManagerOptions;
|
dataSourceManager?: DataSourceManagerOptions;
|
||||||
pluginMenuItems?: Record<string, PluginItemsOptions>;
|
pluginMenuItems?: Record<string, PluginItemsOptions>;
|
||||||
|
attachmentItem?: Record<string, PluginAttachmentItemsOptions>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Application {
|
export class Application {
|
||||||
@ -89,7 +91,7 @@ export class Application {
|
|||||||
public dataSourceManager: DataSourceManager;
|
public dataSourceManager: DataSourceManager;
|
||||||
public noticeManager: NoticeManager;
|
public noticeManager: NoticeManager;
|
||||||
public pluginContextMenu: PluginContextMenu;
|
public pluginContextMenu: PluginContextMenu;
|
||||||
|
public AttachmentPreviewManager: AttachmentPreviewManager;
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
@ -137,6 +139,7 @@ export class Application {
|
|||||||
this.addRoutes();
|
this.addRoutes();
|
||||||
this.name = this.options.name || getSubAppName(options.publicPath) || 'main';
|
this.name = this.options.name || getSubAppName(options.publicPath) || 'main';
|
||||||
this.pluginContextMenu = new PluginContextMenu(options.pluginMenuItems, this);
|
this.pluginContextMenu = new PluginContextMenu(options.pluginMenuItems, this);
|
||||||
|
this.AttachmentPreviewManager = new AttachmentPreviewManager(options.attachmentItem, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private initRequireJs() {
|
private initRequireJs() {
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import type { Application } from './Application';
|
||||||
|
|
||||||
|
export interface PluginAttachmentItemsOptions {
|
||||||
|
key: string;
|
||||||
|
type: string;
|
||||||
|
viewComponet: (any) => JSX.Element;
|
||||||
|
checkedComponent: (any) => JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AttachmentPreviewManager {
|
||||||
|
protected attachmentItem: Record<string, PluginAttachmentItemsOptions> = {};
|
||||||
|
public app: Application;
|
||||||
|
|
||||||
|
constructor(_pluginAttachmentItems: Record<string, PluginAttachmentItemsOptions>, app: Application) {
|
||||||
|
this.app = app;
|
||||||
|
Object.entries(_pluginAttachmentItems || {}).forEach(([name, PluginItemsOptions]) => {
|
||||||
|
this.add(PluginItemsOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get() {
|
||||||
|
return this.attachmentItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTypeItem(key) {
|
||||||
|
return this.attachmentItem[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
add(options: PluginAttachmentItemsOptions) {
|
||||||
|
this.attachmentItem[options.key] = options;
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,10 @@ export class Plugin<T = any> {
|
|||||||
return this.app.dataSourceManager;
|
return this.app.dataSourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get AttachmentPreviewManager() {
|
||||||
|
return this.app.AttachmentPreviewManager;
|
||||||
|
}
|
||||||
|
|
||||||
async afterAdd() {}
|
async afterAdd() {}
|
||||||
|
|
||||||
async beforeLoad() {}
|
async beforeLoad() {}
|
||||||
|
@ -12,3 +12,4 @@ export * from './NoticesManager';
|
|||||||
export * from './hoc';
|
export * from './hoc';
|
||||||
export { ApplicationContext } from './context';
|
export { ApplicationContext } from './context';
|
||||||
export * from './PluginContextMenu';
|
export * from './PluginContextMenu';
|
||||||
|
export * from './AttachmentPreviewManager';
|
||||||
|
@ -0,0 +1,146 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { FileFilled } from '@ant-design/icons';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
|
||||||
|
export const imagejpeg = {
|
||||||
|
key: 'image/jpeg',
|
||||||
|
type: 'image/jpeg',
|
||||||
|
viewComponet: (props) => {
|
||||||
|
const { file, prefixCls } = props;
|
||||||
|
return (
|
||||||
|
file.imageUrl && (
|
||||||
|
<img
|
||||||
|
src={`${file.imageUrl}${file.thumbnailRule || ''}`}
|
||||||
|
style={{ width: '100%', height: '100%' }}
|
||||||
|
alt={file.title}
|
||||||
|
className={`${prefixCls}-list-item-image`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
checkedComponent: (props) => {
|
||||||
|
const { file } = props;
|
||||||
|
return (
|
||||||
|
file.imageUrl && (
|
||||||
|
<img
|
||||||
|
{...props}
|
||||||
|
src={`${file.imageUrl}${file.thumbnailRule || ''}`}
|
||||||
|
alt={file.title}
|
||||||
|
style={{
|
||||||
|
width: '90%',
|
||||||
|
height: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const imagePng = {
|
||||||
|
key: 'image/png',
|
||||||
|
type: 'image/png',
|
||||||
|
viewComponet: (props) => {
|
||||||
|
const { file, prefixCls } = props;
|
||||||
|
return (
|
||||||
|
file.imageUrl && (
|
||||||
|
<img
|
||||||
|
src={`${file.imageUrl}${file.thumbnailRule || ''}`}
|
||||||
|
style={{ width: '100%', height: '100%' }}
|
||||||
|
alt={file.title}
|
||||||
|
className={`${prefixCls}-list-item-image`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
checkedComponent: (props) => {
|
||||||
|
const { file } = props;
|
||||||
|
return (
|
||||||
|
file.imageUrl && (
|
||||||
|
<img
|
||||||
|
{...props}
|
||||||
|
src={`${file.imageUrl}${file.thumbnailRule || ''}`}
|
||||||
|
alt={file.title}
|
||||||
|
style={{
|
||||||
|
width: '90%',
|
||||||
|
height: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fileDef = {
|
||||||
|
key: 'default',
|
||||||
|
type: 'default',
|
||||||
|
viewComponet: (props) => {
|
||||||
|
const { prefixCls } = props;
|
||||||
|
return (
|
||||||
|
<FileFilled
|
||||||
|
className={`${prefixCls}-list-item-image`}
|
||||||
|
style={{ width: '100%', height: '100%', lineHeight: '100%', color: '#000000' }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
checkedComponent: (props) => {
|
||||||
|
return (
|
||||||
|
<div {...props}>
|
||||||
|
<FileFilled style={{ color: '#ffffff', fontSize: '30rem', display: 'block', lineHeight: '100vh' }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filePdf = {
|
||||||
|
key: 'application/pdf',
|
||||||
|
type: 'application/pdf',
|
||||||
|
viewComponet: (props) => {
|
||||||
|
const { file, prefixCls } = props;
|
||||||
|
return (
|
||||||
|
file.imageUrl && (
|
||||||
|
<img
|
||||||
|
src={`${file.imageUrl}${file.thumbnailRule || ''}`}
|
||||||
|
style={{ width: '100%', height: '100%' }}
|
||||||
|
alt={file.title}
|
||||||
|
className={`${prefixCls}-list-item-image`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
checkedComponent: (props) => {
|
||||||
|
const { file } = props;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
style={{
|
||||||
|
maxWidth: '100%',
|
||||||
|
maxHeight: '100%',
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
background: 'white',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
overflowY: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
src={file.url}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxHeight: '90vh',
|
||||||
|
height: '90vh',
|
||||||
|
flex: '1 1 auto',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,11 @@
|
|||||||
|
import { Plugin } from '../../application/Plugin';
|
||||||
|
import { fileDef, filePdf, imagejpeg, imagePng } from './AttachmentItems';
|
||||||
|
|
||||||
|
export class AttachmentPreviewPlugin extends Plugin {
|
||||||
|
async load() {
|
||||||
|
this.app.AttachmentPreviewManager.add(imagejpeg);
|
||||||
|
this.app.AttachmentPreviewManager.add(fileDef);
|
||||||
|
this.app.AttachmentPreviewManager.add(imagePng);
|
||||||
|
this.app.AttachmentPreviewManager.add(filePdf);
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Navigate, useNavigate } from 'react-router-dom';
|
import { Navigate, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useAPIClient } from '../api-client';
|
import { useAPIClient } from '../api-client';
|
||||||
import { Application, NoticeLevel, useApp } from '../application';
|
import { Application, AttachmentPreviewManager, NoticeLevel, useApp } from '../application';
|
||||||
import { Plugin } from '../application/Plugin';
|
import { Plugin } from '../application/Plugin';
|
||||||
import { BlockSchemaComponentPlugin } from '../block-provider';
|
import { BlockSchemaComponentPlugin } from '../block-provider';
|
||||||
import { CollectionPlugin } from '../collection-manager';
|
import { CollectionPlugin } from '../collection-manager';
|
||||||
@ -23,6 +23,7 @@ import { CurrentUserProvider, CurrentUserSettingsMenuProvider } from '../user';
|
|||||||
import { ACLPlugin } from './acl';
|
import { ACLPlugin } from './acl';
|
||||||
import { AdminLayoutPlugin } from './admin-layout';
|
import { AdminLayoutPlugin } from './admin-layout';
|
||||||
import { PluginAssistant } from './assistant';
|
import { PluginAssistant } from './assistant';
|
||||||
|
import { AttachmentPreviewPlugin } from './attachment-preview';
|
||||||
import { PluginBuiltInCollections } from './built-in-collections';
|
import { PluginBuiltInCollections } from './built-in-collections';
|
||||||
import { PluginContextMenu } from './context-menu';
|
import { PluginContextMenu } from './context-menu';
|
||||||
import { RemoteDocumentTitlePlugin } from './document-title';
|
import { RemoteDocumentTitlePlugin } from './document-title';
|
||||||
@ -357,5 +358,6 @@ export class BuiltInPlugin extends Plugin {
|
|||||||
await this.app.pm.add(PluginContextMenu, { name: 'context-menu' });
|
await this.app.pm.add(PluginContextMenu, { name: 'context-menu' });
|
||||||
await this.app.pm.add(PluginAssistant, { name: 'drag-assistant' });
|
await this.app.pm.add(PluginAssistant, { name: 'drag-assistant' });
|
||||||
await this.app.pm.add(UserSettingsPlugin, { name: 'user-settings' });
|
await this.app.pm.add(UserSettingsPlugin, { name: 'user-settings' });
|
||||||
|
await this.app.pm.add(AttachmentPreviewPlugin, { name: 'attachment-preview' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Lightbox } from '@tachybase/components';
|
import { Lightbox } from '@tachybase/components';
|
||||||
import { Field, useField } from '@tachybase/schema';
|
import { Field, useField } from '@tachybase/schema';
|
||||||
import { isString } from '@tachybase/utils/client';
|
import { isString } from '@tachybase/utils/client';
|
||||||
@ -10,8 +10,10 @@ import cls from 'classnames';
|
|||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { SchemaComponent } from '../..';
|
||||||
|
import { useApp } from '../../../application/hooks';
|
||||||
import { useRecord } from '../../../record-provider';
|
import { useRecord } from '../../../record-provider';
|
||||||
import { isImage, isPdf, toArr, toImages } from './shared';
|
import { isImage, isPdf, toArr, toFileList, toImages } from './shared';
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style';
|
||||||
import type { UploadProps } from './type';
|
import type { UploadProps } from './type';
|
||||||
|
|
||||||
@ -24,23 +26,17 @@ export const ReadPretty: Composed = () => null;
|
|||||||
|
|
||||||
ReadPretty.File = function File(props: UploadProps) {
|
ReadPretty.File = function File(props: UploadProps) {
|
||||||
const { size, showCount = 0 } = props;
|
const { size, showCount = 0 } = props;
|
||||||
const { t } = useTranslation();
|
|
||||||
const record = useRecord();
|
const record = useRecord();
|
||||||
const field = useField<Field>();
|
const field = useField<Field>();
|
||||||
const value = isString(field.value) ? record : field.value;
|
const images = isString(field.value) ? record : toFileList(field.value);
|
||||||
|
const app = useApp();
|
||||||
const images = toImages(showCount === 0 ? toArr(value) : toArr(value).slice(0, showCount));
|
|
||||||
const [fileIndex, setFileIndex] = useState(0);
|
const [fileIndex, setFileIndex] = useState(0);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [fileType, setFileType] = useState<'image' | 'pdf'>();
|
|
||||||
const { wrapSSR, hashId, componentCls: prefixCls } = useStyles();
|
const { wrapSSR, hashId, componentCls: prefixCls } = useStyles();
|
||||||
const useUploadStyleVal = (useUploadStyle as any).default ? (useUploadStyle as any).default : useUploadStyle;
|
const useUploadStyleVal = (useUploadStyle as any).default ? (useUploadStyle as any).default : useUploadStyle;
|
||||||
|
const previewList = app.AttachmentPreviewManager.get();
|
||||||
// 加载 antd 的样式
|
// 加载 antd 的样式
|
||||||
useUploadStyleVal(prefixCls);
|
useUploadStyleVal(prefixCls);
|
||||||
|
|
||||||
function closeIFrameModal() {
|
|
||||||
setVisible(false);
|
|
||||||
}
|
|
||||||
return wrapSSR(
|
return wrapSSR(
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
@ -53,95 +49,49 @@ ReadPretty.File = function File(props: UploadProps) {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={cls(`${prefixCls}-list`, `${prefixCls}-list-picture-card`)}>
|
<div className={cls(`${prefixCls}-list`, `${prefixCls}-list-picture-card`)}>
|
||||||
{images.map((file) => {
|
{images.map((file, index) => {
|
||||||
const handleClick = (e) => {
|
if (size === 'small') {
|
||||||
const index = images.indexOf(file);
|
return (
|
||||||
if (isImage(file.extname)) {
|
index === 0 && (
|
||||||
e.preventDefault();
|
<ReadFile
|
||||||
e.stopPropagation();
|
file={file}
|
||||||
setVisible(true);
|
prefixCls={prefixCls}
|
||||||
setFileIndex(index);
|
// handleClick={handleClick}
|
||||||
setFileType('image');
|
size={size}
|
||||||
} else if (isPdf(file.extname)) {
|
images={images}
|
||||||
e.preventDefault();
|
setFileIndex={setFileIndex}
|
||||||
e.stopPropagation();
|
setVisible={setVisible}
|
||||||
setVisible(true);
|
preview={previewList[file?.mimetype] || previewList['default']}
|
||||||
setFileIndex(index);
|
key={index}
|
||||||
setFileType('pdf');
|
/>
|
||||||
}
|
)
|
||||||
// else {
|
);
|
||||||
// saveAs(file.url, `${file.title}${file.extname}`);
|
}
|
||||||
// }
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div
|
<ReadFile
|
||||||
key={file.name}
|
file={file}
|
||||||
className={cls(`${prefixCls}-list-picture-card-container`, `${prefixCls}-list-item-container`)}
|
prefixCls={prefixCls}
|
||||||
>
|
size={size}
|
||||||
<div
|
images={images}
|
||||||
className={cls(
|
setFileIndex={setFileIndex}
|
||||||
`${prefixCls}-list-item`,
|
setVisible={setVisible}
|
||||||
`${prefixCls}-list-item-done`,
|
preview={previewList['image/jpeg']}
|
||||||
`${prefixCls}-list-item-list-type-picture-card`,
|
key={index}
|
||||||
)}
|
/>
|
||||||
>
|
|
||||||
<div className={`${prefixCls}-list-item-info`}>
|
|
||||||
<span className={`${prefixCls}-span`}>
|
|
||||||
<a
|
|
||||||
className={`${prefixCls}-list-item-thumbnail`}
|
|
||||||
href={file.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
{file.imageUrl && (
|
|
||||||
<img
|
|
||||||
src={`${file.imageUrl}${file.thumbnailRule || ''}`}
|
|
||||||
alt={file.title}
|
|
||||||
className={`${prefixCls}-list-item-image`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className={`${prefixCls}-list-item-name`}
|
|
||||||
title={file.title}
|
|
||||||
href={file.url}
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
{file.title}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{size !== 'small' && (
|
|
||||||
<span className={`${prefixCls}-list-item-actions`}>
|
|
||||||
<Space size={3}>
|
|
||||||
<Button
|
|
||||||
size={'small'}
|
|
||||||
type={'text'}
|
|
||||||
icon={<DownloadOutlined />}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
saveAs(file.url, `${file.title}${file.extname}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{visible && fileType === 'image' && (
|
{visible && (
|
||||||
<Lightbox
|
<Lightbox
|
||||||
// discourageDownloads={true}
|
// discourageDownloads={true}
|
||||||
mainSrc={images[fileIndex]?.imageUrl}
|
mainSrc={images[fileIndex]?.imageUrl}
|
||||||
nextSrc={images[(fileIndex + 1) % images.length]?.imageUrl}
|
nextSrc={images[(fileIndex + 1) % images.length]?.imageUrl}
|
||||||
prevSrc={images[(fileIndex + images.length - 1) % images.length]?.imageUrl}
|
prevSrc={images[(fileIndex + images.length - 1) % images.length]?.imageUrl}
|
||||||
|
mainFile={images[fileIndex]}
|
||||||
|
nextFile={images[(fileIndex + 1) % images.length]}
|
||||||
|
prevFile={images[(fileIndex + images.length - 1) % images.length]}
|
||||||
|
previewList={previewList}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
onCloseRequest={(e) => {
|
onCloseRequest={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -171,59 +121,6 @@ ReadPretty.File = function File(props: UploadProps) {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{visible && fileType === 'pdf' && (
|
|
||||||
<Modal
|
|
||||||
open={visible}
|
|
||||||
title={'PDF - ' + images[fileIndex].title}
|
|
||||||
onCancel={closeIFrameModal}
|
|
||||||
footer={[
|
|
||||||
<Button
|
|
||||||
key={'download'}
|
|
||||||
style={{
|
|
||||||
textTransform: 'capitalize',
|
|
||||||
}}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
const file = images[fileIndex];
|
|
||||||
saveAs(file.url, `${file.title}${file.extname}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('download')}
|
|
||||||
</Button>,
|
|
||||||
<Button key={'close'} onClick={closeIFrameModal} style={{ textTransform: 'capitalize' }}>
|
|
||||||
{t('close')}
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
width={'85vw'}
|
|
||||||
centered={true}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
padding: '8px',
|
|
||||||
maxWidth: '100%',
|
|
||||||
maxHeight: 'calc(100vh - 256px)',
|
|
||||||
height: '90vh',
|
|
||||||
width: '100%',
|
|
||||||
background: 'white',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
overflowY: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<iframe
|
|
||||||
src={images[fileIndex].url}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
maxHeight: '90vh',
|
|
||||||
flex: '1 1 auto',
|
|
||||||
}}
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
)}
|
|
||||||
</div>,
|
</div>,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -242,3 +139,87 @@ ReadPretty.Upload = function Upload() {
|
|||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ReadFile = ({ file, prefixCls, size, images, setFileIndex, setVisible, preview }) => {
|
||||||
|
const { viewComponet } = preview;
|
||||||
|
const handleClick = (e) => {
|
||||||
|
const index = images.indexOf(file);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setVisible(true);
|
||||||
|
setFileIndex(index);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={file.name}
|
||||||
|
className={cls(`${prefixCls}-list-picture-card-container`, `${prefixCls}-list-item-container`)}
|
||||||
|
style={{ position: 'relative' }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cls(
|
||||||
|
`${prefixCls}-list-item`,
|
||||||
|
`${prefixCls}-list-item-done`,
|
||||||
|
`${prefixCls}-list-item-list-type-picture-card`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={`${prefixCls}-list-item-info`}>
|
||||||
|
<span className={`${prefixCls}-span`}>
|
||||||
|
<a
|
||||||
|
className={`${prefixCls}-list-item-thumbnail`}
|
||||||
|
href={file.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
onClick={handleClick}
|
||||||
|
style={{ lineHeight: '100%' }}
|
||||||
|
>
|
||||||
|
{viewComponet({ images, size, prefixCls, file, setFileIndex, setVisible })}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={`${prefixCls}-list-item-name`}
|
||||||
|
title={file.title}
|
||||||
|
href={file.url}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{file.title}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{size !== 'small' && (
|
||||||
|
<span className={`${prefixCls}-list-item-actions`}>
|
||||||
|
<Space size={3}>
|
||||||
|
<Button
|
||||||
|
size={'small'}
|
||||||
|
type={'text'}
|
||||||
|
icon={<DownloadOutlined />}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
saveAs(file.url, `${file.title}${file.extname}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{images.length > 1 && size === 'small' && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '0',
|
||||||
|
right: '0',
|
||||||
|
backgroundColor: '#9b999992',
|
||||||
|
width: '50%',
|
||||||
|
height: '30%',
|
||||||
|
lineHeight: '30%',
|
||||||
|
borderRadius: '40%',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -8,6 +8,7 @@ import cls from 'classnames';
|
|||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { useApp } from '../../../application';
|
||||||
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
import { useProps } from '../../hooks/useProps';
|
import { useProps } from '../../hooks/useProps';
|
||||||
import { ReadPretty } from './ReadPretty';
|
import { ReadPretty } from './ReadPretty';
|
||||||
@ -29,18 +30,15 @@ Upload.Attachment = connect((props: UploadProps) => {
|
|||||||
const { disabled, multiple, value, onChange } = props;
|
const { disabled, multiple, value, onChange } = props;
|
||||||
const [fileList, setFileList] = useState<any[]>([]);
|
const [fileList, setFileList] = useState<any[]>([]);
|
||||||
const [sync, setSync] = useState(true);
|
const [sync, setSync] = useState(true);
|
||||||
|
const app = useApp();
|
||||||
|
const previewList = app.AttachmentPreviewManager.get();
|
||||||
const images = fileList;
|
const images = fileList;
|
||||||
const [fileIndex, setFileIndex] = useState(0);
|
const [fileIndex, setFileIndex] = useState(0);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [fileType, setFileType] = useState<'image' | 'pdf'>();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const uploadProps = useUploadProps({ ...props });
|
const uploadProps = useUploadProps({ ...props });
|
||||||
const { wrapSSR, hashId, componentCls: prefixCls } = useStyles();
|
const { wrapSSR, hashId, componentCls: prefixCls } = useStyles();
|
||||||
const internalFileList = useRef([]);
|
const internalFileList = useRef([]);
|
||||||
|
|
||||||
function closeIFrameModal() {
|
|
||||||
setVisible(false);
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sync) {
|
if (sync) {
|
||||||
const fileList = toFileList(value);
|
const fileList = toFileList(value);
|
||||||
@ -53,104 +51,23 @@ Upload.Attachment = connect((props: UploadProps) => {
|
|||||||
<div>
|
<div>
|
||||||
<div className={cls(`${prefixCls}-wrapper`, `${prefixCls}-picture-card-wrapper`, 'nb-upload', hashId)}>
|
<div className={cls(`${prefixCls}-wrapper`, `${prefixCls}-picture-card-wrapper`, 'nb-upload', hashId)}>
|
||||||
<div className={cls(`${prefixCls}-list`, `${prefixCls}-list-picture-card`)}>
|
<div className={cls(`${prefixCls}-list`, `${prefixCls}-list-picture-card`)}>
|
||||||
{fileList.map((file) => {
|
{fileList.map((file, index) => {
|
||||||
const handleClick = (e) => {
|
const fileProps = {
|
||||||
e.preventDefault();
|
file,
|
||||||
e.stopPropagation();
|
prefixCls,
|
||||||
const index = fileList.indexOf(file);
|
fileList,
|
||||||
if (isImage(file.extname)) {
|
setFileIndex,
|
||||||
setFileType('image');
|
setVisible,
|
||||||
setVisible(true);
|
disabled,
|
||||||
setFileIndex(index);
|
t,
|
||||||
} else if (isPdf(file.extname)) {
|
setSync,
|
||||||
setVisible(true);
|
setFileList,
|
||||||
setFileIndex(index);
|
multiple,
|
||||||
setFileType('pdf');
|
onChange,
|
||||||
} else {
|
internalFileList,
|
||||||
saveAs(file.url, `${file.title}${file.extname}`);
|
preview: previewList[file?.mimetype] || previewList['default'],
|
||||||
}
|
|
||||||
};
|
};
|
||||||
return (
|
return <UploadReadFile {...fileProps} key={index} />;
|
||||||
<div
|
|
||||||
key={file.uid || file.id}
|
|
||||||
className={`${prefixCls}-list-picture-card-container ${prefixCls}-list-item-container`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={cls(
|
|
||||||
`${prefixCls}-list-item`,
|
|
||||||
`${prefixCls}-list-item-done`,
|
|
||||||
`${prefixCls}-list-item-list-type-picture-card`,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className={`${prefixCls}-list-item-info`}>
|
|
||||||
<span className={`${prefixCls}-span`}>
|
|
||||||
<a
|
|
||||||
className={`${prefixCls}-list-item-thumbnail`}
|
|
||||||
href={file.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
{file.imageUrl && (
|
|
||||||
<img src={file.imageUrl} alt={file.title} className={`${prefixCls}-list-item-image`} />
|
|
||||||
)}
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className={`${prefixCls}-list-item-name`}
|
|
||||||
title={file.title}
|
|
||||||
href={file.url}
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
{file.status === 'uploading' ? t('Uploading') : file.title}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span className={`${prefixCls}-list-item-actions`}>
|
|
||||||
<Space size={3}>
|
|
||||||
<Button
|
|
||||||
size={'small'}
|
|
||||||
type={'text'}
|
|
||||||
icon={<DownloadOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
saveAs(file.url, `${file.title}${file.extname}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{!disabled && (
|
|
||||||
<Button
|
|
||||||
size={'small'}
|
|
||||||
type={'text'}
|
|
||||||
icon={<DeleteOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
setSync(false);
|
|
||||||
setFileList((prevFileList) => {
|
|
||||||
if (!multiple) {
|
|
||||||
onChange?.(null as any);
|
|
||||||
setSync(true);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const index = prevFileList.indexOf(file);
|
|
||||||
prevFileList.splice(index, 1);
|
|
||||||
internalFileList.current = internalFileList.current.filter(
|
|
||||||
(item) => item.uid !== file.uid,
|
|
||||||
);
|
|
||||||
onChange?.(toValue([...prevFileList]));
|
|
||||||
return [...prevFileList];
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Space>
|
|
||||||
</span>
|
|
||||||
{file.status === 'uploading' && (
|
|
||||||
<div className={`${prefixCls}-list-item-progress`}>
|
|
||||||
<Progress strokeWidth={2} type={'line'} showInfo={false} percent={file.percent} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
{!disabled && (multiple || toArr(value).length < 1) && (
|
{!disabled && (multiple || toArr(value).length < 1) && (
|
||||||
<div className={cls(`${prefixCls}-list-picture-card-container`, `${prefixCls}-list-item-container`)}>
|
<div className={cls(`${prefixCls}-list-picture-card-container`, `${prefixCls}-list-item-container`)}>
|
||||||
@ -198,12 +115,16 @@ Upload.Attachment = connect((props: UploadProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 预览图片的弹框 */}
|
{/* 预览图片的弹框 */}
|
||||||
{visible && fileType === 'image' && (
|
{visible && (
|
||||||
<Lightbox
|
<Lightbox
|
||||||
// discourageDownloads={true}
|
// discourageDownloads={true}
|
||||||
mainSrc={images[fileIndex]?.imageUrl}
|
mainSrc={images[fileIndex]?.imageUrl}
|
||||||
nextSrc={images[(fileIndex + 1) % images.length]?.imageUrl}
|
nextSrc={images[(fileIndex + 1) % images.length]?.imageUrl}
|
||||||
prevSrc={images[(fileIndex + images.length - 1) % images.length]?.imageUrl}
|
prevSrc={images[(fileIndex + images.length - 1) % images.length]?.imageUrl}
|
||||||
|
mainFile={images[fileIndex]}
|
||||||
|
nextFile={images[(fileIndex + 1) % images.length]}
|
||||||
|
prevFile={images[(fileIndex + images.length - 1) % images.length]}
|
||||||
|
previewList={previewList}
|
||||||
onCloseRequest={() => setVisible(false)}
|
onCloseRequest={() => setVisible(false)}
|
||||||
onMovePrevRequest={() => setFileIndex((fileIndex + images.length - 1) % images.length)}
|
onMovePrevRequest={() => setFileIndex((fileIndex + images.length - 1) % images.length)}
|
||||||
onMoveNextRequest={() => setFileIndex((fileIndex + 1) % images.length)}
|
onMoveNextRequest={() => setFileIndex((fileIndex + 1) % images.length)}
|
||||||
@ -227,59 +148,6 @@ Upload.Attachment = connect((props: UploadProps) => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{visible && fileType === 'pdf' && (
|
|
||||||
<Modal
|
|
||||||
open={visible}
|
|
||||||
title={'PDF - ' + images[fileIndex].title}
|
|
||||||
onCancel={closeIFrameModal}
|
|
||||||
footer={[
|
|
||||||
<Button
|
|
||||||
key="download"
|
|
||||||
style={{
|
|
||||||
textTransform: 'capitalize',
|
|
||||||
}}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
const file = images[fileIndex];
|
|
||||||
saveAs(file.url, `${file.title}${file.extname}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('download')}
|
|
||||||
</Button>,
|
|
||||||
<Button key="close" onClick={closeIFrameModal} style={{ textTransform: 'capitalize' }}>
|
|
||||||
{t('close')}
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
width={'85vw'}
|
|
||||||
centered={true}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
padding: '8px',
|
|
||||||
maxWidth: '100%',
|
|
||||||
maxHeight: 'calc(100vh - 256px)',
|
|
||||||
height: '90vh',
|
|
||||||
width: '100%',
|
|
||||||
background: 'white',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
overflowY: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<iframe
|
|
||||||
src={images[fileIndex].url}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
maxHeight: '90vh',
|
|
||||||
flex: '1 1 auto',
|
|
||||||
}}
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
)}
|
|
||||||
</div>,
|
</div>,
|
||||||
);
|
);
|
||||||
}, mapReadPretty(ReadPretty.File));
|
}, mapReadPretty(ReadPretty.File));
|
||||||
@ -355,3 +223,107 @@ function updateFileList(file: UploadFile, fileList: (UploadFile | Readonly<Uploa
|
|||||||
}
|
}
|
||||||
return nextFileList;
|
return nextFileList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const UploadReadFile = (props) => {
|
||||||
|
const {
|
||||||
|
file,
|
||||||
|
prefixCls,
|
||||||
|
fileList,
|
||||||
|
setFileIndex,
|
||||||
|
setVisible,
|
||||||
|
disabled,
|
||||||
|
t,
|
||||||
|
setSync,
|
||||||
|
setFileList,
|
||||||
|
multiple,
|
||||||
|
onChange,
|
||||||
|
internalFileList,
|
||||||
|
preview,
|
||||||
|
} = props;
|
||||||
|
const handleClick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const index = fileList.indexOf(file);
|
||||||
|
setVisible(true);
|
||||||
|
setFileIndex(index);
|
||||||
|
};
|
||||||
|
const { viewComponet } = preview;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={file.uid || file.id}
|
||||||
|
className={`${prefixCls}-list-picture-card-container ${prefixCls}-list-item-container`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cls(
|
||||||
|
`${prefixCls}-list-item`,
|
||||||
|
`${prefixCls}-list-item-done`,
|
||||||
|
`${prefixCls}-list-item-list-type-picture-card`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={`${prefixCls}-list-item-info`}>
|
||||||
|
<span className={`${prefixCls}-span`}>
|
||||||
|
<a
|
||||||
|
className={`${prefixCls}-list-item-thumbnail`}
|
||||||
|
href={file.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
onClick={handleClick}
|
||||||
|
style={{ lineHeight: '100%' }}
|
||||||
|
>
|
||||||
|
{viewComponet({ prefixCls, file, setFileIndex, setVisible })}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={`${prefixCls}-list-item-name`}
|
||||||
|
title={file.title}
|
||||||
|
href={file.url}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{file.status === 'uploading' ? t('Uploading') : file.title}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className={`${prefixCls}-list-item-actions`}>
|
||||||
|
<Space size={3}>
|
||||||
|
<Button
|
||||||
|
size={'small'}
|
||||||
|
type={'text'}
|
||||||
|
icon={<DownloadOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
saveAs(file.url, `${file.title}${file.extname}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!disabled && (
|
||||||
|
<Button
|
||||||
|
size={'small'}
|
||||||
|
type={'text'}
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
setSync(false);
|
||||||
|
setFileList((prevFileList) => {
|
||||||
|
if (!multiple) {
|
||||||
|
onChange?.(null as any);
|
||||||
|
setSync(true);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const index = prevFileList.indexOf(file);
|
||||||
|
prevFileList.splice(index, 1);
|
||||||
|
internalFileList.current = internalFileList.current.filter((item) => item.uid !== file.uid);
|
||||||
|
onChange?.(toValue([...prevFileList]));
|
||||||
|
return [...prevFileList];
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</span>
|
||||||
|
{file.status === 'uploading' && (
|
||||||
|
<div className={`${prefixCls}-list-item-progress`}>
|
||||||
|
<Progress strokeWidth={2} type={'line'} showInfo={false} percent={file.percent} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -25,6 +25,8 @@ import { getHighestSafeWindowContext, getWindowHeight, getWindowWidth, translate
|
|||||||
|
|
||||||
import './style.css';
|
import './style.css';
|
||||||
|
|
||||||
|
import { css } from '@tachybase/client';
|
||||||
|
|
||||||
class ReactImageLightbox extends Component {
|
class ReactImageLightbox extends Component {
|
||||||
static isTargetMatchImage(target) {
|
static isTargetMatchImage(target) {
|
||||||
return target && /ril-image-current/.test(target.className);
|
return target && /ril-image-current/.test(target.className);
|
||||||
@ -282,10 +284,9 @@ class ReactImageLightbox extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get info for the best suited image to display with the given srcType
|
// Get info for the best suited image to display with the given srcType
|
||||||
getBestImageForType(srcType) {
|
getBestImageForType(srcType, fileSrc?) {
|
||||||
let imageSrc = this.props[srcType];
|
let imageSrc = fileSrc || this.props[srcType];
|
||||||
let fitSizes = {};
|
let fitSizes = {};
|
||||||
|
|
||||||
if (this.isImageLoaded(imageSrc)) {
|
if (this.isImageLoaded(imageSrc)) {
|
||||||
// Use full-size image if available
|
// Use full-size image if available
|
||||||
fitSizes = this.getFitSizes(this.imageCache[imageSrc].width, this.imageCache[imageSrc].height);
|
fitSizes = this.getFitSizes(this.imageCache[imageSrc].width, this.imageCache[imageSrc].height);
|
||||||
@ -394,6 +395,30 @@ class ReactImageLightbox extends Component {
|
|||||||
name: 'prevSrcThumbnail',
|
name: 'prevSrcThumbnail',
|
||||||
keyEnding: `t${this.keyCounter - 1}`,
|
keyEnding: `t${this.keyCounter - 1}`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'mainFile',
|
||||||
|
keyEnding: `i${this.keyCounter}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mainSrcThumbnail',
|
||||||
|
keyEnding: `t${this.keyCounter}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nextFile',
|
||||||
|
keyEnding: `i${this.keyCounter + 1}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nextFileThumbnail',
|
||||||
|
keyEnding: `t${this.keyCounter + 1}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'prevFile',
|
||||||
|
keyEnding: `i${this.keyCounter - 1}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'prevFileThumbnail',
|
||||||
|
keyEnding: `t${this.keyCounter - 1}`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1175,6 +1200,10 @@ class ReactImageLightbox extends Component {
|
|||||||
imageTitle,
|
imageTitle,
|
||||||
nextSrc,
|
nextSrc,
|
||||||
prevSrc,
|
prevSrc,
|
||||||
|
mainFile,
|
||||||
|
nextFile,
|
||||||
|
prevFile,
|
||||||
|
previewList,
|
||||||
toolbarButtons,
|
toolbarButtons,
|
||||||
reactModalStyle,
|
reactModalStyle,
|
||||||
onAfterOpen,
|
onAfterOpen,
|
||||||
@ -1203,17 +1232,21 @@ class ReactImageLightbox extends Component {
|
|||||||
|
|
||||||
// Images to be displayed
|
// Images to be displayed
|
||||||
const images = [];
|
const images = [];
|
||||||
const addImage = (srcType, imageClass, transforms) => {
|
|
||||||
// Ignore types that have no source defined for their full size image
|
|
||||||
if (!this.props[srcType]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const bestImageInfo = this.getBestImageForType(srcType);
|
|
||||||
|
|
||||||
|
const addImage = (srcType, imageClass, transforms) => {
|
||||||
|
const fieldType = this.props[srcType]?.mimetype;
|
||||||
|
const { checkedComponent } = previewList[fieldType] || previewList['default'];
|
||||||
|
const bestImageInfo = (this.props[srcType]?.mimetype as string).includes('image')
|
||||||
|
? this.getBestImageForType(srcType, this.props[srcType]?.imageUrl)
|
||||||
|
: {};
|
||||||
const imageStyle = {
|
const imageStyle = {
|
||||||
...transitionStyle,
|
...transitionStyle,
|
||||||
...ReactImageLightbox.getTransform({
|
...ReactImageLightbox.getTransform({
|
||||||
...transforms,
|
...transforms,
|
||||||
|
height: boxSize.height - 20,
|
||||||
|
width: boxSize.width - 20,
|
||||||
|
targetWidth: boxSize.width - 50,
|
||||||
|
targeHeight: boxSize.height - 50,
|
||||||
...bestImageInfo,
|
...bestImageInfo,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
@ -1221,99 +1254,43 @@ class ReactImageLightbox extends Component {
|
|||||||
if (zoomLevel > MIN_ZOOM_LEVEL) {
|
if (zoomLevel > MIN_ZOOM_LEVEL) {
|
||||||
imageStyle.cursor = 'move';
|
imageStyle.cursor = 'move';
|
||||||
}
|
}
|
||||||
|
images.push(
|
||||||
// support IE 9 and 11
|
<div
|
||||||
const hasTrueValue = (object) => Object.keys(object).some((key) => object[key]);
|
className={`${imageClass} ril__image`}
|
||||||
|
style={{
|
||||||
// when error on one of the loads then push custom error stuff
|
...imageStyle,
|
||||||
if (bestImageInfo === null && hasTrueValue(loadErrorStatus)) {
|
width: bestImageInfo?.width || '90%',
|
||||||
images.push(
|
height: '90%',
|
||||||
<div
|
display: 'flex',
|
||||||
className={`${imageClass} ril__image ril-errored`}
|
justifyContent: 'center',
|
||||||
style={imageStyle}
|
alignItems: 'center',
|
||||||
key={this.props[srcType] + keyEndings[srcType]}
|
}}
|
||||||
>
|
>
|
||||||
<div className="ril__errorContainer">{this.props.imageLoadErrorMessage}</div>
|
{checkedComponent({
|
||||||
</div>,
|
key: `${fieldType + keyEndings[srcType]}`,
|
||||||
);
|
onDoubleClick: this.handleImageDoubleClick,
|
||||||
|
onWheel: this.handleImageMouseWheel,
|
||||||
return;
|
onDragStart: (e) => e.preventDefault(),
|
||||||
}
|
file: this.props[srcType],
|
||||||
if (bestImageInfo === null) {
|
bestImageInfo,
|
||||||
const loadingIcon =
|
})}
|
||||||
loader !== undefined ? (
|
</div>,
|
||||||
loader
|
);
|
||||||
) : (
|
|
||||||
<div className="ril-loading-circle ril__loadingCircle ril__loadingContainer__icon">
|
|
||||||
{[...new Array(12)].map((_, index) => (
|
|
||||||
<div
|
|
||||||
// eslint-disable-next-line react/no-array-index-key
|
|
||||||
key={index}
|
|
||||||
className="ril-loading-circle-point ril__loadingCirclePoint"
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fall back to loading icon if the thumbnail has not been loaded
|
|
||||||
images.push(
|
|
||||||
<div
|
|
||||||
className={`${imageClass} ril__image ril-not-loaded`}
|
|
||||||
style={imageStyle}
|
|
||||||
key={this.props[srcType] + keyEndings[srcType]}
|
|
||||||
>
|
|
||||||
<div className="ril__loadingContainer">{loadingIcon}</div>
|
|
||||||
</div>,
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const imageSrc = bestImageInfo.src;
|
|
||||||
if (discourageDownloads) {
|
|
||||||
imageStyle.backgroundImage = `url('${imageSrc}')`;
|
|
||||||
images.push(
|
|
||||||
<div
|
|
||||||
className={`${imageClass} ril__image ril__imageDiscourager`}
|
|
||||||
onDoubleClick={this.handleImageDoubleClick}
|
|
||||||
onWheel={this.handleImageMouseWheel}
|
|
||||||
style={imageStyle}
|
|
||||||
key={imageSrc + keyEndings[srcType]}
|
|
||||||
>
|
|
||||||
<div className="ril-download-blocker ril__downloadBlocker" />
|
|
||||||
</div>,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
images.push(
|
|
||||||
<img
|
|
||||||
{...(imageCrossOrigin ? { crossOrigin: imageCrossOrigin } : {})}
|
|
||||||
className={`${imageClass} ril__image`}
|
|
||||||
onDoubleClick={this.handleImageDoubleClick}
|
|
||||||
onWheel={this.handleImageMouseWheel}
|
|
||||||
onDragStart={(e) => e.preventDefault()}
|
|
||||||
style={imageStyle}
|
|
||||||
src={imageSrc}
|
|
||||||
key={imageSrc + keyEndings[srcType]}
|
|
||||||
alt={typeof imageTitle === 'string' ? imageTitle : translate('Image')}
|
|
||||||
draggable={false}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const zoomMultiplier = this.getZoomMultiplier();
|
const zoomMultiplier = this.getZoomMultiplier();
|
||||||
// Next Image (displayed on the right)
|
// Next Image (displayed on the right)
|
||||||
addImage('nextSrc', 'ril-image-next ril__imageNext', {
|
addImage('nextFile', 'ril-image-next ril__imageNext', {
|
||||||
x: boxSize.width,
|
x: boxSize.width,
|
||||||
});
|
});
|
||||||
// Main Image
|
// Main Image
|
||||||
addImage('mainSrc', 'ril-image-current', {
|
addImage('mainFile', 'ril-image-current', {
|
||||||
x: -1 * offsetX,
|
x: -1 * offsetX,
|
||||||
y: -1 * offsetY,
|
y: -1 * offsetY,
|
||||||
zoom: zoomMultiplier,
|
zoom: zoomMultiplier,
|
||||||
});
|
});
|
||||||
// Previous Image (displayed on the left)
|
// Previous Image (displayed on the left)
|
||||||
addImage('prevSrc', 'ril-image-prev ril__imagePrev', {
|
addImage('prevFile', 'ril-image-prev ril__imagePrev', {
|
||||||
x: -1 * boxSize.width,
|
x: -1 * boxSize.width,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1336,7 +1313,6 @@ class ReactImageLightbox extends Component {
|
|||||||
...reactModalStyle.content, // Allow style overrides via props
|
...reactModalStyle.content, // Allow style overrides via props
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen
|
isOpen
|
||||||
@ -1497,33 +1473,37 @@ class ReactImageLightbox extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReactImageLightbox.propTypes = {
|
ReactImageLightbox.propTypes = {
|
||||||
|
previewList: PropTypes.object,
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// Image sources
|
// Image sources
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
||||||
// Main display image url
|
// Main display image url
|
||||||
mainSrc: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types
|
mainSrc: PropTypes.string.isRequired,
|
||||||
|
mainFile: PropTypes.object,
|
||||||
|
|
||||||
// Previous display image url (displayed to the left)
|
// Previous display image url (displayed to the left)
|
||||||
// If left undefined, movePrev actions will not be performed, and the button not displayed
|
// If left undefined, movePrev actions will not be performed, and the button not displayed
|
||||||
prevSrc: PropTypes.string,
|
prevSrc: PropTypes.string,
|
||||||
|
prevFile: PropTypes.object,
|
||||||
|
|
||||||
// Next display image url (displayed to the right)
|
// Next display image url (displayed to the right)
|
||||||
// If left undefined, moveNext actions will not be performed, and the button not displayed
|
// If left undefined, moveNext actions will not be performed, and the button not displayed
|
||||||
nextSrc: PropTypes.string,
|
nextSrc: PropTypes.string,
|
||||||
|
nextFile: PropTypes.object,
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// Image thumbnail sources
|
// Image thumbnail sources
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
||||||
// Thumbnail image url corresponding to props.mainSrc
|
// Thumbnail image url corresponding to props.mainSrc
|
||||||
mainSrcThumbnail: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
mainSrcThumbnail: PropTypes.string,
|
||||||
|
|
||||||
// Thumbnail image url corresponding to props.prevSrc
|
// Thumbnail image url corresponding to props.prevSrc
|
||||||
prevSrcThumbnail: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
prevSrcThumbnail: PropTypes.string,
|
||||||
|
|
||||||
// Thumbnail image url corresponding to props.nextSrc
|
// Thumbnail image url corresponding to props.nextSrc
|
||||||
nextSrcThumbnail: PropTypes.string, // eslint-disable-line react/no-unused-prop-types
|
nextSrcThumbnail: PropTypes.string,
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// Event Handlers
|
// Event Handlers
|
||||||
|
Loading…
Reference in New Issue
Block a user