nhanh-pure-function 1.0.1 → 1.2.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/lib/Index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  export * from "./Utility";
2
2
  export * from "./User";
3
-
3
+
4
+ // 提取单个函数的参数类型
5
+ export type ExtractParameters<T> = T extends (...args: infer P) => any
6
+ ? P
7
+ : never;
package/lib/Index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import "./index.css";
2
+
1
3
  export * from "./Utility";
2
4
  export * from "./User";
3
-
package/lib/User.d.ts CHANGED
@@ -3,14 +3,17 @@
3
3
  * @param {滚动标签} element
4
4
  * @param {触底事件} callback
5
5
  */
6
- export function _AddScrollBottomListener(element: HTMLElement, callback: Function): void;
6
+ export function _AddScrollBottomListener(
7
+ element: HTMLElement,
8
+ callback: Function
9
+ ): void;
7
10
 
8
11
  /**
9
12
  * 自动处理 currentPage * pageSize > total
10
13
  * @param ask 请求方法
11
14
  * @param config 请求参数
12
15
  */
13
- export function _PagingQuery<Ask extends Function>(
16
+ export function _PagingQuery<Ask extends (...args: any[]) => any>(
14
17
  ask: Ask,
15
18
  config: {
16
19
  currentPage: number;
@@ -67,10 +70,10 @@ export function _SetQuantifierAttribute<T>(
67
70
  */
68
71
  export function _SetDefaultValue<T>(
69
72
  data: T,
70
- options: {
73
+ options?: {
71
74
  defaultValue?: string;
72
75
  fieldsNotRequiringAction?: (string | number | symbol)[];
73
- } = {}
76
+ }
74
77
  ): T;
75
78
 
76
79
  /**
@@ -113,7 +116,7 @@ export function _SetPhoto<T>(
113
116
  */
114
117
  export function _Exhibit_details<T>(
115
118
  data: T,
116
- options: {
119
+ options?: {
117
120
  dictionaryOptions?: { [key in keyof T]: { [key: string | number]: any } };
118
121
  dictionaryLabel?: (keyof T)[];
119
122
  dictionaryLabelJoin?: (keyof T)[];
@@ -138,7 +141,7 @@ export function _Exhibit_details<T>(
138
141
  filterLabel?: (keyof T)[];
139
142
 
140
143
  defaultValue?: string;
141
- } = {}
144
+ }
142
145
  ): T;
143
146
 
144
147
  // 定义加载状态更新函数类型
@@ -146,7 +149,7 @@ type LoadingStateUpdater = (newState: boolean) => void;
146
149
  // 定义加载控制器的类型
147
150
  interface LoadingController {
148
151
  invokers: Set<any>; // 假设invoker可以是任何类型
149
- timer: NodeJS.Timeout | null;
152
+ timer: number | null;
150
153
  startTime: number;
151
154
  loadingState: LoadingStateUpdater;
152
155
  delayTime: number;
@@ -156,11 +159,9 @@ interface LoadingController {
156
159
  export class _LoadingController {
157
160
  #controllersCollection: Map<string, LoadingController>;
158
161
 
159
- constructor() {}
160
-
161
162
  // addController方法的类型定义
162
163
  addController(
163
- key?: string,
164
+ key: string,
164
165
  config: {
165
166
  loadingState: LoadingStateUpdater;
166
167
  delayTime?: number;
@@ -183,3 +184,77 @@ export class _LoadingController {
183
184
  // stopLoading方法的类型定义
184
185
  stopLoading(invoker: any, key?: string): void;
185
186
  }
187
+
188
+ type UiLibrary = "naiveUI" | "ElementPlus" | "Element";
189
+ /**
190
+ * 点击非指定dom(包含子级dom)时执行 callback
191
+ * @param querySelector 允许点击的 dom 顶层祖先元素选择器
192
+ * @param callback 满足条件时执行的回调
193
+ *
194
+ * @param options 其他配置
195
+ * @param options.uiLibrary 项目使用的 ui库 , 用于排除 ui库 创建的元素 , 避免点击 ui库 创建的元素时意外的执行 callback
196
+ * @param options.isClickAllowed 是否允许该点击 ( 如果不确定可以返回 undefined )
197
+ */
198
+ export function _CloseOnOutsideClick(
199
+ querySelector: string[],
200
+ callback: Function,
201
+ options?: {
202
+ uiLibrary?: UiLibrary[];
203
+ isClickAllowed?: (event: MouseEvent) => boolean | undefined;
204
+ }
205
+ ): void;
206
+
207
+ /** 拖拽配置 */
208
+ type DragOption = {
209
+ /** 拖拽范围限制 */
210
+ limit?: {
211
+ max: {
212
+ top: number;
213
+ left: number;
214
+ };
215
+ min: {
216
+ top: number;
217
+ left: number;
218
+ };
219
+ };
220
+ /** 指定的拖拽元素 */
221
+ dragDom?: HTMLElement;
222
+ };
223
+ /** 拖拽 */
224
+ export class Drag {
225
+ /**
226
+ * 初始化拖拽
227
+ * @param dom 被拖拽的元素
228
+ * @param option 拖拽配置
229
+ */
230
+ init(dom: HTMLElement, option?: DragOption): void;
231
+ /** 结束拖拽 */
232
+ finish(): void;
233
+ }
234
+
235
+ /** 更新后的位置信息 */
236
+ type UpdateValue = {
237
+ top: number;
238
+ left: number;
239
+ percentage?: {
240
+ top: number;
241
+ left: number;
242
+ };
243
+ };
244
+ /** 局部拖拽配置 */
245
+ type LocalDragOptions = {
246
+ limit?: DragOption["limit"];
247
+ update_move?: (value: UpdateValue) => void | undefined;
248
+ update_up?: (value: UpdateValue) => void | undefined;
249
+ };
250
+ /** 局部拖拽 计算位置距离/百分比 */
251
+ export class LocalDrag {
252
+ /**
253
+ * 初始化拖拽
254
+ * @param parentDom 被拖拽元素的祖先元素
255
+ * @param option 局部拖拽配置
256
+ */
257
+ init(parentDom: HTMLElement, options?: LocalDragOptions): void;
258
+ /** 结束拖拽 */
259
+ finish(): void;
260
+ }
package/lib/User.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _IsObject, _IsWithinErrorMargin, _NotNull } from './Utility';
1
+ import { _IsObject, _IsWithinErrorMargin, _NotNull } from "./Utility";
2
2
 
3
3
  /**
4
4
  * 添加滚动触底事件
@@ -6,8 +6,14 @@ import { _IsObject, _IsWithinErrorMargin, _NotNull } from './Utility';
6
6
  * @param {触底事件} callback
7
7
  */
8
8
  export function _AddScrollBottomListener(element, callback) {
9
- element.addEventListener('scroll', function () {
10
- if (_IsWithinErrorMargin(element.scrollTop + element.clientHeight, element.scrollHeight, 2)) {
9
+ element.addEventListener("scroll", function () {
10
+ if (
11
+ _IsWithinErrorMargin(
12
+ element.scrollTop + element.clientHeight,
13
+ element.scrollHeight,
14
+ 2
15
+ )
16
+ ) {
11
17
  callback();
12
18
  }
13
19
  });
@@ -53,25 +59,25 @@ export function _FormatNumberWithUnit(number, config = {}) {
53
59
  const { join, suffix, integer } = Object.assign(
54
60
  {
55
61
  join: true,
56
- suffix: '',
57
- integer: false
62
+ suffix: "",
63
+ integer: false,
58
64
  },
59
65
  config
60
66
  );
61
67
 
62
68
  function _join(value, suffix, plus = true) {
63
- value = (plus ? '' : '-') + value;
69
+ value = (plus ? "" : "-") + value;
64
70
  if (join) return value + suffix;
65
71
  else return [value, suffix];
66
72
  }
67
73
 
68
- if (typeof number == 'string') {
74
+ if (typeof number == "string") {
69
75
  if (!/^\d+$/.test(number.trim())) {
70
- console.error('错误输入:', number);
76
+ console.error("错误输入:", number);
71
77
  return _join(0, suffix);
72
78
  }
73
- } else if (typeof number != 'number') {
74
- console.error('错误输入:', number);
79
+ } else if (typeof number != "number") {
80
+ console.error("错误输入:", number);
75
81
  return _join(0, suffix);
76
82
  }
77
83
 
@@ -83,7 +89,21 @@ export function _FormatNumberWithUnit(number, config = {}) {
83
89
  const plus = number >= 0;
84
90
  number = Math.abs(number);
85
91
 
86
- const units = ['', '万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载', '极'];
92
+ const units = [
93
+ "",
94
+ "万",
95
+ "亿",
96
+ "兆",
97
+ "京",
98
+ "垓",
99
+ "秭",
100
+ "穰",
101
+ "沟",
102
+ "涧",
103
+ "正",
104
+ "载",
105
+ "极",
106
+ ];
87
107
  const digits = Math.floor(Math.log10(number) / 4); // 计算位数
88
108
 
89
109
  // 不超过万位的数字直接返回
@@ -109,16 +129,17 @@ export function _FormatNumberWithUnit(number, config = {}) {
109
129
  */
110
130
  export function _SetQuantifierAttribute(data, options = []) {
111
131
  if (!_IsObject(data)) {
112
- console.error('异常输入:', data);
132
+ console.error("异常输入:", data);
113
133
  return data;
114
134
  }
115
135
 
116
136
  options.forEach((item) => {
117
- if (typeof item === 'string') {
137
+ if (typeof item === "string") {
118
138
  data[item] = _FormatNumberWithUnit(data[item]);
119
139
  } else if (Array.isArray(item)) {
120
140
  const [label, config] = data[item];
121
- if (_NotNull(label) && _IsObject(config)) data[label] = _FormatNumberWithUnit(label, config);
141
+ if (_NotNull(label) && _IsObject(config))
142
+ data[label] = _FormatNumberWithUnit(label, config);
122
143
  }
123
144
  });
124
145
  return data;
@@ -132,11 +153,11 @@ export function _SetQuantifierAttribute(data, options = []) {
132
153
  */
133
154
  export function _SetDefaultValue(data, options = {}) {
134
155
  if (!_IsObject(data)) {
135
- console.error('异常输入:', data);
156
+ console.error("异常输入:", data);
136
157
  return data;
137
158
  }
138
159
 
139
- const { defaultValue = '--', fieldsNotRequiringAction } = options;
160
+ const { defaultValue = "--", fieldsNotRequiringAction } = options;
140
161
 
141
162
  for (const key in data) {
142
163
  if (Object.prototype.hasOwnProperty.call(data, key)) {
@@ -162,7 +183,7 @@ export function _SetDefaultValue(data, options = {}) {
162
183
  */
163
184
  export function _SetDictionary(data, options = {}) {
164
185
  if (!_IsObject(data)) {
165
- console.error('异常输入:', data);
186
+ console.error("异常输入:", data);
166
187
  return data;
167
188
  }
168
189
 
@@ -170,7 +191,7 @@ export function _SetDictionary(data, options = {}) {
170
191
  dictionaryLabel = [],
171
192
  dictionaryLabelJoin = [],
172
193
  dictionaryOptions,
173
- defaultValue = '--'
194
+ defaultValue = "--",
174
195
  } = options;
175
196
 
176
197
  if (dictionaryOptions) {
@@ -188,11 +209,11 @@ export function _SetDictionary(data, options = {}) {
188
209
  }
189
210
  });
190
211
  dictionaryLabelJoin.forEach((label) => {
191
- if (_NotNull(data[label]) && data[label] != '') {
212
+ if (_NotNull(data[label]) && data[label] != "") {
192
213
  const options = dictionaryOptions[label];
193
214
  if (options) {
194
- const oldvalue = data[label].split(',');
195
- data[label] = '';
215
+ const oldvalue = data[label].split(",");
216
+ data[label] = "";
196
217
  oldvalue.forEach((_label) => {
197
218
  data[label] += options[_label];
198
219
  });
@@ -216,7 +237,7 @@ export function _SetDictionary(data, options = {}) {
216
237
  */
217
238
  export function _SetPhoto(data, options = {}) {
218
239
  if (!_IsObject(data)) {
219
- console.error('异常输入:', data);
240
+ console.error("异常输入:", data);
220
241
  return data;
221
242
  }
222
243
 
@@ -226,8 +247,8 @@ export function _SetPhoto(data, options = {}) {
226
247
  label.forEach((label) => {
227
248
  const defaultValue = (defaultUrl && defaultUrl[label]) || [];
228
249
  const value = data[label];
229
- if (typeof value === 'string') {
230
- data[label] = value.split(',').filter(Boolean);
250
+ if (typeof value === "string") {
251
+ data[label] = value.split(",").filter(Boolean);
231
252
  } else {
232
253
  data[label] = defaultValue;
233
254
  }
@@ -245,7 +266,7 @@ export function _SetPhoto(data, options = {}) {
245
266
  */
246
267
  export function _Exhibit_details(data, options = {}) {
247
268
  if (!_IsObject(data)) {
248
- console.error('异常输入:', data);
269
+ console.error("异常输入:", data);
249
270
  return {};
250
271
  }
251
272
 
@@ -263,19 +284,19 @@ export function _Exhibit_details(data, options = {}) {
263
284
 
264
285
  filterLabel = [],
265
286
 
266
- defaultValue = '--'
287
+ defaultValue = "--",
267
288
  } = options;
268
289
 
269
290
  _SetDictionary(data, {
270
291
  dictionaryLabel,
271
292
  dictionaryLabelJoin,
272
293
  dictionaryOptions,
273
- defaultValue
294
+ defaultValue,
274
295
  });
275
296
 
276
297
  _SetPhoto(data, {
277
298
  label: photoLabel,
278
- defaultUrl: photoDefaultUrl
299
+ defaultUrl: photoDefaultUrl,
279
300
  });
280
301
 
281
302
  _SetQuantifierAttribute(data, quantifierLabel);
@@ -288,12 +309,12 @@ export function _Exhibit_details(data, options = {}) {
288
309
  .concat(
289
310
  quantifierLabel
290
311
  .map((item) => {
291
- if (typeof item == 'string') return item;
312
+ if (typeof item == "string") return item;
292
313
  if (Array.isArray(item)) return item[0];
293
314
  })
294
315
  .filter(Boolean)
295
316
  )
296
- .concat(filterLabel)
317
+ .concat(filterLabel),
297
318
  });
298
319
 
299
320
  return data;
@@ -304,14 +325,14 @@ export class _LoadingController {
304
325
  #controllersCollection = new Map();
305
326
  constructor() {}
306
327
 
307
- addController(key = 'default', config) {
328
+ addController(key = "default", config) {
308
329
  if (this.#controllersCollection.has(key))
309
- throw new Error('key为: ' + key + ' 的loading控制器已存在, 请重命名。');
330
+ throw new Error("key为: " + key + " 的loading控制器已存在, 请重命名。");
310
331
 
311
332
  const {
312
333
  loadingState /** 更新/获取 loading 状态的方法 */,
313
334
  delayTime = 200 /** 延迟时间 */,
314
- minDisplayTime = 400 /** 最少显示时间 */
335
+ minDisplayTime = 400 /** 最少显示时间 */,
315
336
  } = config;
316
337
  this.#controllersCollection.set(key, {
317
338
  invokers: new Set(),
@@ -319,7 +340,7 @@ export class _LoadingController {
319
340
  startTime: 0,
320
341
  loadingState,
321
342
  delayTime,
322
- minDisplayTime
343
+ minDisplayTime,
323
344
  });
324
345
  }
325
346
 
@@ -327,9 +348,10 @@ export class _LoadingController {
327
348
  this.#controllersCollection.delete(key);
328
349
  }
329
350
 
330
- getController(key = 'default') {
351
+ getController(key = "default") {
331
352
  const controller = this.#controllersCollection.get(key);
332
- if (!controller) throw new Error('还未添加key为: ' + key + ' 的loading控制器');
353
+ if (!controller)
354
+ throw new Error("还未添加key为: " + key + " 的loading控制器");
333
355
  return controller;
334
356
  }
335
357
 
@@ -378,10 +400,257 @@ export class _LoadingController {
378
400
  if (displayTime >= minDisplayTime) {
379
401
  this.resetController(key);
380
402
  } else {
381
- setTimeout(() => this.stopLoading(invoker, key), displayTime - minDisplayTime);
403
+ setTimeout(
404
+ () => this.stopLoading(invoker, key),
405
+ displayTime - minDisplayTime
406
+ );
382
407
  }
383
408
  } else {
384
409
  invokers.delete(invoker);
385
410
  }
386
411
  }
387
412
  }
413
+
414
+ /**
415
+ * 点击非指定dom(包含子级dom)时执行 callback
416
+ * @param querySelector 允许点击的 dom 顶层祖先元素选择器
417
+ * @param callback 满足条件时执行的回调
418
+ *
419
+ * @param options 其他配置
420
+ * @param options.uiLibrary 项目使用的 ui库 , 用于排除 ui库 创建的元素 , 避免点击 ui库 创建的元素时意外的执行 callback
421
+ * @param options.isClickAllowed 是否允许该点击 ( 如果不确定可以返回 undefined )
422
+ */
423
+ export function _CloseOnOutsideClick(
424
+ querySelector,
425
+ callback,
426
+ options = {
427
+ uiLibrary: ["naiveUI", "ElementPlus", "Element"],
428
+ }
429
+ ) {
430
+ function end() {
431
+ callback();
432
+ document.removeEventListener("mousedown", mousedown);
433
+ }
434
+ function mousedown(event) {
435
+ const { isClickAllowed, uiLibrary } = options;
436
+
437
+ if (isClickAllowed) {
438
+ const bool = isClickAllowed(event);
439
+ if (bool) return;
440
+ if (bool === false) return end();
441
+ }
442
+
443
+ const target = event.target;
444
+
445
+ /** 元素这时可能已经被删除了 */
446
+ if (!target?.closest("body")) return;
447
+
448
+ const UI = (function (obj) {
449
+ const arr = [];
450
+ for (const key in obj) {
451
+ if (Object.hasOwnProperty.call(obj, key)) {
452
+ if (uiLibrary.includes(key)) arr.concat(obj[key]);
453
+ }
454
+ }
455
+ return arr;
456
+ })({
457
+ naiveUI: [
458
+ ".v-binder-follower-container",
459
+ ".n-image-preview-container",
460
+ ".n-modal-container",
461
+ ],
462
+ ElementPlus: ["el-popper"],
463
+ Element: ["el-popper"],
464
+ });
465
+ const isClickable = querySelector
466
+ .concat(UI)
467
+ .some((className) => Boolean(target?.closest(className)));
468
+
469
+ if (!isClickable) end();
470
+ }
471
+ requestAnimationFrame(() =>
472
+ document.addEventListener("mousedown", mousedown)
473
+ );
474
+ }
475
+
476
+ /** 拖拽dom */
477
+ export class Drag {
478
+ #dom = null;
479
+ #isAllowed = false;
480
+ #eventFunction = {};
481
+ #pageX = 0;
482
+ #pageY = 0;
483
+ #top = 0;
484
+ #left = 0;
485
+ #limit;
486
+ #dragDom;
487
+
488
+ init(dom, option) {
489
+ this.#dom = dom;
490
+ this.#limit = option?.limit;
491
+ this.#dragDom = option?.dragDom;
492
+ this.#eventFunction = {
493
+ mousedown: this.mousedown.bind(this),
494
+ mousemove: this.mousemove.bind(this),
495
+ mouseup: this.mouseup.bind(this),
496
+ };
497
+
498
+ this.bindOrUnbindEvent("bind");
499
+ }
500
+ finish() {
501
+ this.bindOrUnbindEvent("unbind");
502
+ }
503
+ bindOrUnbindEvent(type) {
504
+ const EventType =
505
+ type === "bind" ? "addEventListener" : "removeEventListener";
506
+ if (!this.#dom) return console.error("No DOM");
507
+
508
+ this.#dom[EventType]("mousedown", this.#eventFunction.mousedown);
509
+ document[EventType]("mousemove", this.#eventFunction.mousemove);
510
+ document[EventType]("mouseup", this.#eventFunction.mouseup);
511
+ }
512
+ alterLocation() {
513
+ if (!this.#dom) return console.error("No DOM");
514
+ if (this.#limit) {
515
+ this.#top = Math.min(this.#top, this.#limit.max.top);
516
+ this.#top = Math.max(this.#top, this.#limit.min.top);
517
+ this.#left = Math.min(this.#left, this.#limit.max.left);
518
+ this.#left = Math.max(this.#left, this.#limit.min.left);
519
+ }
520
+ this.#dom.style.setProperty("--top", this.#top + "px");
521
+ this.#dom.style.setProperty("--left", this.#left + "px");
522
+ }
523
+ mousedown(event) {
524
+ if (!this.#dom) return console.error("No DOM");
525
+ if (this.#dragDom && event.target != this.#dragDom) return;
526
+ document.body.classList.add("no-select");
527
+
528
+ this.#isAllowed = true;
529
+ const clientRect = this.#dom.getBoundingClientRect();
530
+
531
+ const { pageX, pageY } = event;
532
+ this.#pageX = pageX;
533
+ this.#pageY = pageY;
534
+ this.#top = clientRect.y;
535
+ this.#left = clientRect.x;
536
+ }
537
+ mousemove(event) {
538
+ const { pageX, pageY } = event;
539
+ if (this.#isAllowed) {
540
+ this.#top += pageY - this.#pageY;
541
+ this.#left += pageX - this.#pageX;
542
+ this.#pageX = pageX;
543
+ this.#pageY = pageY;
544
+
545
+ this.alterLocation();
546
+ }
547
+ }
548
+ mouseup() {
549
+ if (this.#isAllowed) {
550
+ this.#isAllowed = false;
551
+ document.body.classList.remove("no-select");
552
+ }
553
+ }
554
+ }
555
+
556
+ /** 局部拖拽 计算位置距离/百分比 */
557
+ export class LocalDrag {
558
+ #parentDom = null;
559
+ #isAllowed = false;
560
+ #eventFunction = {};
561
+ #clientRectX = 0;
562
+ #clientRectY = 0;
563
+ #top = 0;
564
+ #left = 0;
565
+ #limit;
566
+ #update_move;
567
+ #update_up;
568
+
569
+ init(parentDom, options = {}) {
570
+ this.#parentDom = parentDom;
571
+ this.#limit = options.limit;
572
+ this.#update_move = options.update_move;
573
+ this.#update_up = options.update_up;
574
+ this.#eventFunction = {
575
+ mousedown: this.mousedown.bind(this),
576
+ mousemove: this.mousemove.bind(this),
577
+ mouseup: this.mouseup.bind(this),
578
+ };
579
+
580
+ this.bindOrUnbindEvent("bind");
581
+ }
582
+ finish() {
583
+ this.bindOrUnbindEvent("unbind");
584
+ }
585
+ bindOrUnbindEvent(type) {
586
+ const EventType =
587
+ type === "bind" ? "addEventListener" : "removeEventListener";
588
+ if (!this.#parentDom) return window.customize_error("No DOM");
589
+
590
+ this.#parentDom[EventType]("mousedown", this.#eventFunction.mousedown);
591
+ document[EventType]("mousemove", this.#eventFunction.mousemove);
592
+ document[EventType]("mouseup", this.#eventFunction.mouseup);
593
+ }
594
+ updateValue() {
595
+ const value = {
596
+ top: this.#top,
597
+ left: this.#left,
598
+ };
599
+ if (this.#limit) {
600
+ const v = (type) =>
601
+ this.#limit
602
+ ? (value[type] - this.#limit.min[type]) /
603
+ (this.#limit.max[type] - this.#limit.min[type])
604
+ : 0;
605
+
606
+ value.percentage = {
607
+ top: v("top") || 0,
608
+ left: v("left") || 0,
609
+ };
610
+ }
611
+ return value;
612
+ }
613
+ alterLocation() {
614
+ if (!this.#parentDom) return window.customize_error("No DOM");
615
+ if (this.#limit) {
616
+ this.#top = Math.min(this.#top, this.#limit.max.top);
617
+ this.#top = Math.max(this.#top, this.#limit.min.top);
618
+ this.#left = Math.min(this.#left, this.#limit.max.left);
619
+ this.#left = Math.max(this.#left, this.#limit.min.left);
620
+ }
621
+ if (this.#update_move) this.#update_move(this.updateValue());
622
+
623
+ this.#parentDom.style.setProperty("--top", this.#top + "px");
624
+ this.#parentDom.style.setProperty("--left", this.#left + "px");
625
+ }
626
+ mousedown(event) {
627
+ if (!this.#parentDom) return window.customize_error("No DOM");
628
+ document.body.classList.add("no-select");
629
+
630
+ this.#isAllowed = true;
631
+ const clientRect = this.#parentDom.getBoundingClientRect();
632
+ this.#clientRectY = clientRect.y;
633
+ this.#clientRectX = clientRect.x;
634
+
635
+ const { pageX, pageY } = event;
636
+ this.#top = pageY - this.#clientRectY;
637
+ this.#left = pageX - this.#clientRectX;
638
+
639
+ this.alterLocation();
640
+ }
641
+ mousemove(event) {
642
+ const { pageX, pageY } = event;
643
+ if (this.#isAllowed) {
644
+ this.#top = pageY - this.#clientRectY;
645
+ this.#left = pageX - this.#clientRectX;
646
+ this.alterLocation();
647
+ }
648
+ }
649
+ mouseup() {
650
+ if (this.#isAllowed) {
651
+ this.#isAllowed = false;
652
+ document.body.classList.remove("no-select");
653
+ if (this.#update_up) this.#update_up(this.updateValue());
654
+ }
655
+ }
656
+ }
package/lib/Utility.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { ExtractParameters } from "./Index";
2
+
1
3
  /**
2
4
  * 非null | undefined判断
3
5
  * @param value any
@@ -40,7 +42,7 @@ export function _ConvertToPercentage(
40
42
  export function _WaitForCondition(
41
43
  conditionChecker: () => boolean,
42
44
  timeoutMillis: number
43
- ): Promise;
45
+ ): Promise<"完成" | "超时">;
44
46
 
45
47
  /**
46
48
  * 排除子串
@@ -104,27 +106,30 @@ export function _ReadFile(src: string): Promise<string>;
104
106
 
105
107
  /**
106
108
  * 下载文件
107
- * @param {文件路径} href
108
- * @param {导出文件名} fileName
109
+ * @param {string} href 文件路径
110
+ * @param {string} fileName 导出文件名
109
111
  */
110
112
  export function _DownloadFile(href: string, fileName?: string): void;
111
113
 
112
114
  /**
113
115
  * 获取帧率
114
- * @param {Function} callback callback( 帧率 , 每帧时间 )
116
+ * @param {(fps , frameTime)=>void} callback callback( 帧率 , 每帧时间 )
115
117
  * @param {Number} referenceNode 参考节点数量
116
118
  */
117
119
  export function _GetFrameRate(
118
- callback: Function,
119
- referenceNode: number = 10
120
+ callback: (fps: number, frameTime: number) => void,
121
+ referenceNode: number
120
122
  ): void;
121
123
 
122
124
  /**
123
125
  * 进度
124
- * @param {Function} callback callback( 进度百分比 )
126
+ * @param {(schedule)=>void} callback callback( 进度百分比 )
125
127
  * @param {Number} TIME 总时长
126
128
  */
127
- export function _Schedule(callback: Function, TIME: number = 500): void;
129
+ export function _Schedule(
130
+ callback: (schedule: number) => void,
131
+ TIME: number
132
+ ): void;
128
133
 
129
134
  /**
130
135
  * 格式化数字,给数字加上千位分隔符。
@@ -150,3 +155,50 @@ export function _ConvertToCamelCase(
150
155
  str: string,
151
156
  isRemoveDelimiter?: boolean
152
157
  ): string;
158
+
159
+ /**
160
+ * 创建文件并下载
161
+ * @param {BlobPart[]} content 文件内容
162
+ * @param {string} fileName 文件名称
163
+ * @param {BlobPropertyBag} options Blob 配置
164
+ */
165
+ export function _CreateAndDownloadFile(
166
+ content: BlobPart[],
167
+ fileName: string,
168
+ options?: BlobPropertyBag
169
+ ): void;
170
+
171
+ /**
172
+ * 获取url参数
173
+ * @param {string} url
174
+ * @returns {Object}
175
+ */
176
+ export function _GetQueryParams(url: string): void;
177
+
178
+ /**
179
+ * 生成uuid
180
+ * @returns {string}
181
+ */
182
+ export function _GenerateUUID(): string;
183
+
184
+ /**
185
+ * 防抖
186
+ * @param {Function} fn
187
+ * @param {number} delay
188
+ * @returns {Function}
189
+ */
190
+ export function _Debounce<T extends Function>(
191
+ fn: T,
192
+ delay: number
193
+ ): (...args: ExtractParameters<T>) => void;
194
+
195
+ /**
196
+ * 节流
197
+ * @param {Function} fn
198
+ * @param {number} delay
199
+ * @returns {Function}
200
+ */
201
+ export function _Throttle<T extends Function>(
202
+ fn: T,
203
+ delay: number
204
+ ): (...args: ExtractParameters<T>) => void;
package/lib/Utility.js CHANGED
@@ -228,32 +228,32 @@ export function _DownloadFile(href, fileName) {
228
228
 
229
229
  /**
230
230
  * 获取帧率
231
- * @param {Function} callback callback( 帧率 , 每帧时间 )
231
+ * @param {(fps , frameTime)=>void} callback callback( 帧率 , 每帧时间 )
232
232
  * @param {Number} referenceNode 参考节点数量
233
233
  */
234
234
  export function _GetFrameRate(callback, referenceNode = 10) {
235
235
  let t,
236
- arr = [];
237
- function loop(time) {
238
- if (t) {
239
- arr.push(time - t);
240
- let l = arr.length;
241
- if (l >= referenceNode) {
242
- let num = arr.reduce((a, b) => a + b, 0);
243
- num /= l;
244
- callback(1000 / num, num);
245
- return;
246
- }
236
+ l = referenceNode;
237
+ function loop() {
238
+ if (l > 0) {
239
+ l--;
240
+ requestAnimationFrame(loop);
241
+ } else {
242
+ const time = new Date() - t;
243
+ const frameTime = time / referenceNode;
244
+ const fps = 1000 / frameTime;
245
+ callback(Number(fps.toFixed(2)), Number(frameTime.toFixed(2)));
247
246
  }
248
- t = time;
249
- requestAnimationFrame(loop);
250
247
  }
251
- requestAnimationFrame(loop);
248
+ requestAnimationFrame(() => {
249
+ t = new Date() - 0;
250
+ loop();
251
+ });
252
252
  }
253
253
 
254
254
  /**
255
255
  * 进度
256
- * @param {Function} callback callback( 进度百分比 )
256
+ * @param {(schedule)=>void} callback callback( 进度百分比 )
257
257
  * @param {Number} TIME 总时长
258
258
  */
259
259
  export function _Schedule(callback, TIME = 500) {
@@ -302,3 +302,82 @@ export function _ConvertToCamelCase(str, isRemoveDelimiter) {
302
302
  if (isRemoveDelimiter) return str.replace(/[^a-zA-Z]+/g, "");
303
303
  return str;
304
304
  }
305
+
306
+ /**
307
+ * 创建文件并下载
308
+ * @param {BlobPart[]} content 文件内容
309
+ * @param {string} fileName 文件名称
310
+ * @param {BlobPropertyBag} options Blob 配置
311
+ */
312
+ export function _CreateAndDownloadFile(content, fileName, options) {
313
+ if (!options) {
314
+ let type = fileName.replace(/^[^.]+./, "");
315
+ type = type == fileName ? "text/plain" : "application/" + type;
316
+ options = { type };
317
+ }
318
+ const bolb = new Blob(content, options);
319
+ // 创建一个 URL,该 URL 可以用于在浏览器中引用 Blob 对象(例如,在 <a> 标签的 href 属性中)
320
+ const url = URL.createObjectURL(bolb);
321
+ // 你可以创建一个链接来下载这个 Blob 对象
322
+ const downloadLink = document.createElement("a");
323
+ downloadLink.href = url;
324
+ downloadLink.download = fileName; // 设置下载文件的名称
325
+ document.body.appendChild(downloadLink); // 添加到文档中
326
+ downloadLink.click(); // 模拟点击以开始下载
327
+ document.body.removeChild(downloadLink); // 然后从文档中移除
328
+ // 最后,别忘了撤销 Blob 对象的 URL,以释放资源
329
+ URL.revokeObjectURL(url);
330
+ }
331
+
332
+ /**
333
+ * 获取url参数
334
+ * @param {string} url
335
+ * @returns {Object}
336
+ */
337
+ export function _GetQueryParams(url) {
338
+ return Object.fromEntries(new URL(url).searchParams);
339
+ }
340
+
341
+ /**
342
+ * 生成uuid
343
+ * @returns {string}
344
+ */
345
+ export function _GenerateUUID() {
346
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
347
+ const r = (Math.random() * 16) | 0; // 随机生成一个0到15的数
348
+ const v = c === "x" ? r : (r & 0x3) | 0x8; // 对于'y'位, v = (r & 0x3 | 0x8) 确保变体正确
349
+ return v.toString(16); // 将数字转换为16进制
350
+ });
351
+ }
352
+
353
+ /**
354
+ * 防抖
355
+ * @param {Function} fn
356
+ * @param {number} delay
357
+ * @returns {Function}
358
+ */
359
+ export function _Debounce(fn, delay) {
360
+ let timeoutId;
361
+ return function (...args) {
362
+ clearTimeout(timeoutId);
363
+ timeoutId = setTimeout(() => fn.apply(this, args), delay);
364
+ };
365
+ }
366
+
367
+ /**
368
+ * 节流
369
+ * @param {Function} fn
370
+ * @param {number} delay
371
+ * @returns {Function}
372
+ */
373
+ export function _Throttle(fn, delay) {
374
+ let timer;
375
+ return function (...args) {
376
+ if (!timer) {
377
+ timer = setTimeout(() => {
378
+ fn.apply(this, args);
379
+ timer = null;
380
+ }, delay);
381
+ }
382
+ };
383
+ }
package/lib/index.css ADDED
@@ -0,0 +1,8 @@
1
+ .no-select {
2
+ -webkit-user-select: none;
3
+ /* Safari */
4
+ -ms-user-select: none;
5
+ /* IE10+/Edge */
6
+ user-select: none;
7
+ /* Standard syntax */
8
+ }
package/lib/index.less ADDED
@@ -0,0 +1,5 @@
1
+ .no-select {
2
+ -webkit-user-select: none; /* Safari */
3
+ -ms-user-select: none; /* IE10+/Edge */
4
+ user-select: none; /* Standard syntax */
5
+ }
package/lib/test.ts ADDED
@@ -0,0 +1 @@
1
+ /** 用于测试 js / ts 功能实现及类型是否正确 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nhanh-pure-function",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "纯函数工具",
5
5
  "main": "lib/Index.js",
6
6
  "scripts": {