209 lines
5.6 KiB
Vue
209 lines
5.6 KiB
Vue
import type { PropType, Ref, ComputedRef } from 'vue';
|
|
import {
|
|
ref,
|
|
shallowRef,
|
|
provide,
|
|
defineComponent,
|
|
inject,
|
|
watch,
|
|
reactive,
|
|
computed,
|
|
watchEffect,
|
|
} from 'vue';
|
|
import { type ImagePreviewType, mergeDefaultValue } from './Image';
|
|
import Preview from './Preview';
|
|
import type { PreviewProps } from './Preview';
|
|
import useMergedState from '../../_util/hooks/useMergedState';
|
|
|
|
export interface PreviewGroupPreview
|
|
extends Omit<ImagePreviewType, 'icons' | 'mask' | 'maskClassName'> {
|
|
/**
|
|
* If Preview the show img index
|
|
* @default 0
|
|
*/
|
|
current?: number;
|
|
}
|
|
|
|
export interface GroupConsumerProps {
|
|
previewPrefixCls?: string;
|
|
icons?: PreviewProps['icons'];
|
|
preview?: boolean | PreviewGroupPreview;
|
|
}
|
|
|
|
interface PreviewUrl {
|
|
url: string;
|
|
canPreview: boolean;
|
|
}
|
|
|
|
export interface GroupConsumerValue extends GroupConsumerProps {
|
|
isPreviewGroup?: Ref<boolean | undefined>;
|
|
previewUrls: ComputedRef<Map<number, string>>;
|
|
setPreviewUrls: (id: number, url: string, canPreview?: boolean) => void;
|
|
current: Ref<number>;
|
|
setCurrent: (current: number) => void;
|
|
setShowPreview: (isShowPreview: boolean) => void;
|
|
setMousePosition: (mousePosition: null | { x: number; y: number }) => void;
|
|
registerImage: (id: number, url: string, canPreview?: boolean) => () => void;
|
|
rootClassName?: string;
|
|
}
|
|
const previewGroupContext = Symbol('previewGroupContext');
|
|
export const context = {
|
|
provide: (val: GroupConsumerValue) => {
|
|
provide(previewGroupContext, val);
|
|
},
|
|
inject: () => {
|
|
return inject<GroupConsumerValue>(previewGroupContext, {
|
|
isPreviewGroup: shallowRef(false),
|
|
previewUrls: computed(() => new Map()),
|
|
setPreviewUrls: () => {},
|
|
current: ref(null),
|
|
setCurrent: () => {},
|
|
setShowPreview: () => {},
|
|
setMousePosition: () => {},
|
|
registerImage: null,
|
|
rootClassName: '',
|
|
});
|
|
},
|
|
};
|
|
|
|
export const imageGroupProps = () => ({
|
|
previewPrefixCls: String,
|
|
preview: {
|
|
type: [Boolean, Object] as PropType<boolean | ImagePreviewType>,
|
|
default: true as boolean | ImagePreviewType,
|
|
},
|
|
icons: {
|
|
type: Object as PropType<PreviewProps['icons']>,
|
|
default: () => ({}),
|
|
},
|
|
});
|
|
|
|
const Group = defineComponent({
|
|
compatConfig: { MODE: 3 },
|
|
name: 'PreviewGroup',
|
|
inheritAttrs: false,
|
|
props: imageGroupProps(),
|
|
setup(props, { slots }) {
|
|
const preview = computed<PreviewGroupPreview>(() => {
|
|
const defaultValues = {
|
|
visible: undefined,
|
|
onVisibleChange: () => {},
|
|
getContainer: undefined,
|
|
current: 0,
|
|
};
|
|
return typeof props.preview === 'object'
|
|
? mergeDefaultValue(props.preview, defaultValues)
|
|
: defaultValues;
|
|
});
|
|
const previewUrls = reactive(new Map<number, PreviewUrl>());
|
|
const current = ref<number>();
|
|
|
|
const previewVisible = computed(() => preview.value.visible);
|
|
const getPreviewContainer = computed(() => preview.value.getContainer);
|
|
const onPreviewVisibleChange = (val, preval) => {
|
|
preview.value.onVisibleChange?.(val, preval);
|
|
};
|
|
const [isShowPreview, setShowPreview] = useMergedState(!!previewVisible.value, {
|
|
value: previewVisible,
|
|
onChange: onPreviewVisibleChange,
|
|
});
|
|
|
|
const mousePosition = ref<{ x: number; y: number }>(null);
|
|
const isControlled = computed(() => previewVisible.value !== undefined);
|
|
const previewUrlsKeys = computed(() => Array.from(previewUrls.keys()));
|
|
const currentControlledKey = computed(() => previewUrlsKeys.value[preview.value.current]);
|
|
const canPreviewUrls = computed(
|
|
() =>
|
|
new Map<number, string>(
|
|
Array.from(previewUrls)
|
|
.filter(([, { canPreview }]) => !!canPreview)
|
|
.map(([id, { url }]) => [id, url]),
|
|
),
|
|
);
|
|
|
|
const setPreviewUrls = (id: number, url: string, canPreview = true) => {
|
|
previewUrls.set(id, {
|
|
url,
|
|
canPreview,
|
|
});
|
|
};
|
|
const setCurrent = (val: number) => {
|
|
current.value = val;
|
|
};
|
|
const setMousePosition = (val: null | { x: number; y: number }) => {
|
|
mousePosition.value = val;
|
|
};
|
|
|
|
const registerImage = (id: number, url: string, canPreview = true) => {
|
|
const unRegister = () => {
|
|
previewUrls.delete(id);
|
|
};
|
|
previewUrls.set(id, {
|
|
url,
|
|
canPreview,
|
|
});
|
|
return unRegister;
|
|
};
|
|
|
|
const onPreviewClose = (e: any) => {
|
|
e?.stopPropagation();
|
|
setShowPreview(false);
|
|
setMousePosition(null);
|
|
};
|
|
|
|
watch(
|
|
currentControlledKey,
|
|
val => {
|
|
setCurrent(val);
|
|
},
|
|
{
|
|
immediate: true,
|
|
flush: 'post',
|
|
},
|
|
);
|
|
watchEffect(
|
|
() => {
|
|
if (isShowPreview.value && isControlled.value) {
|
|
setCurrent(currentControlledKey.value);
|
|
}
|
|
},
|
|
{
|
|
flush: 'post',
|
|
},
|
|
);
|
|
|
|
context.provide({
|
|
isPreviewGroup: shallowRef(true),
|
|
previewUrls: canPreviewUrls,
|
|
setPreviewUrls,
|
|
current,
|
|
setCurrent,
|
|
setShowPreview,
|
|
setMousePosition,
|
|
registerImage,
|
|
});
|
|
|
|
return () => {
|
|
const { ...dialogProps } = preview.value;
|
|
return (
|
|
<>
|
|
{slots.default && slots.default()}
|
|
<Preview
|
|
{...dialogProps}
|
|
ria-hidden={!isShowPreview.value}
|
|
visible={isShowPreview.value}
|
|
prefixCls={props.previewPrefixCls}
|
|
onClose={onPreviewClose}
|
|
mousePosition={mousePosition.value}
|
|
src={canPreviewUrls.value.get(current.value)}
|
|
icons={props.icons}
|
|
getContainer={getPreviewContainer.value}
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
},
|
|
});
|
|
|
|
export default Group;
|