@yh-ui/utils 0.1.0

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.
package/dist/index.cjs ADDED
@@ -0,0 +1,378 @@
1
+ 'use strict';
2
+
3
+ const isString = (val) => typeof val === "string";
4
+ const isNumber = (val) => typeof val === "number";
5
+ const isBoolean = (val) => typeof val === "boolean";
6
+ const isFunction = (val) => typeof val === "function";
7
+ const isObject = (val) => val !== null && typeof val === "object";
8
+ const isPromise = (val) => isObject(val) && isFunction(val.then) && isFunction(val.catch);
9
+ const isArray = Array.isArray;
10
+ const isUndefined = (val) => val === void 0;
11
+ const isNil = (val) => val == null;
12
+ const isEmpty = (val) => {
13
+ if (isNil(val)) return true;
14
+ if (isArray(val)) return val.length === 0;
15
+ if (isString(val)) return val.trim().length === 0;
16
+ if (isObject(val)) return Object.keys(val).length === 0;
17
+ return false;
18
+ };
19
+ const isNumeric = (val) => !isNil(val) && !isNaN(parseFloat(String(val))) && isFinite(Number(val));
20
+
21
+ const isClient = typeof window !== "undefined";
22
+ const isServer = !isClient;
23
+ const getStyle = (element, styleName) => {
24
+ if (!isClient || !element || !styleName) return "";
25
+ try {
26
+ const style = element.style[styleName];
27
+ if (style) return style;
28
+ const computed = document.defaultView?.getComputedStyle(element, "");
29
+ return computed ? computed[styleName] : "";
30
+ } catch {
31
+ return element.style[styleName];
32
+ }
33
+ };
34
+ const setStyle = (element, styleName, value) => {
35
+ if (!element || !styleName) return;
36
+ if (typeof styleName === "object") {
37
+ Object.entries(styleName).forEach(([key, val]) => {
38
+ setStyle(element, key, val);
39
+ });
40
+ } else {
41
+ element.style[styleName] = value ?? "";
42
+ }
43
+ };
44
+ const hasClass = (el, cls) => {
45
+ if (!el || !cls) return false;
46
+ if (cls.includes(" ")) throw new Error("className should not contain space.");
47
+ return el.classList.contains(cls);
48
+ };
49
+ const addClass = (el, cls) => {
50
+ if (!el || !cls.trim()) return;
51
+ el.classList.add(...cls.split(" ").filter(Boolean));
52
+ };
53
+ const removeClass = (el, cls) => {
54
+ if (!el || !cls.trim()) return;
55
+ el.classList.remove(...cls.split(" ").filter(Boolean));
56
+ };
57
+ const toggleClass = (el, cls, force) => {
58
+ if (!el || !cls.trim()) return;
59
+ el.classList.toggle(cls, force);
60
+ };
61
+ const getScrollContainer = (el, isVertical) => {
62
+ if (!isClient) return void 0;
63
+ let parent = el;
64
+ while (parent) {
65
+ if ([document.documentElement, document.body].includes(parent)) {
66
+ return window;
67
+ }
68
+ const overflow = isVertical ? getStyle(parent, "overflowY") : getStyle(parent, "overflow");
69
+ if (/(scroll|auto)/.test(overflow)) {
70
+ return parent;
71
+ }
72
+ parent = parent.parentNode;
73
+ }
74
+ return void 0;
75
+ };
76
+ const isInViewport = (el) => {
77
+ if (!isClient || !el) return false;
78
+ const rect = el.getBoundingClientRect();
79
+ return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
80
+ };
81
+
82
+ const withInstall = (main, extra) => {
83
+ main.install = (app) => {
84
+ for (const comp of [main, ...Object.values(extra ?? {})]) {
85
+ const name = comp.name || comp.__name;
86
+ if (name) {
87
+ app.component(name, comp);
88
+ }
89
+ }
90
+ };
91
+ if (extra) {
92
+ for (const [key, comp] of Object.entries(extra)) {
93
+ main[key] = comp;
94
+ }
95
+ }
96
+ return main;
97
+ };
98
+ const withNoopInstall = (component) => {
99
+ component.install = () => {
100
+ };
101
+ return component;
102
+ };
103
+ const withInstallFunction = (fn, name) => {
104
+ const func = fn;
105
+ func.install = (app) => {
106
+ app.config.globalProperties[name] = fn;
107
+ };
108
+ return func;
109
+ };
110
+ const withInstallDirective = (directive, name) => {
111
+ const dir = directive;
112
+ dir.install = (app) => {
113
+ app.directive(name, dir);
114
+ };
115
+ return dir;
116
+ };
117
+ const withInstallAll = (components, directives) => {
118
+ return {
119
+ install(app) {
120
+ components.forEach((component) => {
121
+ const name = component.name || component.__name;
122
+ if (name) {
123
+ app.component(name, component);
124
+ }
125
+ });
126
+ if (directives) {
127
+ Object.entries(directives).forEach(([name, directive]) => {
128
+ app.directive(name, directive);
129
+ });
130
+ }
131
+ }
132
+ };
133
+ };
134
+
135
+ let idCounter = 0;
136
+ const generateId = (prefix = "yh") => {
137
+ return `${prefix}-${Date.now()}-${++idCounter}`;
138
+ };
139
+ const debounce = (fn, delay) => {
140
+ let timer = null;
141
+ const debounced = (...args) => {
142
+ if (timer) clearTimeout(timer);
143
+ timer = setTimeout(() => {
144
+ fn(...args);
145
+ timer = null;
146
+ }, delay);
147
+ };
148
+ debounced.cancel = () => {
149
+ if (timer) {
150
+ clearTimeout(timer);
151
+ timer = null;
152
+ }
153
+ };
154
+ return debounced;
155
+ };
156
+ const throttle = (fn, delay) => {
157
+ let lastTime = 0;
158
+ let timer = null;
159
+ const throttled = (...args) => {
160
+ const now = Date.now();
161
+ const remaining = delay - (now - lastTime);
162
+ if (remaining <= 0) {
163
+ if (timer) {
164
+ clearTimeout(timer);
165
+ timer = null;
166
+ }
167
+ fn(...args);
168
+ lastTime = now;
169
+ } else if (!timer) {
170
+ timer = setTimeout(() => {
171
+ fn(...args);
172
+ lastTime = Date.now();
173
+ timer = null;
174
+ }, remaining);
175
+ }
176
+ };
177
+ throttled.cancel = () => {
178
+ if (timer) {
179
+ clearTimeout(timer);
180
+ timer = null;
181
+ }
182
+ lastTime = 0;
183
+ };
184
+ return throttled;
185
+ };
186
+ const deepClone = (obj) => {
187
+ if (obj === null || typeof obj !== "object") return obj;
188
+ if (obj instanceof Date) return new Date(obj.getTime());
189
+ if (obj instanceof Array) {
190
+ return obj.map((item) => deepClone(item));
191
+ }
192
+ if (obj instanceof Object) {
193
+ const copy = {};
194
+ Object.keys(obj).forEach((key) => {
195
+ copy[key] = deepClone(obj[key]);
196
+ });
197
+ return copy;
198
+ }
199
+ return obj;
200
+ };
201
+ const deepMerge = (target, ...sources) => {
202
+ if (!sources.length) return target;
203
+ const source = sources.shift();
204
+ if (source === void 0) return target;
205
+ for (const key in source) {
206
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
207
+ const targetValue = target[key];
208
+ const sourceValue = source[key];
209
+ if (typeof targetValue === "object" && targetValue !== null && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(targetValue) && !Array.isArray(sourceValue)) {
210
+ target[key] = deepMerge(
211
+ { ...targetValue },
212
+ sourceValue
213
+ );
214
+ } else {
215
+ target[key] = sourceValue;
216
+ }
217
+ }
218
+ }
219
+ return deepMerge(target, ...sources);
220
+ };
221
+ const toArray = (val) => {
222
+ return Array.isArray(val) ? val : [val];
223
+ };
224
+ const capitalize = (str) => {
225
+ return str.charAt(0).toUpperCase() + str.slice(1);
226
+ };
227
+ const kebabCase = (str) => {
228
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
229
+ };
230
+ const camelCase = (str) => {
231
+ return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
232
+ };
233
+ const sleep = (ms) => {
234
+ return new Promise((resolve) => setTimeout(resolve, ms));
235
+ };
236
+ const get = (obj, path, defaultValue) => {
237
+ const result = path.split(".").reduce((res, key) => {
238
+ if (res !== null && res !== void 0 && typeof res === "object") {
239
+ return res[key];
240
+ }
241
+ return void 0;
242
+ }, obj);
243
+ return result === void 0 ? defaultValue : result;
244
+ };
245
+ const set = (obj, path, value) => {
246
+ if (Object(obj) !== obj) return obj;
247
+ const keys = path.split(".");
248
+ const lastKey = keys.pop();
249
+ const node = keys.reduce(
250
+ (res, key) => {
251
+ if (res[key] === void 0) res[key] = {};
252
+ return res[key];
253
+ },
254
+ obj
255
+ );
256
+ node[lastKey] = value;
257
+ return obj;
258
+ };
259
+ const retry = async (fn, retries = 3, delay = 1e3) => {
260
+ try {
261
+ return await fn();
262
+ } catch (error) {
263
+ if (retries > 0) {
264
+ await sleep(delay);
265
+ return retry(fn, retries - 1, delay);
266
+ }
267
+ throw error;
268
+ }
269
+ };
270
+
271
+ const addUnit = (value, unit = "px") => {
272
+ if (value === void 0 || value === null || value === "") return void 0;
273
+ if (isNumeric(value)) {
274
+ return `${value}${unit}`;
275
+ }
276
+ return String(value);
277
+ };
278
+ const removeUnit = (value) => {
279
+ return parseFloat(value) || 0;
280
+ };
281
+ const hexToRgb = (hex) => {
282
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
283
+ return result ? {
284
+ r: parseInt(result[1], 16),
285
+ g: parseInt(result[2], 16),
286
+ b: parseInt(result[3], 16)
287
+ } : null;
288
+ };
289
+ const rgbToHex = (r, g, b) => {
290
+ const toHex = (c) => {
291
+ const hex = c.toString(16);
292
+ return hex.length === 1 ? "0" + hex : hex;
293
+ };
294
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
295
+ };
296
+ const adjustColorBrightness = (color, amount) => {
297
+ const rgb = hexToRgb(color);
298
+ if (!rgb) return color;
299
+ const adjust = (value) => {
300
+ const newValue = value + amount;
301
+ return Math.max(0, Math.min(255, newValue));
302
+ };
303
+ return rgbToHex(adjust(rgb.r), adjust(rgb.g), adjust(rgb.b));
304
+ };
305
+ const generateColorPalette = (baseColor, levels = [1, 2, 3, 4, 5, 6, 7, 8, 9]) => {
306
+ const palette = {};
307
+ const rgb = hexToRgb(baseColor);
308
+ if (!rgb) return palette;
309
+ levels.forEach((level) => {
310
+ const factor = (10 - level) / 10;
311
+ palette[level] = rgbToHex(
312
+ Math.round(rgb.r + (255 - rgb.r) * factor),
313
+ Math.round(rgb.g + (255 - rgb.g) * factor),
314
+ Math.round(rgb.b + (255 - rgb.b) * factor)
315
+ );
316
+ });
317
+ return palette;
318
+ };
319
+ const setCssVar = (name, value, element = document.documentElement) => {
320
+ element.style.setProperty(name, value);
321
+ };
322
+ const getCssVar = (name, element = document.documentElement) => {
323
+ return getComputedStyle(element).getPropertyValue(name).trim();
324
+ };
325
+ const setCssVars = (vars, element = document.documentElement) => {
326
+ Object.entries(vars).forEach(([name, value]) => {
327
+ setCssVar(name, value, element);
328
+ });
329
+ };
330
+
331
+ exports.addClass = addClass;
332
+ exports.addUnit = addUnit;
333
+ exports.adjustColorBrightness = adjustColorBrightness;
334
+ exports.camelCase = camelCase;
335
+ exports.capitalize = capitalize;
336
+ exports.debounce = debounce;
337
+ exports.deepClone = deepClone;
338
+ exports.deepMerge = deepMerge;
339
+ exports.generateColorPalette = generateColorPalette;
340
+ exports.generateId = generateId;
341
+ exports.get = get;
342
+ exports.getCssVar = getCssVar;
343
+ exports.getScrollContainer = getScrollContainer;
344
+ exports.getStyle = getStyle;
345
+ exports.hasClass = hasClass;
346
+ exports.hexToRgb = hexToRgb;
347
+ exports.isArray = isArray;
348
+ exports.isBoolean = isBoolean;
349
+ exports.isClient = isClient;
350
+ exports.isEmpty = isEmpty;
351
+ exports.isFunction = isFunction;
352
+ exports.isInViewport = isInViewport;
353
+ exports.isNil = isNil;
354
+ exports.isNumber = isNumber;
355
+ exports.isNumeric = isNumeric;
356
+ exports.isObject = isObject;
357
+ exports.isPromise = isPromise;
358
+ exports.isServer = isServer;
359
+ exports.isString = isString;
360
+ exports.isUndefined = isUndefined;
361
+ exports.kebabCase = kebabCase;
362
+ exports.removeClass = removeClass;
363
+ exports.removeUnit = removeUnit;
364
+ exports.retry = retry;
365
+ exports.rgbToHex = rgbToHex;
366
+ exports.set = set;
367
+ exports.setCssVar = setCssVar;
368
+ exports.setCssVars = setCssVars;
369
+ exports.setStyle = setStyle;
370
+ exports.sleep = sleep;
371
+ exports.throttle = throttle;
372
+ exports.toArray = toArray;
373
+ exports.toggleClass = toggleClass;
374
+ exports.withInstall = withInstall;
375
+ exports.withInstallAll = withInstallAll;
376
+ exports.withInstallDirective = withInstallDirective;
377
+ exports.withInstallFunction = withInstallFunction;
378
+ exports.withNoopInstall = withNoopInstall;
@@ -0,0 +1,236 @@
1
+ import { Plugin, App, Component, Directive } from 'vue';
2
+
3
+ /**
4
+ * Type utilities
5
+ * @description 基础类型工具库,彻底移除 any,支持更严谨的类型推断
6
+ */
7
+
8
+ /**
9
+ * 可空类型
10
+ */
11
+ type Nullable<T> = T | null;
12
+ /**
13
+ * 可异步类型
14
+ */
15
+ type Awaitable<T> = T | Promise<T>;
16
+ /**
17
+ * 数组或单值类型
18
+ */
19
+ type Arrayable<T> = T | T[];
20
+ /**
21
+ * 通用对象类型
22
+ */
23
+ type Recordable<T = unknown> = Record<string, T>;
24
+ /**
25
+ * 组件尺寸标准
26
+ */
27
+ type ComponentSize = 'large' | 'default' | 'small';
28
+ /**
29
+ * Vue 插件类型(带安装器)
30
+ */
31
+ type SFCWithInstall<T> = T & Plugin & {
32
+ install: (app: App, options?: Record<string, unknown>) => void;
33
+ };
34
+ /**
35
+ * 类型判断工具函数
36
+ */
37
+ declare const isString: (val: unknown) => val is string;
38
+ declare const isNumber: (val: unknown) => val is number;
39
+ declare const isBoolean: (val: unknown) => val is boolean;
40
+ declare const isFunction: (val: unknown) => val is (...args: unknown[]) => unknown;
41
+ declare const isObject: (val: unknown) => val is Record<string, unknown>;
42
+ declare const isPromise: <T = unknown>(val: unknown) => val is Promise<T>;
43
+ declare const isArray: (arg: any) => arg is any[];
44
+ declare const isUndefined: (val: unknown) => val is undefined;
45
+ declare const isNil: (val: unknown) => val is null | undefined;
46
+ /**
47
+ * 判断是否为空值(null, undefined, '', [], {})
48
+ */
49
+ declare const isEmpty: (val: unknown) => boolean;
50
+ /**
51
+ * 判断是否为有效的数字字符串
52
+ */
53
+ declare const isNumeric: (val: unknown) => val is string | number;
54
+
55
+ /**
56
+ * DOM utilities
57
+ */
58
+ /**
59
+ * 判断是否在浏览器环境
60
+ */
61
+ declare const isClient: boolean;
62
+ /**
63
+ * 判断是否在服务端环境
64
+ */
65
+ declare const isServer: boolean;
66
+ /**
67
+ * 获取元素的样式
68
+ */
69
+ declare const getStyle: (element: HTMLElement, styleName: string) => string;
70
+ /**
71
+ * 设置元素的样式
72
+ */
73
+ declare const setStyle: (element: HTMLElement, styleName: string | Record<string, string>, value?: string) => void;
74
+ /**
75
+ * 检查元素是否包含某个类名
76
+ */
77
+ declare const hasClass: (el: HTMLElement, cls: string) => boolean;
78
+ /**
79
+ * 添加类名
80
+ */
81
+ declare const addClass: (el: HTMLElement, cls: string) => void;
82
+ /**
83
+ * 移除类名
84
+ */
85
+ declare const removeClass: (el: HTMLElement, cls: string) => void;
86
+ /**
87
+ * 切换类名
88
+ */
89
+ declare const toggleClass: (el: HTMLElement, cls: string, force?: boolean) => void;
90
+ /**
91
+ * 获取滚动容器
92
+ */
93
+ declare const getScrollContainer: (el: HTMLElement, isVertical?: boolean) => HTMLElement | Window | undefined;
94
+ /**
95
+ * 检查元素是否在视口中
96
+ */
97
+ declare const isInViewport: (el: HTMLElement) => boolean;
98
+
99
+ /**
100
+ * Vue 相关的工具函数
101
+ * @description 获取组件名、添加安装方法等
102
+ */
103
+
104
+ /**
105
+ * 为组件添加 install 方法,使其可以作为 Vue 插件使用
106
+ */
107
+ declare const withInstall: <T extends Component, E extends Record<string, unknown>>(main: T, extra?: E) => SFCWithInstall<T> & E;
108
+ /**
109
+ * 空安装方法,用于不需要独立安装的子组件
110
+ */
111
+ declare const withNoopInstall: <T extends Component>(component: T) => SFCWithInstall<T>;
112
+ /**
113
+ * 为函数式组件或工具添加 install 方法
114
+ */
115
+ declare const withInstallFunction: <T>(fn: T, name: string) => SFCWithInstall<T>;
116
+ /**
117
+ * 为指令添加 install 方法
118
+ */
119
+ declare const withInstallDirective: <T extends Directive>(directive: T, name: string) => SFCWithInstall<T>;
120
+ /**
121
+ * 批量注册组件和指令
122
+ */
123
+ declare const withInstallAll: (components: Component[], directives?: Record<string, Directive>) => Plugin;
124
+ /**
125
+ * 用于 defineOptions 的类型辅助
126
+ */
127
+ interface ComponentOptions {
128
+ name: string;
129
+ inheritAttrs?: boolean;
130
+ }
131
+
132
+ /**
133
+ * Common utilities
134
+ * @description 通用工具函数库,严格类型化
135
+ */
136
+ declare const generateId: (prefix?: string) => string;
137
+ /**
138
+ * 函数类型定义
139
+ */
140
+ type AnyFunction = (...args: unknown[]) => unknown;
141
+ /**
142
+ * 防抖函数
143
+ */
144
+ declare const debounce: <T extends AnyFunction>(fn: T, delay: number) => ((...args: Parameters<T>) => void) & {
145
+ cancel: () => void;
146
+ };
147
+ /**
148
+ * 节流函数
149
+ */
150
+ declare const throttle: <T extends AnyFunction>(fn: T, delay: number) => ((...args: Parameters<T>) => void) & {
151
+ cancel: () => void;
152
+ };
153
+ /**
154
+ * 深拷贝
155
+ */
156
+ declare const deepClone: <T>(obj: T) => T;
157
+ /**
158
+ * 深度合并对象
159
+ */
160
+ declare const deepMerge: <T extends Record<string, unknown>>(target: T, ...sources: Partial<T>[]) => T;
161
+ /**
162
+ * 将值转换为数组
163
+ */
164
+ declare const toArray: <T>(val: T | T[]) => T[];
165
+ /**
166
+ * 首字母大写
167
+ */
168
+ declare const capitalize: (str: string) => string;
169
+ /**
170
+ * 转换为 kebab-case
171
+ */
172
+ declare const kebabCase: (str: string) => string;
173
+ /**
174
+ * 转换为 camelCase
175
+ */
176
+ declare const camelCase: (str: string) => string;
177
+ /**
178
+ * 休眠函数
179
+ */
180
+ declare const sleep: (ms: number) => Promise<void>;
181
+ /**
182
+ * 访问对象嵌套路径的值
183
+ */
184
+ declare const get: <T = unknown>(obj: Record<string, unknown>, path: string, defaultValue?: T) => T;
185
+ /**
186
+ * 设置对象嵌套路径的值
187
+ */
188
+ declare const set: <T extends Record<string, unknown>>(obj: T, path: string, value: unknown) => T;
189
+ /**
190
+ * 异步函数重试
191
+ */
192
+ declare const retry: <T>(fn: () => Promise<T>, retries?: number, delay?: number) => Promise<T>;
193
+
194
+ /**
195
+ * 添加单位
196
+ */
197
+ declare const addUnit: (value?: string | number, unit?: string) => string | undefined;
198
+ /**
199
+ * 移除单位,返回数值
200
+ */
201
+ declare const removeUnit: (value: string) => number;
202
+ /**
203
+ * 将十六进制颜色转换为 RGB
204
+ */
205
+ declare const hexToRgb: (hex: string) => {
206
+ r: number;
207
+ g: number;
208
+ b: number;
209
+ } | null;
210
+ /**
211
+ * 将 RGB 转换为十六进制颜色
212
+ */
213
+ declare const rgbToHex: (r: number, g: number, b: number) => string;
214
+ /**
215
+ * 调整颜色亮度
216
+ */
217
+ declare const adjustColorBrightness: (color: string, amount: number) => string;
218
+ /**
219
+ * 生成颜色的色阶
220
+ */
221
+ declare const generateColorPalette: (baseColor: string, levels?: number[]) => Record<number, string>;
222
+ /**
223
+ * 设置 CSS 变量
224
+ */
225
+ declare const setCssVar: (name: string, value: string, element?: HTMLElement) => void;
226
+ /**
227
+ * 获取 CSS 变量
228
+ */
229
+ declare const getCssVar: (name: string, element?: HTMLElement) => string;
230
+ /**
231
+ * 批量设置 CSS 变量
232
+ */
233
+ declare const setCssVars: (vars: Record<string, string>, element?: HTMLElement) => void;
234
+
235
+ export { addClass, addUnit, adjustColorBrightness, camelCase, capitalize, debounce, deepClone, deepMerge, generateColorPalette, generateId, get, getCssVar, getScrollContainer, getStyle, hasClass, hexToRgb, isArray, isBoolean, isClient, isEmpty, isFunction, isInViewport, isNil, isNumber, isNumeric, isObject, isPromise, isServer, isString, isUndefined, kebabCase, removeClass, removeUnit, retry, rgbToHex, set, setCssVar, setCssVars, setStyle, sleep, throttle, toArray, toggleClass, withInstall, withInstallAll, withInstallDirective, withInstallFunction, withNoopInstall };
236
+ export type { Arrayable, Awaitable, ComponentOptions, ComponentSize, Nullable, Recordable, SFCWithInstall };