feat: initial commit - Phase 1 & 2 core features

This commit is contained in:
hiderfong
2026-04-22 17:07:33 +08:00
commit 1773bda06b
25005 changed files with 6252106 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
import { SFCWithInstall } from "../../utils/vue/typescript.js";
import "../../utils/index.js";
import { ImageCrossorigin, ImageEmits, ImageFitType, ImageInstance, ImageProps, ImagePropsPublic, imageEmits, imageProps } from "./src/image.js";
import { _default } from "./src/image.vue.js";
//#region ../../packages/components/image/index.d.ts
declare const ElImage: SFCWithInstall<typeof _default>;
//#endregion
export { ElImage, ElImage as default, ImageCrossorigin, ImageEmits, ImageFitType, ImageInstance, ImageProps, ImagePropsPublic, imageEmits, imageProps };
+10
View File
@@ -0,0 +1,10 @@
import { withInstall } from "../../utils/vue/install.mjs";
import { imageEmits, imageProps } from "./src/image.mjs";
import image_default from "./src/image2.mjs";
//#region ../../packages/components/image/index.ts
const ElImage = withInstall(image_default);
//#endregion
export { ElImage, ElImage as default, imageEmits, imageProps };
//# sourceMappingURL=index.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.mjs","names":["Image"],"sources":["../../../../../packages/components/image/index.ts"],"sourcesContent":["import { withInstall } from '@element-plus/utils'\nimport Image from './src/image.vue'\n\nimport type { SFCWithInstall } from '@element-plus/utils'\n\nexport const ElImage: SFCWithInstall<typeof Image> = withInstall(Image)\nexport default ElImage\n\nexport * from './src/image'\n"],"mappings":";;;;;AAKA,MAAa,UAAwC,YAAYA,cAAM"}
+141
View File
@@ -0,0 +1,141 @@
import { EpPropFinalized, EpPropMergeType } from "../../../utils/vue/props/types.js";
import "../../../utils/index.js";
import { _default } from "./image.vue.js";
import * as vue from "vue";
import { ExtractPublicPropTypes } from "vue";
//#region ../../packages/components/image/src/image.d.ts
type ImageFitType = '' | 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
type ImageCrossorigin = 'anonymous' | 'use-credentials' | '';
interface ImageProps {
/**
* @description when enabling preview, use this flag to control whether clicking on backdrop can exit preview mode.
*/
hideOnClickModal?: boolean;
/**
* @description image source, same as native.
*/
src?: string;
/**
* @description indicate how the image should be resized to fit its container, same as [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit).
*/
fit?: ImageFitType;
/**
* @description Indicates how the browser should load the image, same as [native](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img#loading)
*/
loading?: 'eager' | 'lazy';
/**
* @description whether to use lazy load.
*/
lazy?: boolean;
/**
* @description the container to add scroll listener when using lazy load.
*/
scrollContainer?: string | HTMLElement;
/**
* @description allow big image preview.
*/
previewSrcList?: string[];
/**
* @description whether to append image-viewer to body. A nested parent element attribute transform should have this attribute set to `true`.
*/
previewTeleported?: boolean;
/**
* @description set image preview z-index.
*/
zIndex?: number;
/**
* @description initial preview image index, less than the length of `url-list`.
*/
initialIndex?: number;
/**
* @description whether the viewer preview is infinite.
*/
infinite?: boolean;
/**
* @description whether the image-viewer can be closed by pressing ESC.
*/
closeOnPressEscape?: boolean;
/**
* @description the zoom rate of the image viewer zoom event
*/
zoomRate?: number;
/**
* @description preview image scale.
*/
scale?: number;
/**
* @description the min scale of the image viewer zoom event.
*/
minScale?: number;
/**
* @description the max scale of the image viewer zoom event.
*/
maxScale?: number;
/**
* @description show preview image progress content.
*/
showProgress?: boolean;
/**
* @description set HTML attribute: crossorigin.
*/
crossorigin?: ImageCrossorigin;
}
/**
* @deprecated Removed after 3.0.0, Use `ImageProps` instead.
*/
declare const imageProps: {
readonly hideOnClickModal: BooleanConstructor;
readonly src: EpPropFinalized<StringConstructor, unknown, unknown, "", boolean>;
readonly fit: EpPropFinalized<StringConstructor, "" | "fill" | "none" | "contain" | "cover" | "scale-down", unknown, "", boolean>;
readonly loading: {
readonly type: vue.PropType<EpPropMergeType<StringConstructor, "lazy" | "eager", unknown>>;
readonly required: false;
readonly validator: ((val: unknown) => boolean) | undefined;
__epPropKey: true;
};
readonly lazy: BooleanConstructor;
readonly scrollContainer: {
readonly type: vue.PropType<EpPropMergeType<(new (...args: any[]) => string | HTMLElement) | (() => string | HTMLElement | undefined) | (((new (...args: any[]) => string | HTMLElement) | (() => string | HTMLElement | undefined)) | null)[], unknown, unknown>>;
readonly required: false;
readonly validator: ((val: unknown) => boolean) | undefined;
__epPropKey: true;
};
readonly previewSrcList: EpPropFinalized<(new (...args: any[]) => string[]) | (() => string[]) | (((new (...args: any[]) => string[]) | (() => string[])) | null)[], unknown, unknown, () => [], boolean>;
readonly previewTeleported: BooleanConstructor;
readonly zIndex: {
readonly type: vue.PropType<number>;
readonly required: false;
readonly validator: ((val: unknown) => boolean) | undefined;
__epPropKey: true;
};
readonly initialIndex: EpPropFinalized<NumberConstructor, unknown, unknown, 0, boolean>;
readonly infinite: EpPropFinalized<BooleanConstructor, unknown, unknown, true, boolean>;
readonly closeOnPressEscape: EpPropFinalized<BooleanConstructor, unknown, unknown, true, boolean>;
readonly zoomRate: EpPropFinalized<NumberConstructor, unknown, unknown, 1.2, boolean>;
readonly scale: EpPropFinalized<NumberConstructor, unknown, unknown, 1, boolean>;
readonly minScale: EpPropFinalized<NumberConstructor, unknown, unknown, 0.2, boolean>;
readonly maxScale: EpPropFinalized<NumberConstructor, unknown, unknown, 7, boolean>;
readonly showProgress: BooleanConstructor;
readonly crossorigin: {
readonly type: vue.PropType<EpPropMergeType<(new (...args: any[]) => "" | "anonymous" | "use-credentials") | (() => ImageCrossorigin) | (((new (...args: any[]) => "" | "anonymous" | "use-credentials") | (() => ImageCrossorigin)) | null)[], unknown, unknown>>;
readonly required: false;
readonly validator: ((val: unknown) => boolean) | undefined;
__epPropKey: true;
};
};
/**
* @deprecated Removed after 3.0.0, Use `ImageProps` instead.
*/
type ImagePropsPublic = ExtractPublicPropTypes<typeof imageProps>;
declare const imageEmits: {
load: (evt: Event) => boolean;
error: (evt: Event) => boolean;
switch: (val: number) => boolean;
close: () => boolean;
show: () => boolean;
};
type ImageEmits = typeof imageEmits;
type ImageInstance = InstanceType<typeof _default> & unknown;
//#endregion
export { ImageCrossorigin, ImageEmits, ImageFitType, ImageInstance, ImageProps, ImagePropsPublic, imageEmits, imageProps };
+80
View File
@@ -0,0 +1,80 @@
import { isNumber } from "../../../utils/types.mjs";
import { buildProps, definePropType } from "../../../utils/vue/props/runtime.mjs";
import { mutable } from "../../../utils/typescript.mjs";
//#region ../../packages/components/image/src/image.ts
/**
* @deprecated Removed after 3.0.0, Use `ImageProps` instead.
*/
const imageProps = buildProps({
hideOnClickModal: Boolean,
src: {
type: String,
default: ""
},
fit: {
type: String,
values: [
"",
"contain",
"cover",
"fill",
"none",
"scale-down"
],
default: ""
},
loading: {
type: String,
values: ["eager", "lazy"]
},
lazy: Boolean,
scrollContainer: { type: definePropType([String, Object]) },
previewSrcList: {
type: definePropType(Array),
default: () => mutable([])
},
previewTeleported: Boolean,
zIndex: { type: Number },
initialIndex: {
type: Number,
default: 0
},
infinite: {
type: Boolean,
default: true
},
closeOnPressEscape: {
type: Boolean,
default: true
},
zoomRate: {
type: Number,
default: 1.2
},
scale: {
type: Number,
default: 1
},
minScale: {
type: Number,
default: .2
},
maxScale: {
type: Number,
default: 7
},
showProgress: Boolean,
crossorigin: { type: definePropType(String) }
});
const imageEmits = {
load: (evt) => evt instanceof Event,
error: (evt) => evt instanceof Event,
switch: (val) => isNumber(val),
close: () => true,
show: () => true
};
//#endregion
export { imageEmits, imageProps };
//# sourceMappingURL=image.mjs.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,69 @@
import { ImageViewerAction } from "../../image-viewer/src/image-viewer.js";
import "../../image-viewer/index.js";
import { ImageFitType, ImageProps } from "./image.js";
import * as vue from "vue";
//#region ../../packages/components/image/src/image.vue.d.ts
declare function clickHandler(): void;
declare var __VLS_1: {}, __VLS_3: {}, __VLS_14: {}, __VLS_17: {
activeIndex: number;
total: number;
}, __VLS_20: {
actions: (action: ImageViewerAction, options?: {}) => void;
prev: () => void;
next: () => void;
reset: () => void;
activeIndex: number;
setActiveItem: (index: number) => void;
}, __VLS_23: {
activeIndex: number;
src: string;
};
type __VLS_Slots = {} & {
error?: (props: typeof __VLS_1) => any;
} & {
placeholder?: (props: typeof __VLS_3) => any;
} & {
viewer?: (props: typeof __VLS_14) => any;
} & {
progress?: (props: typeof __VLS_17) => any;
} & {
toolbar?: (props: typeof __VLS_20) => any;
} & {
'viewer-error'?: (props: typeof __VLS_23) => any;
};
declare const __VLS_base: vue.DefineComponent<ImageProps, {
/** @description manually open preview */showPreview: typeof clickHandler;
}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
close: () => void;
error: (evt: Event) => void;
load: (evt: Event) => void;
show: () => void;
switch: (val: number) => void;
}, string, vue.PublicProps, Readonly<ImageProps> & Readonly<{
onClose?: (() => any) | undefined;
onError?: ((evt: Event) => any) | undefined;
onLoad?: ((evt: Event) => any) | undefined;
onShow?: (() => any) | undefined;
onSwitch?: ((val: number) => any) | undefined;
}>, {
infinite: boolean;
scale: number;
src: string;
fit: ImageFitType;
initialIndex: number;
closeOnPressEscape: boolean;
previewSrcList: string[];
zoomRate: number;
minScale: number;
maxScale: number;
}, {}, {}, {}, string, vue.ComponentProvideOptions, false, {}, any>;
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
declare const _default: typeof __VLS_export;
type __VLS_WithSlots<T, S> = T & {
new (): {
$slots: S;
};
};
//#endregion
export { _default };
@@ -0,0 +1,208 @@
import { isClient as isClient$1 } from "../../../utils/browser.mjs";
import { isArray, isElement, isString, isWindow } from "../../../utils/types.mjs";
import { getScrollContainer } from "../../../utils/dom/scroll.mjs";
import { useAttrs as useAttrs$1 } from "../../../hooks/use-attrs/index.mjs";
import { useLocale } from "../../../hooks/use-locale/index.mjs";
import { useNamespace } from "../../../hooks/use-namespace/index.mjs";
import { imageEmits, imageProps } from "./image.mjs";
import { ElImageViewer } from "../../image-viewer/index.mjs";
import { useIntersectionObserver, useThrottleFn } from "@vueuse/core";
import { fromPairs } from "lodash-unified";
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, defineComponent, guardReactiveProps, mergeProps, nextTick, normalizeClass, normalizeProps, onMounted, openBlock, ref, renderSlot, toDisplayString, unref, useAttrs, watch, withCtx } from "vue";
//#region ../../packages/components/image/src/image.vue?vue&type=script&setup=true&lang.ts
const _hoisted_1 = [
"src",
"loading",
"crossorigin"
];
const _hoisted_2 = { key: 0 };
var image_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
name: "ElImage",
inheritAttrs: false,
__name: "image",
props: imageProps,
emits: imageEmits,
setup(__props, { expose: __expose, emit: __emit }) {
const props = __props;
const emit = __emit;
const { t } = useLocale();
const ns = useNamespace("image");
const rawAttrs = useAttrs();
const containerAttrs = computed(() => {
return fromPairs(Object.entries(rawAttrs).filter(([key]) => /^(data-|on[A-Z])/i.test(key) || ["id", "style"].includes(key)));
});
const imgAttrs = useAttrs$1({
excludeListeners: true,
excludeKeys: computed(() => {
return Object.keys(containerAttrs.value);
})
});
const imageSrc = ref();
const hasLoadError = ref(false);
const isLoading = ref(true);
const showViewer = ref(false);
const container = ref();
const _scrollContainer = ref();
const supportLoading = isClient$1 && "loading" in HTMLImageElement.prototype;
let stopScrollListener;
const imageKls = computed(() => [
ns.e("inner"),
preview.value && ns.e("preview"),
isLoading.value && ns.is("loading")
]);
const imageStyle = computed(() => {
const { fit } = props;
if (isClient$1 && fit) return { objectFit: fit };
return {};
});
const preview = computed(() => {
const { previewSrcList } = props;
return isArray(previewSrcList) && previewSrcList.length > 0;
});
const imageIndex = computed(() => {
const { previewSrcList, initialIndex } = props;
let previewIndex = initialIndex;
if (initialIndex > previewSrcList.length - 1) previewIndex = 0;
return previewIndex;
});
const isManual = computed(() => {
if (props.loading === "eager") return false;
return !supportLoading && props.loading === "lazy" || props.lazy;
});
const loadImage = () => {
if (!isClient$1) return;
isLoading.value = true;
hasLoadError.value = false;
imageSrc.value = props.src;
};
function handleLoad(event) {
isLoading.value = false;
hasLoadError.value = false;
emit("load", event);
}
function handleError(event) {
isLoading.value = false;
hasLoadError.value = true;
emit("error", event);
}
function handleLazyLoad(isIntersecting) {
if (isIntersecting) {
loadImage();
removeLazyLoadListener();
}
}
const lazyLoadHandler = useThrottleFn(handleLazyLoad, 200, true);
async function addLazyLoadListener() {
if (!isClient$1) return;
await nextTick();
const { scrollContainer } = props;
if (isElement(scrollContainer)) _scrollContainer.value = scrollContainer;
else if (isString(scrollContainer) && scrollContainer !== "") _scrollContainer.value = document.querySelector(scrollContainer) ?? void 0;
else if (container.value) {
const scrollContainer = getScrollContainer(container.value);
_scrollContainer.value = isWindow(scrollContainer) ? void 0 : scrollContainer;
}
const { stop } = useIntersectionObserver(container, ([entry]) => {
lazyLoadHandler(entry.isIntersecting);
}, { root: _scrollContainer });
stopScrollListener = stop;
}
function removeLazyLoadListener() {
if (!isClient$1 || !lazyLoadHandler) return;
stopScrollListener?.();
_scrollContainer.value = void 0;
stopScrollListener = void 0;
}
function clickHandler() {
if (!preview.value) return;
showViewer.value = true;
emit("show");
}
function closeViewer() {
showViewer.value = false;
emit("close");
}
function switchViewer(val) {
emit("switch", val);
}
watch(() => props.src, () => {
if (isManual.value) {
isLoading.value = true;
hasLoadError.value = false;
removeLazyLoadListener();
addLazyLoadListener();
} else loadImage();
});
onMounted(() => {
if (isManual.value) addLazyLoadListener();
else loadImage();
});
__expose({ showPreview: clickHandler });
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", mergeProps({
ref_key: "container",
ref: container
}, containerAttrs.value, { class: [unref(ns).b(), _ctx.$attrs.class] }), [hasLoadError.value ? renderSlot(_ctx.$slots, "error", { key: 0 }, () => [createElementVNode("div", { class: normalizeClass(unref(ns).e("error")) }, toDisplayString(unref(t)("el.image.error")), 3)]) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [imageSrc.value !== void 0 ? (openBlock(), createElementBlock("img", mergeProps({ key: 0 }, unref(imgAttrs), {
src: imageSrc.value,
loading: __props.loading,
style: imageStyle.value,
class: imageKls.value,
crossorigin: __props.crossorigin,
onClick: clickHandler,
onLoad: handleLoad,
onError: handleError
}), null, 16, _hoisted_1)) : createCommentVNode("v-if", true), isLoading.value ? (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass(unref(ns).e("wrapper"))
}, [renderSlot(_ctx.$slots, "placeholder", {}, () => [createElementVNode("div", { class: normalizeClass(unref(ns).e("placeholder")) }, null, 2)])], 2)) : createCommentVNode("v-if", true)], 64)), preview.value ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [showViewer.value ? (openBlock(), createBlock(unref(ElImageViewer), {
key: 0,
"z-index": __props.zIndex,
"initial-index": imageIndex.value,
infinite: __props.infinite,
"zoom-rate": __props.zoomRate,
"min-scale": __props.minScale,
"max-scale": __props.maxScale,
"show-progress": __props.showProgress,
"url-list": __props.previewSrcList,
scale: __props.scale,
crossorigin: __props.crossorigin,
"hide-on-click-modal": __props.hideOnClickModal,
teleported: __props.previewTeleported,
"close-on-press-escape": __props.closeOnPressEscape,
onClose: closeViewer,
onSwitch: switchViewer
}, createSlots({
toolbar: withCtx((toolbar) => [renderSlot(_ctx.$slots, "toolbar", normalizeProps(guardReactiveProps(toolbar)))]),
default: withCtx(() => [_ctx.$slots.viewer ? (openBlock(), createElementBlock("div", _hoisted_2, [renderSlot(_ctx.$slots, "viewer")])) : createCommentVNode("v-if", true)]),
_: 2
}, [_ctx.$slots.progress ? {
name: "progress",
fn: withCtx((progress) => [renderSlot(_ctx.$slots, "progress", normalizeProps(guardReactiveProps(progress)))]),
key: "0"
} : void 0, _ctx.$slots["viewer-error"] ? {
name: "viewer-error",
fn: withCtx((viewerError) => [renderSlot(_ctx.$slots, "viewer-error", normalizeProps(guardReactiveProps(viewerError)))]),
key: "1"
} : void 0]), 1032, [
"z-index",
"initial-index",
"infinite",
"zoom-rate",
"min-scale",
"max-scale",
"show-progress",
"url-list",
"scale",
"crossorigin",
"hide-on-click-modal",
"teleported",
"close-on-press-escape"
])) : createCommentVNode("v-if", true)], 64)) : createCommentVNode("v-if", true)], 16);
};
}
});
//#endregion
export { image_vue_vue_type_script_setup_true_lang_default as default };
//# sourceMappingURL=image.vue_vue_type_script_setup_true_lang.mjs.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
import image_vue_vue_type_script_setup_true_lang_default from "./image.vue_vue_type_script_setup_true_lang.mjs";
//#region ../../packages/components/image/src/image.vue
var image_default = image_vue_vue_type_script_setup_true_lang_default;
//#endregion
export { image_default as default };
//# sourceMappingURL=image2.mjs.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
import "../../base/style/css.mjs";
import "../../image-viewer/style/css.mjs";
import "element-plus/theme-chalk/el-image.css";
@@ -0,0 +1,3 @@
import "../../base/style/index.mjs";
import "../../image-viewer/style/index.mjs";
import "element-plus/theme-chalk/src/image.scss";