@kine-design/core 0.0.1-beta.5 → 0.0.1-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/components/base/affix/useAffix.ts +2 -1
  2. package/components/base/anchor/useAnchor.ts +2 -1
  3. package/components/base/autoComplete/useAutoComplete.ts +2 -1
  4. package/components/base/carousel/useCarousel.ts +2 -1
  5. package/components/base/cascader/useCascader.ts +2 -1
  6. package/components/base/checkbox/useCheckbox.ts +2 -1
  7. package/components/base/collapse/useCollapse.ts +2 -1
  8. package/components/base/datePicker/__tests__/useDatePicker.test.ts +239 -0
  9. package/components/base/dropdown/useDropdown.ts +2 -1
  10. package/components/base/image/__tests__/useImage.test.ts +174 -0
  11. package/components/base/input/useInput.ts +3 -1
  12. package/components/base/inputNumber/__tests__/useInputNumber.test.ts +153 -0
  13. package/components/base/inputNumber/useInputNumber.ts +53 -11
  14. package/components/base/popover/usePopover.ts +4 -3
  15. package/components/base/rate/useRate.ts +2 -1
  16. package/components/base/select/useSelect.ts +2 -1
  17. package/components/base/slider/useSlider.ts +2 -1
  18. package/components/base/steps/__tests__/useSteps.test.ts +46 -0
  19. package/components/base/switch/useSwitch.tsx +2 -1
  20. package/components/base/tabs/useTabs.ts +2 -1
  21. package/components/base/timePicker/__tests__/useTimePicker.test.ts +118 -0
  22. package/components/base/transfer/useTransfer.ts +2 -1
  23. package/components/base/tree/__tests__/tree.test.ts +214 -0
  24. package/components/message/notification/__tests__/useNotification.test.ts +129 -0
  25. package/components/message/popover/usePopover.ts +4 -4
  26. package/components/other/darkMode/useDarkMode.ts +2 -2
  27. package/components/template/menu/__tests__/useMenu.test.ts +157 -0
  28. package/components/template/pagination/__tests__/usePagination.test.ts +138 -0
  29. package/components/template/table/__tests__/useTable.test.ts +139 -0
  30. package/components/template/table/useTable.ts +2 -4
  31. package/components/types/hook.d.ts +11 -0
  32. package/compositions/common/__tests__/useDebounceFn.test.ts +62 -0
  33. package/compositions/common/__tests__/useEventListener.test.ts +71 -0
  34. package/compositions/common/__tests__/usePopover.test.ts +38 -0
  35. package/compositions/common/__tests__/useTeleport.test.ts +25 -0
  36. package/compositions/common/testAnchor.ts +211 -0
  37. package/compositions/common/useEventListener.ts +3 -3
  38. package/compositions/common/useResizeObserver.ts +6 -2
  39. package/compositions/input/__tests__/useBooleanInput.test.ts +73 -0
  40. package/compositions/modal/__tests__/useModal.test.ts +92 -0
  41. package/compositions/modal/useModal.ts +2 -2
  42. package/compositions/popper/useClickAway.ts +4 -4
  43. package/compositions/utils/__tests__/filters.test.ts +136 -0
  44. package/compositions/virtualList/__tests__/useHeightCache.test.ts +97 -0
  45. package/dist/components/base/affix/useAffix.d.ts +2 -1
  46. package/dist/components/base/anchor/useAnchor.d.ts +2 -1
  47. package/dist/components/base/autoComplete/useAutoComplete.d.ts +2 -1
  48. package/dist/components/base/carousel/useCarousel.d.ts +2 -1
  49. package/dist/components/base/cascader/useCascader.d.ts +2 -1
  50. package/dist/components/base/checkbox/useCheckbox.d.ts +2 -1
  51. package/dist/components/base/collapse/useCollapse.d.ts +2 -1
  52. package/dist/components/base/datePicker/__tests__/useDatePicker.test.d.ts +1 -0
  53. package/dist/components/base/dropdown/useDropdown.d.ts +2 -1
  54. package/dist/components/base/image/__tests__/useImage.test.d.ts +1 -0
  55. package/dist/components/base/input/useInput.d.ts +2 -1
  56. package/dist/components/base/inputNumber/__tests__/useInputNumber.test.d.ts +1 -0
  57. package/dist/components/base/inputNumber/useInputNumber.d.ts +2 -1
  58. package/dist/components/base/popover/usePopover.d.ts +2 -1
  59. package/dist/components/base/rate/useRate.d.ts +2 -1
  60. package/dist/components/base/select/useSelect.d.ts +2 -1
  61. package/dist/components/base/slider/useSlider.d.ts +2 -1
  62. package/dist/components/base/steps/__tests__/useSteps.test.d.ts +1 -0
  63. package/dist/components/base/switch/useSwitch.d.ts +2 -1
  64. package/dist/components/base/tabs/useTabs.d.ts +2 -1
  65. package/dist/components/base/timePicker/__tests__/useTimePicker.test.d.ts +1 -0
  66. package/dist/components/base/transfer/useTransfer.d.ts +2 -1
  67. package/dist/components/base/tree/__tests__/tree.test.d.ts +1 -0
  68. package/dist/components/message/notification/__tests__/useNotification.test.d.ts +1 -0
  69. package/dist/components/message/popover/usePopover.d.ts +1 -1
  70. package/dist/components/other/darkMode/useDarkMode.d.ts +2 -3
  71. package/dist/components/template/menu/__tests__/useMenu.test.d.ts +1 -0
  72. package/dist/components/template/pagination/__tests__/usePagination.test.d.ts +1 -0
  73. package/dist/components/template/table/__tests__/useTable.test.d.ts +1 -0
  74. package/dist/compositions/common/__tests__/useDebounceFn.test.d.ts +1 -0
  75. package/dist/compositions/common/__tests__/useEventListener.test.d.ts +1 -0
  76. package/dist/compositions/common/__tests__/usePopover.test.d.ts +1 -0
  77. package/dist/compositions/common/__tests__/useTeleport.test.d.ts +1 -0
  78. package/dist/compositions/common/testAnchor.d.ts +40 -0
  79. package/dist/compositions/common/useEventListener.d.ts +2 -2
  80. package/dist/compositions/input/__tests__/useBooleanInput.test.d.ts +1 -0
  81. package/dist/compositions/modal/__tests__/useModal.test.d.ts +1 -0
  82. package/dist/compositions/modal/useModal.d.ts +2 -1
  83. package/dist/compositions/popper/useClickAway.d.ts +3 -3
  84. package/dist/compositions/utils/__tests__/filters.test.d.ts +1 -0
  85. package/dist/compositions/virtualList/__tests__/useHeightCache.test.d.ts +1 -0
  86. package/dist/core.js +64 -18
  87. package/dist/tools/__tests__/empty.test.d.ts +1 -0
  88. package/dist/tools/empty.d.ts +2 -2
  89. package/dist/tools/types.d.ts +1 -1
  90. package/dist/vitest.config.d.ts +10 -0
  91. package/package.json +6 -2
  92. package/tools/__tests__/empty.test.ts +72 -0
  93. package/tools/empty.ts +2 -2
  94. package/tools/types.ts +1 -1
  95. package/vitest.config.ts +17 -0
