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 { RateEmits, RateInstance, RateProps, RatePropsPublic, rateEmits, rateProps } from "./src/rate.js";
import { _default } from "./src/rate.vue.js";
//#region ../../packages/components/rate/index.d.ts
declare const ElRate: SFCWithInstall<typeof _default>;
//#endregion
export { ElRate, ElRate as default, RateEmits, RateInstance, RateProps, RatePropsPublic, rateEmits, rateProps };
+10
View File
@@ -0,0 +1,10 @@
import { withInstall } from "../../utils/vue/install.mjs";
import { rateEmits, rateProps } from "./src/rate.mjs";
import rate_default from "./src/rate2.mjs";
//#region ../../packages/components/rate/index.ts
const ElRate = withInstall(rate_default);
//#endregion
export { ElRate, ElRate as default, rateEmits, rateProps };
//# sourceMappingURL=index.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.mjs","names":["Rate"],"sources":["../../../../../packages/components/rate/index.ts"],"sourcesContent":["import { withInstall } from '@element-plus/utils'\nimport Rate from './src/rate.vue'\n\nimport type { SFCWithInstall } from '@element-plus/utils'\n\nexport const ElRate: SFCWithInstall<typeof Rate> = withInstall(Rate)\nexport default ElRate\n\nexport * from './src/rate'\n"],"mappings":";;;;;AAKA,MAAa,SAAsC,YAAYA,aAAK"}
+138
View File
@@ -0,0 +1,138 @@
import { IconPropType } from "../../../utils/vue/icon.js";
import { EpPropFinalized, EpPropMergeType } from "../../../utils/vue/props/types.js";
import { ComponentSize } from "../../../constants/size.js";
import "../../../utils/index.js";
import { _default } from "./rate.vue.js";
import * as vue from "vue";
import { Component, ExtractPublicPropTypes } from "vue";
//#region ../../packages/components/rate/src/rate.d.ts
interface RateProps {
/**
* @description binding value
*/
modelValue?: number;
/**
* @description native `id` attribute
*/
id?: string;
/**
* @description threshold value between low and medium level. The value itself will be included in low level
*/
lowThreshold?: number;
/**
* @description threshold value between medium and high level. The value itself will be included in high level
*/
highThreshold?: number;
/**
* @description max rating score
*/
max?: number;
/**
* @description colors for icons. If array, it should have 3 elements, each of which corresponds with a score level, else if object, the key should be threshold value between two levels, and the value should be corresponding color
*/
colors?: string[] | Record<number, string>;
/**
* @description color of unselected icons
*/
voidColor?: string;
/**
* @description color of unselected read-only icons
*/
disabledVoidColor?: string;
/**
* @description icon components. If array, it should have 3 elements, each of which corresponds with a score level, else if object, the key should be threshold value between two levels, and the value should be corresponding icon component
*/
icons?: Array<IconPropType> | Record<number, IconPropType>;
/**
* @description component of unselected icons
*/
voidIcon?: IconPropType;
/**
* @description component of unselected read-only icons
*/
disabledVoidIcon?: IconPropType;
/**
* @description whether Rate is read-only
*/
disabled?: boolean;
/**
* @description whether picking half start is allowed
*/
allowHalf?: boolean;
/**
* @description whether to display texts
*/
showText?: boolean;
/**
* @description whether to display current score. show-score and show-text cannot be true at the same time
*/
showScore?: boolean;
/**
* @description color of texts
*/
textColor?: string;
/**
* @description text array
*/
texts?: string[];
/**
* @description score template
*/
scoreTemplate?: string;
/**
* @description size of Rate
*/
size?: ComponentSize;
/**
* @description whether value can be reset to `0`
*/
clearable?: boolean;
/**
* @description native `aria-label` attribute
*/
ariaLabel?: string;
}
/**
* @deprecated Removed after 3.0.0, Use `RateProps` instead.
*/
declare const rateProps: {
readonly ariaLabel: StringConstructor;
readonly modelValue: EpPropFinalized<NumberConstructor, unknown, unknown, 0, boolean>;
readonly id: EpPropFinalized<StringConstructor, unknown, unknown, undefined, boolean>;
readonly lowThreshold: EpPropFinalized<NumberConstructor, unknown, unknown, 2, boolean>;
readonly highThreshold: EpPropFinalized<NumberConstructor, unknown, unknown, 4, boolean>;
readonly max: EpPropFinalized<NumberConstructor, unknown, unknown, 5, boolean>;
readonly colors: EpPropFinalized<(new (...args: any[]) => string[] | Record<number, string>) | (() => string[] | Record<number, string>) | (((new (...args: any[]) => string[] | Record<number, string>) | (() => string[] | Record<number, string>)) | null)[], unknown, unknown, () => ["", "", ""], boolean>;
readonly voidColor: EpPropFinalized<StringConstructor, unknown, unknown, "", boolean>;
readonly disabledVoidColor: EpPropFinalized<StringConstructor, unknown, unknown, "", boolean>;
readonly icons: EpPropFinalized<(new (...args: any[]) => (string | Component)[] | Record<number, string | Component>) | (() => (string | Component)[] | Record<number, string | Component>) | (((new (...args: any[]) => (string | Component)[] | Record<number, string | Component>) | (() => (string | Component)[] | Record<number, string | Component>)) | null)[], unknown, unknown, () => [Component, Component, Component], boolean>;
readonly voidIcon: EpPropFinalized<(new (...args: any[]) => (string | Component) & {}) | (() => string | Component) | (((new (...args: any[]) => (string | Component) & {}) | (() => string | Component)) | null)[], unknown, unknown, () => Component, boolean>;
readonly disabledVoidIcon: EpPropFinalized<(new (...args: any[]) => (string | Component) & {}) | (() => string | Component) | (((new (...args: any[]) => (string | Component) & {}) | (() => string | Component)) | null)[], unknown, unknown, () => Component, boolean>;
readonly disabled: EpPropFinalized<BooleanConstructor, unknown, unknown, undefined, boolean>;
readonly allowHalf: BooleanConstructor;
readonly showText: BooleanConstructor;
readonly showScore: BooleanConstructor;
readonly textColor: EpPropFinalized<StringConstructor, unknown, unknown, "", boolean>;
readonly texts: EpPropFinalized<(new (...args: any[]) => string[]) | (() => string[]) | (((new (...args: any[]) => string[]) | (() => string[])) | null)[], unknown, unknown, () => ["Extremely bad", "Disappointed", "Fair", "Satisfied", "Surprise"], boolean>;
readonly scoreTemplate: EpPropFinalized<StringConstructor, unknown, unknown, "{value}", boolean>;
readonly size: {
readonly type: vue.PropType<EpPropMergeType<StringConstructor, "" | "default" | "small" | "large", never>>;
readonly required: false;
readonly validator: ((val: unknown) => boolean) | undefined;
__epPropKey: true;
};
readonly clearable: BooleanConstructor;
};
/**
* @deprecated Removed after 3.0.0, Use `RateProps` instead.
*/
type RatePropsPublic = ExtractPublicPropTypes<typeof rateProps>;
declare const rateEmits: {
change: (value: number) => boolean;
"update:modelValue": (value: number) => boolean;
};
type RateEmits = typeof rateEmits;
type RateInstance = InstanceType<typeof _default> & unknown;
//#endregion
export { RateEmits, RateInstance, RateProps, RatePropsPublic, rateEmits, rateProps };
+103
View File
@@ -0,0 +1,103 @@
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from "../../../constants/event.mjs";
import { isNumber } from "../../../utils/types.mjs";
import { buildProps, definePropType } from "../../../utils/vue/props/runtime.mjs";
import { iconPropType } from "../../../utils/vue/icon.mjs";
import { mutable } from "../../../utils/typescript.mjs";
import { useSizeProp } from "../../../hooks/use-size/index.mjs";
import { useAriaProps } from "../../../hooks/use-aria/index.mjs";
import { Star, StarFilled } from "@element-plus/icons-vue";
//#region ../../packages/components/rate/src/rate.ts
/**
* @deprecated Removed after 3.0.0, Use `RateProps` instead.
*/
const rateProps = buildProps({
modelValue: {
type: Number,
default: 0
},
id: {
type: String,
default: void 0
},
lowThreshold: {
type: Number,
default: 2
},
highThreshold: {
type: Number,
default: 4
},
max: {
type: Number,
default: 5
},
colors: {
type: definePropType([Array, Object]),
default: () => mutable([
"",
"",
""
])
},
voidColor: {
type: String,
default: ""
},
disabledVoidColor: {
type: String,
default: ""
},
icons: {
type: definePropType([Array, Object]),
default: () => [
StarFilled,
StarFilled,
StarFilled
]
},
voidIcon: {
type: iconPropType,
default: () => Star
},
disabledVoidIcon: {
type: iconPropType,
default: () => StarFilled
},
disabled: {
type: Boolean,
default: void 0
},
allowHalf: Boolean,
showText: Boolean,
showScore: Boolean,
textColor: {
type: String,
default: ""
},
texts: {
type: definePropType(Array),
default: () => mutable([
"Extremely bad",
"Disappointed",
"Fair",
"Satisfied",
"Surprise"
])
},
scoreTemplate: {
type: String,
default: "{value}"
},
size: useSizeProp,
clearable: Boolean,
...useAriaProps(["ariaLabel"])
});
const rateEmits = {
[CHANGE_EVENT]: (value) => isNumber(value),
[UPDATE_MODEL_EVENT]: (value) => isNumber(value)
};
//#endregion
export { rateEmits, rateProps };
//# sourceMappingURL=rate.mjs.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
import { IconPropType } from "../../../utils/vue/icon.js";
import "../../../utils/index.js";
import { RateProps } from "./rate.js";
import * as vue from "vue";
//#region ../../packages/components/rate/src/rate.vue.d.ts
declare function setCurrentValue(value: number, event?: MouseEvent): void;
declare function resetCurrentValue(): void;
declare const __VLS_export: vue.DefineComponent<RateProps, {
/** @description set current value */setCurrentValue: typeof setCurrentValue; /** @description reset current value */
resetCurrentValue: typeof resetCurrentValue;
}, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
change: (value: number) => void;
"update:modelValue": (value: number) => void;
}, string, vue.PublicProps, Readonly<RateProps> & Readonly<{
onChange?: ((value: number) => any) | undefined;
"onUpdate:modelValue"?: ((value: number) => any) | undefined;
}>, {
id: string;
disabled: boolean;
modelValue: number;
max: number;
textColor: string;
lowThreshold: number;
highThreshold: number;
colors: string[] | Record<number, string>;
voidColor: string;
disabledVoidColor: string;
icons: Array<IconPropType> | Record<number, IconPropType>;
voidIcon: IconPropType;
disabledVoidIcon: IconPropType;
texts: string[];
scoreTemplate: string;
}, {}, {}, {}, string, vue.ComponentProvideOptions, false, {}, any>;
declare const _default: typeof __VLS_export;
//#endregion
export { _default };
@@ -0,0 +1,222 @@
import { EVENT_CODE } from "../../../constants/aria.mjs";
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from "../../../constants/event.mjs";
import { getEventCode } from "../../../utils/dom/event.mjs";
import { isArray, isObject, isString } from "../../../utils/types.mjs";
import { useNamespace } from "../../../hooks/use-namespace/index.mjs";
import { ElIcon } from "../../icon/index.mjs";
import { formItemContextKey } from "../../form/src/constants.mjs";
import { useFormDisabled, useFormSize } from "../../form/src/hooks/use-form-common-props.mjs";
import { useFormItemInputId } from "../../form/src/hooks/use-form-item.mjs";
import { rateEmits, rateProps } from "./rate.mjs";
import { clamp } from "lodash-unified";
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createVNode, defineComponent, inject, markRaw, normalizeClass, normalizeStyle, openBlock, ref, renderList, resolveDynamicComponent, toDisplayString, unref, vShow, watch, withCtx, withDirectives } from "vue";
//#region ../../packages/components/rate/src/rate.vue?vue&type=script&setup=true&lang.ts
const _hoisted_1 = [
"id",
"aria-label",
"aria-labelledby",
"aria-valuenow",
"aria-valuetext",
"aria-valuemax",
"tabindex",
"aria-disabled"
];
const _hoisted_2 = ["onMousemove", "onClick"];
var rate_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
name: "ElRate",
__name: "rate",
props: rateProps,
emits: rateEmits,
setup(__props, { expose: __expose, emit: __emit }) {
function getValueFromMap(value, map) {
const isExcludedObject = (val) => isObject(val);
const matchedValue = map[Object.keys(map).map((key) => +key).filter((key) => {
const val = map[key];
return (isExcludedObject(val) ? val.excluded : false) ? value < key : value <= key;
}).sort((a, b) => a - b)[0]];
return isExcludedObject(matchedValue) && matchedValue.value || matchedValue;
}
const props = __props;
const emit = __emit;
const formItemContext = inject(formItemContextKey, void 0);
const rateSize = useFormSize();
const ns = useNamespace("rate");
const { inputId, isLabeledByFormItem } = useFormItemInputId(props, { formItemContext });
const currentValue = ref(clamp(props.modelValue, 0, props.max));
const hoverIndex = ref(-1);
const pointerAtLeftHalf = ref(true);
const iconRefs = ref([]);
const iconClientWidths = computed(() => iconRefs.value.map((icon) => icon.$el.clientWidth));
const rateClasses = computed(() => [ns.b(), ns.m(rateSize.value)]);
const rateDisabled = useFormDisabled();
const rateStyles = computed(() => {
return ns.cssVarBlock({
"void-color": props.voidColor,
"disabled-void-color": props.disabledVoidColor,
"fill-color": activeColor.value
});
});
const text = computed(() => {
let result = "";
if (props.showScore) result = props.scoreTemplate.replace(/\{\s*value\s*\}/, rateDisabled.value ? `${props.modelValue}` : `${currentValue.value}`);
else if (props.showText) result = props.texts[Math.ceil(currentValue.value) - 1];
return result;
});
const valueDecimal = computed(() => props.modelValue * 100 - Math.floor(props.modelValue) * 100);
const colorMap = computed(() => isArray(props.colors) ? {
[props.lowThreshold]: props.colors[0],
[props.highThreshold]: {
value: props.colors[1],
excluded: true
},
[props.max]: props.colors[2]
} : props.colors);
const activeColor = computed(() => {
const color = getValueFromMap(currentValue.value, colorMap.value);
return isObject(color) ? "" : color;
});
const decimalStyle = computed(() => {
let width = "";
if (rateDisabled.value) width = `${valueDecimal.value}%`;
else if (props.allowHalf) width = "50%";
return {
color: activeColor.value,
width
};
});
const componentMap = computed(() => {
let icons = isArray(props.icons) ? [...props.icons] : { ...props.icons };
icons = markRaw(icons);
return isArray(icons) ? {
[props.lowThreshold]: icons[0],
[props.highThreshold]: {
value: icons[1],
excluded: true
},
[props.max]: icons[2]
} : icons;
});
const decimalIconComponent = computed(() => getValueFromMap(props.modelValue, componentMap.value));
const voidComponent = computed(() => rateDisabled.value ? isString(props.disabledVoidIcon) ? props.disabledVoidIcon : markRaw(props.disabledVoidIcon) : isString(props.voidIcon) ? props.voidIcon : markRaw(props.voidIcon));
const activeComponent = computed(() => getValueFromMap(currentValue.value, componentMap.value));
function showDecimalIcon(item) {
const showWhenDisabled = rateDisabled.value && valueDecimal.value > 0 && item - 1 < props.modelValue && item > props.modelValue;
const showWhenAllowHalf = props.allowHalf && pointerAtLeftHalf.value && item - .5 <= currentValue.value && item > currentValue.value;
return showWhenDisabled || showWhenAllowHalf;
}
function emitValue(value) {
if (props.clearable && value === props.modelValue) value = 0;
emit(UPDATE_MODEL_EVENT, value);
if (props.modelValue !== value) emit(CHANGE_EVENT, value);
}
function selectValue(value) {
if (rateDisabled.value) return;
if (props.allowHalf && pointerAtLeftHalf.value) emitValue(currentValue.value);
else emitValue(value);
}
function handleKey(e) {
if (rateDisabled.value) return;
const code = getEventCode(e);
const step = props.allowHalf ? .5 : 1;
let _currentValue = currentValue.value;
switch (code) {
case EVENT_CODE.up:
case EVENT_CODE.right:
_currentValue += step;
break;
case EVENT_CODE.left:
case EVENT_CODE.down:
_currentValue -= step;
break;
}
_currentValue = clamp(_currentValue, 0, props.max);
if (_currentValue === currentValue.value) return;
e.stopPropagation();
e.preventDefault();
emit(UPDATE_MODEL_EVENT, _currentValue);
emit(CHANGE_EVENT, _currentValue);
return _currentValue;
}
function setCurrentValue(value, event) {
if (rateDisabled.value) return;
if (props.allowHalf && event) {
pointerAtLeftHalf.value = event.offsetX * 2 <= iconClientWidths.value[value - 1];
currentValue.value = pointerAtLeftHalf.value ? value - .5 : value;
} else currentValue.value = value;
hoverIndex.value = value;
}
function resetCurrentValue() {
if (rateDisabled.value) return;
if (props.allowHalf) pointerAtLeftHalf.value = props.modelValue !== Math.floor(props.modelValue);
currentValue.value = clamp(props.modelValue, 0, props.max);
hoverIndex.value = -1;
}
watch(() => props.modelValue, (val) => {
currentValue.value = clamp(val, 0, props.max);
pointerAtLeftHalf.value = props.modelValue !== Math.floor(props.modelValue);
});
if (!props.modelValue) emit(UPDATE_MODEL_EVENT, 0);
__expose({
setCurrentValue,
resetCurrentValue
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
id: unref(inputId),
class: normalizeClass([rateClasses.value, unref(ns).is("disabled", unref(rateDisabled))]),
role: "slider",
"aria-label": !unref(isLabeledByFormItem) ? __props.ariaLabel || "rating" : void 0,
"aria-labelledby": unref(isLabeledByFormItem) ? unref(formItemContext)?.labelId : void 0,
"aria-valuenow": currentValue.value,
"aria-valuetext": text.value || void 0,
"aria-valuemin": "0",
"aria-valuemax": __props.max,
style: normalizeStyle(rateStyles.value),
tabindex: unref(rateDisabled) ? void 0 : 0,
"aria-disabled": unref(rateDisabled),
onKeydown: handleKey
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.max, (item, key) => {
return openBlock(), createElementBlock("span", {
key,
class: normalizeClass(unref(ns).e("item")),
onMousemove: ($event) => setCurrentValue(item, $event),
onMouseleave: resetCurrentValue,
onClick: ($event) => selectValue(item)
}, [createVNode(unref(ElIcon), {
ref_for: true,
ref_key: "iconRefs",
ref: iconRefs,
class: normalizeClass([
unref(ns).e("icon"),
{ hover: hoverIndex.value === item },
unref(ns).is("active", item <= currentValue.value),
unref(ns).is("focus-visible", item === Math.ceil(currentValue.value || 1))
])
}, {
default: withCtx(() => [
withDirectives((openBlock(), createBlock(resolveDynamicComponent(activeComponent.value), null, null, 512)), [[vShow, !showDecimalIcon(item) && item <= currentValue.value]]),
withDirectives((openBlock(), createBlock(resolveDynamicComponent(voidComponent.value), null, null, 512)), [[vShow, !showDecimalIcon(item) && item > currentValue.value]]),
withDirectives((openBlock(), createBlock(resolveDynamicComponent(voidComponent.value), { class: normalizeClass([unref(ns).em("decimal", "box")]) }, null, 8, ["class"])), [[vShow, showDecimalIcon(item)]]),
withDirectives(createVNode(unref(ElIcon), {
style: normalizeStyle(decimalStyle.value),
class: normalizeClass([unref(ns).e("icon"), unref(ns).e("decimal")])
}, {
default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(decimalIconComponent.value)))]),
_: 1
}, 8, ["style", "class"]), [[vShow, showDecimalIcon(item)]])
]),
_: 2
}, 1032, ["class"])], 42, _hoisted_2);
}), 128)), __props.showText || __props.showScore ? (openBlock(), createElementBlock("span", {
key: 0,
class: normalizeClass(unref(ns).e("text")),
style: normalizeStyle({ color: __props.textColor })
}, toDisplayString(text.value), 7)) : createCommentVNode("v-if", true)], 46, _hoisted_1);
};
}
});
//#endregion
export { rate_vue_vue_type_script_setup_true_lang_default as default };
//# sourceMappingURL=rate.vue_vue_type_script_setup_true_lang.mjs.map
File diff suppressed because one or more lines are too long
+8
View File
@@ -0,0 +1,8 @@
import rate_vue_vue_type_script_setup_true_lang_default from "./rate.vue_vue_type_script_setup_true_lang.mjs";
//#region ../../packages/components/rate/src/rate.vue
var rate_default = rate_vue_vue_type_script_setup_true_lang_default;
//#endregion
export { rate_default as default };
//# sourceMappingURL=rate2.mjs.map
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
import "../../base/style/css.mjs";
import "element-plus/theme-chalk/el-rate.css";
@@ -0,0 +1,2 @@
import "../../base/style/index.mjs";
import "element-plus/theme-chalk/src/rate.scss";