package/dist/core.js CHANGED
@@ -1091,20 +1091,58 @@ var props$39 = {
1091
1091
  * @description inputNumber composable
1092
1092
  * @author 阿怪
1093
1093
  * @date 2026/2/25 00:00
1094
- * @version v1.1.0
1094
+ * @version v1.2.0
1095
1095
  *
1096
1096
  * 江湖的业务千篇一律,复杂的代码好几百行。
1097
+ *
1098
+ * v1.2.0 changelog:
1099
+ * - emit 类型保真:string 入 string 出,number 入 number 出;空值一律 emit null
1100
+ * - 内部 currentValue 保留中间态('-' / '0.' / ''),仅在 emit 时归一
1101
+ */
1102
+ /** 用户输入中间态:不构成可 emit 的数字,emit null */
1103
+ var MID_STATES = new Set([
1104
+ "",
1105
+ "-",
1106
+ ".",
1107
+ "-."
1108
+ ]);
1109
+ /**
1110
+ * 把任意内部值归一为 number | null。
1111
+ * 规则:
1112
+ * - null/undefined/中间态 → null
1113
+ * - 末尾小数点去掉('1.' → '1')
1114
+ * - 非法 NaN → null
1115
+ * - 其他 → Number(v)
1097
1116
  */
1117
+ var normalize = (v) => {
1118
+ if (v === null || v === void 0) return null;
1119
+ const s = String(v);
1120
+ if (MID_STATES.has(s)) return null;
1121
+ const cleaned = s.endsWith(".") ? s.slice(0, -1) : s;
1122
+ const n = Number(cleaned);
1123
+ return Number.isNaN(n) ? null : n;
1124
+ };
1098
1125
  function useInputNumber(props, ctx) {
1099
1126
  const currentValue = ref(props.modelValue ?? "");
1127
+ /**
1128
+ * 按外部 modelValue 的当前 typeof 决定出参类型,保真 v-model 声明。
1129
+ * - null/undefined → null
1130
+ * - 外部当前是 string → emit String(n)
1131
+ * - 否则 → emit n(默认 number)
1132
+ */
1133
+ const toEmitValue = (n) => {
1134
+ if (n === null) return null;
1135
+ return typeof props.modelValue === "string" ? String(n) : n;
1136
+ };
1100
1137
  const updateInput = (oldVal) => {
1101
- ctx.emit("update:modelValue", currentValue.value);
1102
- ctx.emit("change", currentValue.value, oldVal);
1138
+ const out = toEmitValue(normalize(currentValue.value));
1139
+ ctx.emit("update:modelValue", out);
1140
+ ctx.emit("change", out, oldVal);
1103
1141
  };
1104
1142
  const setCurrentValue = (newVal, e) => {
1105
1143
  const oldVal = currentValue.value;
1106
1144
  const { min, max, precision } = props;
1107
- if (oldVal === newVal) return;
1145
+ if (String(oldVal) === String(newVal)) return;
1108
1146
  else if (+newVal >= +max) newVal = max;
1109
1147
  else if (+newVal <= +min) newVal = min;
1110
1148
  else if (precision !== 0 && String(newVal).includes(".") && `${newVal}`.length - (`${newVal}`.indexOf(".") + 1) >= precision) newVal = Number(`${newVal}`.substring(0, `${newVal}`.indexOf(".") + (precision + 1)));
@@ -1132,11 +1170,15 @@ function useInputNumber(props, ctx) {
1132
1170
  validate(val, e);
1133
1171
  };
1134
1172
  const handleInputBlur = () => {
1173
+ const oldVal = currentValue.value;
1135
1174
  if (currentValue.value === "-") currentValue.value = "";
1136
1175
  else if (currentValue.value === "-0") currentValue.value = 0;
1137
- const oldVal = currentValue.value;
1138
- const str = String(currentValue.value);
1139
- currentValue.value = str.indexOf(".") === str.length - 1 ? str.replace(/\.$/g, "") : currentValue.value;
1176
+ else {
1177
+ const str = String(currentValue.value);
1178
+ if (str.indexOf(".") === str.length - 1 && str.length > 0) currentValue.value = str.replace(/\.$/g, "");
1179
+ }
1180
+ const n = normalize(currentValue.value);
1181
+ currentValue.value = n === null ? "" : n;
1140
1182
  updateInput(oldVal);
1141
1183
  };
1142
1184
  /**
@@ -1252,13 +1294,17 @@ function useResizeObserver(target, callback, options = {}) {
1252
1294
  observer = void 0;
1253
1295
  }
1254
1296
  };
1255
- watch(target, () => {
1297
+ const stopWatch = watch(target, () => {
1256
1298
  if (target.value) {
1257
1299
  cleanup();
1258
1300
  observer = new ResizeObserver(callback);
1259
1301
  observer.observe(target.value, options);
1260
1302
  }
1261
1303
  });
1304
+ onBeforeUnmount(() => {
1305
+ cleanup();
1306
+ stopWatch();
1307
+ });
1262
1308
  return { cleanup };
1263
1309
  }
1264
1310
  //#endregion
@@ -1642,14 +1688,14 @@ function useEventListener(options) {
1642
1688
  const onMounted = () => {
1643
1689
  add();
1644
1690
  };
1645
- const onBeforeDestroy = () => {
1691
+ const onBeforeUnmount = () => {
1646
1692
  remove();
1647
1693
  };
1648
1694
  return {
1649
1695
  add,
1650
1696
  remove,
1651
1697
  onMounted,
1652
- onBeforeDestroy
1698
+ onBeforeUnmount
1653
1699
  };
1654
1700
  }
1655
1701
  //#endregion
@@ -1807,8 +1853,8 @@ function usePopover$1(props, ctx) {
1807
1853
  });
1808
1854
  onBeforeUnmount(() => {
1809
1855
  if (clickAwayInstance) {
1810
- const { onBeforeDestroy } = clickAwayInstance;
1811
- onBeforeDestroy();
1856
+ const { onBeforeUnmount } = clickAwayInstance;
1857
+ onBeforeUnmount();
1812
1858
  }
1813
1859
  instance?.destroy();
1814
1860
  });
@@ -3418,8 +3464,8 @@ function useTable() {
3418
3464
  });
3419
3465
  /** 从 data[i] 中安全取值 */
3420
3466
  const getData = (i, param) => {
3421
- if (data[i] && data[i][param]) return data[i][param];
3422
- return "";
3467
+ if (data[i] == null) return "";
3468
+ return data[i][param] ?? "";
3423
3469
  };
3424
3470
  /** 向每行的 td 列表中追加一列 */
3425
3471
  const pushTd = (param, bodySlot, style) => {
@@ -4009,7 +4055,7 @@ function usePopover(options, lifecycle) {
4009
4055
  const popoverLeave = () => {
4010
4056
  if (props.hover) instance?.hide();
4011
4057
  };
4012
- const onBeforeDestroyEvents = [];
4058
+ const onBeforeUnmountEvents = [];
4013
4059
  onMounted(() => {
4014
4060
  if (!popoverRef.value || !contentRef.value) return;
4015
4061
  popperInstance.value = createPopover(popoverRef.value, contentRef.value, arrowRef.value, {
@@ -4026,8 +4072,8 @@ function usePopover(options, lifecycle) {
4026
4072
  });
4027
4073
  onBeforeUnmount(() => {
4028
4074
  if (clickAwayInstance) {
4029
- const { onBeforeDestroy } = clickAwayInstance;
4030
- onBeforeDestroy();
4075
+ const { onBeforeUnmount } = clickAwayInstance;
4076
+ onBeforeUnmount();
4031
4077
  }
4032
4078
  instance?.destroy();
4033
4079
  });
@@ -4053,7 +4099,7 @@ function usePopover(options, lifecycle) {
4053
4099
  popperInstance,
4054
4100
  style,
4055
4101
  arrowStyle,
4056
- lifecycle: { onBeforeDestroyEvents }
4102
+ lifecycle: { onBeforeUnmountEvents }
4057
4103
  };
4058
4104
  }
4059
4105
  //#endregion
@@ -0,0 +1 @@
1
+ export {};
@@ -10,9 +10,9 @@
10
10
  * 支持稍多类型的判断非空的方法
11
11
  * @param value
12
12
  */
13
- export declare const notEmpty: (value: any) => number | boolean | undefined;
13
+ export declare const notEmpty: (value: unknown) => number | boolean | undefined;
14
14
  /**
15
15
  * 判断为空的方法,即notEmpty的取反
16
16
  * @param value
17
17
  */
18
- export declare const isEmpty: (value: any) => boolean;
18
+ export declare const isEmpty: (value: unknown) => boolean;
@@ -6,4 +6,4 @@
6
6
  *
7
7
  * 江湖的业务千篇一律,复杂的代码好几百行。
8
8
  */
9
- export declare const isBoolean: (val: any) => val is boolean;
9
+ export declare const isBoolean: (val: unknown) => val is boolean;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @description @kine-design/core 单元测试配置
3
+ * @author 阿怪
4
+ * @date 2026/3/23
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ declare const _default: import('vite').UserConfig;
10
+ export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kine-design/core",
3
- "version": "0.0.1-beta.5",
3
+ "version": "0.0.1-beta.7",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/core.js",
@@ -19,8 +19,12 @@
19
19
  "dist"
20
20
  ]
21
21
  },
22
+ "devDependencies": {
23
+ "vitest": "^4.1.0"
24
+ },
22
25
  "scripts": {
23
- "build": "vite build --config vite.config.build.ts"
26
+ "build": "vite build --config vite.config.build.ts",
27
+ "test": "vitest --config vitest.config.ts --run"
24
28
  },
25
29
  "module": "./dist/core.js",
26
30
  "exports": {
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @description notEmpty / isEmpty 工具函数测试
3
+ * @author 阿怪
4
+ * @date 2026/3/23
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+ import { describe, expect, it } from 'vitest';
10
+ import { notEmpty, isEmpty } from '../empty';
11
+
12
+ describe('notEmpty', () => {
13
+ // 基本空值
14
+ it('null 为空', () => expect(notEmpty(null)).toBe(false));
15
+ it('undefined 为空', () => expect(notEmpty(undefined)).toBe(false));
16
+
17
+ // 字符串
18
+ it('空字符串为空', () => expect(notEmpty('')).toBe(false));
19
+ it('非空字符串非空', () => expect(notEmpty('hello')).toBe(true));
20
+
21
+ // 数字
22
+ it('0 非空', () => expect(notEmpty(0)).toBe(true));
23
+ it('正数非空', () => expect(notEmpty(42)).toBe(true));
24
+ it('NaN 非空(数字类型)', () => expect(notEmpty(NaN)).toBe(true));
25
+
26
+ // 布尔
27
+ it('false 非空', () => expect(notEmpty(false)).toBe(true));
28
+ it('true 非空', () => expect(notEmpty(true)).toBe(true));
29
+
30
+ // 函数
31
+ it('函数非空', () => expect(notEmpty(() => {})).toBe(true));
32
+
33
+ // Symbol
34
+ it('空 Symbol 为空', () => expect(notEmpty(Symbol())).toBe(false));
35
+ it('有描述的 Symbol 非空', () => expect(notEmpty(Symbol('desc'))).toBe(true));
36
+
37
+ // 数组
38
+ it('空数组为空', () => expect(notEmpty([])).toBe(false));
39
+ it('非空数组非空', () => expect(notEmpty([1])).toBe(true));
40
+
41
+ // 对象
42
+ it('空对象为空', () => expect(notEmpty({})).toBe(false));
43
+ it('非空对象非空', () => expect(notEmpty({ a: 1 })).toBe(true));
44
+
45
+ // Map / Set
46
+ it('空 Map 为空', () => expect(notEmpty(new Map())).toBe(false));
47
+ it('非空 Map 非空', () => expect(notEmpty(new Map([['a', 1]]))).toBe(true));
48
+ it('空 Set 为空', () => expect(notEmpty(new Set())).toBe(false));
49
+ it('非空 Set 非空', () => expect(notEmpty(new Set([1]))).toBe(true));
50
+
51
+ // Date
52
+ it('有效 Date 非空', () => expect(notEmpty(new Date())).toBe(true));
53
+ it('Invalid Date 为空', () => expect(notEmpty(new Date('invalid'))).toBe(false));
54
+
55
+ // RegExp
56
+ it('空正则为空', () => expect(notEmpty(new RegExp(''))).toBe(false));
57
+ it('非空正则非空', () => expect(notEmpty(/abc/)).toBe(true));
58
+
59
+ // TypedArray
60
+ it('空 Uint8Array 为空', () => expect(notEmpty(new Uint8Array(0))).toBe(0));
61
+ it('非空 Float64Array 非空', () => expect(notEmpty(new Float64Array([1.5]))).toBe(1));
62
+ });
63
+
64
+ describe('isEmpty', () => {
65
+ it('是 notEmpty 的取反', () => {
66
+ expect(isEmpty(null)).toBe(true);
67
+ expect(isEmpty('hello')).toBe(false);
68
+ expect(isEmpty(0)).toBe(false);
69
+ expect(isEmpty([])).toBe(true);
70
+ expect(isEmpty({})).toBe(true);
71
+ });
72
+ });
package/tools/empty.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  * 支持稍多类型的判断非空的方法
12
12
  * @param value
13
13
  */
14
- export const notEmpty = (value: any) => {
14
+ export const notEmpty = (value: unknown) => {
15
15
  // 先处理基本类型
16
16
  // null
17
17
  // undefined
@@ -78,4 +78,4 @@ export const notEmpty = (value: any) => {
78
78
  * 判断为空的方法,即notEmpty的取反
79
79
  * @param value
80
80
  */
81
- export const isEmpty = (value: any) => !notEmpty(value);
81
+ export const isEmpty = (value: unknown) => !notEmpty(value);
package/tools/types.ts CHANGED
@@ -8,4 +8,4 @@
8
8
  */
9
9
 
10
10
 
11
- export const isBoolean = (val: any): val is boolean => typeof val === 'boolean';
11
+ export const isBoolean = (val: unknown): val is boolean => typeof val === 'boolean';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @description @kine-design/core 单元测试配置
3
+ * @author 阿怪
4
+ * @date 2026/3/23
5
+ * @version v1.0.0
6
+ *
7
+ * 江湖的业务千篇一律,复杂的代码好几百行。
8
+ */
9
+
10
+ import { defineConfig } from 'vitest/config';
11
+
12
+ export default defineConfig({
13
+ root: __dirname,
14
+ test: {
15
+ include: ['**/__tests__/*.test.ts'],
16
+ },
17
+ });