rc-utils 是绘制 react-component 组件所使用的工具库。

Portal

使用 react16 中的 ReactDOM.createPortal 将浮层组件渲染到指定节点中。

prop 属性 意义 默认值
autoMount componentDidMount, componentDidUpdate 生命周期是否自动渲染浮层组件 true
children 浮层组件,ReactElement undefined
didUpdate componentDidUpdate 生命周期重绘时的回调函数 undefined
实例属性 意义
container 浮层组件的父节点

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| import React from ‘react’;
import ReactDOM from ‘react-dom’;
import PropTypes from ‘prop-types’;

export default class Portal extends React.Component {
static propTypes = {
getContainer: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,
didUpdate: PropTypes.func,
}

componentDidMount() {
this.createContainer();
}

componentDidUpdate(prevProps) {
const { didUpdate } = this.props;
if (didUpdate) {
didUpdate(prevProps);
}
}

componentWillUnmount() {
this.removeContainer();
}

createContainer() {
this._container = this.props.getContainer();
this.forceUpdate();
}

removeContainer() {
if (this._container) {
this._container.parentNode.removeChild(this._container);
}
}

render() {
if (this._container) {
return ReactDOM.createPortal(this.props.children, this._container);
}
return null;
}
}
| | :—- | :—- |

ContainerRender

使用 ReactDOM.unstable_renderSubtreeIntoContainer 将组件渲染到指定节点中,通常用于渲染浮层。下文以浮层组件指代渲染到指定节点中的组件。

prop 属性 意义 默认值
autoMount componentDidMount, componentDidUpdate 生命周期是否自动渲染浮层组件 true
autoDestroy componentWillUnmount 生命周期是否自动卸载浮层组件 true
visible 是否显示浮层组件 undefined
forceRender ContainerRender 或子组件重绘过程中,是否重绘浮层组件 false
parent 渲染过程中的虚拟父节点,需要使用者主动将浮层组价记录到 parent._component;parent._component 为真值时将重绘浮层组件 undefined
getComponent 函数形式,用于渲染浮层组件 undefined
getContainer 函数形式,用于指定浮层组件的父节点,需要临时创建 undefined
children 渲染过程中的虚拟子节点,函数形式 ({ renderComponent, removeContainer }) => {} undefined
实例方法及属性 意义
renderComponent(props, ready) 渲染浮层组件,props 将注入到浮层组件中,ready 为渲染完成后的回调
removeContainer 移除浮层组件并其父节点
container 浮层组件的父节点

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
| import React from ‘react’;
import ReactDOM from ‘react-dom’;
import PropTypes from ‘prop-types’;

export default class ContainerRender extends React.Component {
static propTypes = {
autoMount: PropTypes.bool,
autoDestroy: PropTypes.bool,
visible: PropTypes.bool,
forceRender: PropTypes.bool,
parent: PropTypes.any,
getComponent: PropTypes.func.isRequired,
getContainer: PropTypes.func.isRequired,
children: PropTypes.func.isRequired,
}

static defaultProps = {
autoMount: true,
autoDestroy: true,
forceRender: false,
}

componentDidMount() {
if (this.props.autoMount) {
this.renderComponent();
}
}

componentDidUpdate() {
if (this.props.autoMount) {
this.renderComponent();
}
}

componentWillUnmount() {
if (this.props.autoDestroy) {
this.removeContainer();
}
}

removeContainer = () => {
if (this.container) {
ReactDOM.unmountComponentAtNode(this.container);
this.container.parentNode.removeChild(this.container);
this.container = null;
}
}

renderComponent = (props, ready) => {
const { visible, getComponent, forceRender, getContainer, parent } = this.props;
if (visible || parent._component || forceRender) {
if (!this.container) {
this.container = getContainer();
}
ReactDOM.unstable_renderSubtreeIntoContainer(
parent,
getComponent(props),
this.container,
function callback() {
if (ready) {
ready.call(this);
}
}
);
}
}

render() {
return this.props.children({
renderComponent: this.renderComponent,
removeContainer: this.removeContainer,
});
}
}
| | :—- | :—- |

Chilren

  • Chilren/mapSelf(children): 将成组的 ReactElement 转换为 React16 支持渲染的数组形式。关于使用 jsx 语法书写的成组的 ReactElement,也可以使用 React.Fragment 组件进行渲染。
  • Children/toArray(children): 将成组的 ReactElement 转换为数组,效果同 mapSelf 函数。

    Dom

  • Dom/addEventListenerWrap(target, eventType, cb, option): 借助 add-dom-event-listener 为节点绑定事件处理函数,返回解绑函数。cb 回调将尝试使用 ReactDOM.unstable_batchedUpdates 调用,以批处理形式更新组件状态。

  • Dom/canUseDom: 校验是否支持 dom 节点操作。
  • Dom/class: 输出 hasClass(node, className), addClass(node, className), removeClass(node, className) 函数以操作节点的样式类。
  • Dom/contains(root, n): 校验 root 节点下是否包含 n 节点。
  • Dom/css: 支撑样式操作,参见下文。
  • Dom/focus: 焦点操作,参见下文。
  • Dom/support: 输出 animation, transition 变量,以判断平台支持的 animationend, transitionend 事件名;如不支持,变量的值为 false。
  • getScrollBarSize(fresh): 计算滚动条宽度,fresh 为真值时重新计算,否则返回缓存。

    css

    样式操作函数集。

  • get(node, name): 获取节点的指定样式。

  • set(node, name, value): 设置节点的指定样式,次参支持对象形式。
  • getOuterWidth(el): 获取节点的宽度(offsetWidth 属性);document.body 返回 document.documentElement.clientWidth 属性。
  • getOuterHeight(el): 获取节点的高度(offsetHeight 属性);document.body 返回 window.innerHeight 或 document.documentElement.clientHeight 属性。
  • getDocSize: 获取文档的宽高。
  • getClientSize: 获取文档的宽高。
  • getScroll: 获取文档的滚动偏移量。
  • getOffset(node): 获取元素的滚动偏移量。 | 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    | / eslint-disable no-nested-ternary /
    const PIXEL_PATTERN = /margin|padding|width|height|max|min|offset/;

const removePixel = {
left: true,
top: true,
};
const floatMap = {
cssFloat: 1,
styleFloat: 1,
float: 1,
};

function getComputedStyle(node) {
return node.nodeType === 1 ?
node.ownerDocument.defaultView.getComputedStyle(node, null) : {};
}

function getStyleValue(node, type, value) {
type = type.toLowerCase();
if (value === ‘auto’) {
if (type === ‘height’) {
return node.offsetHeight;
}
if (type === ‘width’) {
return node.offsetWidth;
}
}
if (!(type in removePixel)) {
removePixel[type] = PIXEL_PATTERN.test(type);
}
return removePixel[type] ? (parseFloat(value) || 0) : value;
}

export function get(node, name) {
const length = arguments.length;
const style = getComputedStyle(node);

name = floatMap[name] ? ‘cssFloat’ in node.style ? ‘cssFloat’ : ‘styleFloat’ : name;

return (length === 1) ? style : getStyleValue(node, name, style[name] || node.style[name]);
}

export function set(node, name, value) {
const length = arguments.length;
name = floatMap[name] ? ‘cssFloat’ in node.style ? ‘cssFloat’ : ‘styleFloat’ : name;
if (length === 3) {
if (typeof value === ‘number’ && PIXEL_PATTERN.test(name)) {
value = ${value}px;
}
node.style[name] = value; // Number
return value;
}
for (const x in name) {
if (name.hasOwnProperty(x)) {
set(node, x, name[x]);
}
}
return getComputedStyle(node);
}

export function getOuterWidth(el) {
if (el === document.body) {
return document.documentElement.clientWidth;
}
return el.offsetWidth;
}

export function getOuterHeight(el) {
if (el === document.body) {
return window.innerHeight || document.documentElement.clientHeight;
}
return el.offsetHeight;
}

export function getDocSize() {
const width = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth);
const height = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);

return {
width,
height,
};
}

export function getClientSize() {
const width = document.documentElement.clientWidth;
const height = window.innerHeight || document.documentElement.clientHeight;
return {
width,
height,
};
}

export function getScroll() {
return {
scrollLeft: Math.max(document.documentElement.scrollLeft, document.body.scrollLeft),
scrollTop: Math.max(document.documentElement.scrollTop, document.body.scrollTop),
};
}

export function getOffset(node) {
const box = node.getBoundingClientRect();
const docElem = document.documentElement;

// < ie8 不支持 win.pageXOffset, 则使用 docElem.scrollLeft
return {
left: box.left + (window.pageXOffset || docElem.scrollLeft) -
(docElem.clientLeft || document.body.clientLeft || 0),
top: box.top + (window.pageYOffset || docElem.scrollTop) -
(docElem.clientTop || document.body.clientTop || 0),
};
}
| | :—- | :—- |

focus

焦点操作集。

  • getFocusNodeList(node): 获取指定节点下可聚焦的节点。
  • saveLastFocusNode: 缓存最后一个聚焦节点。
  • clearLastFocusNode: 清除最后一个聚焦节点。
  • backLastFocusNode: 重新聚焦最后一个聚焦节点。
  • limitTabRange(node, e): 指定节点中点击 tab 按键时,支持从尾部可聚焦节点切换顶部可聚焦节点、或者从顶部可聚焦节点切换尾部可聚焦节点(同时按 shift 键)。 | 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    | function hidden(node) {
    return node.style.display === ‘none’;
    }

function visible(node) {
while (node) {
if (node === document.body) {
break;
}
if (hidden(node)) {
return false;
}
node = node.parentNode;
}
return true;
}

function focusable(node) {
const nodeName = node.nodeName.toLowerCase();
const tabIndex = parseInt(node.getAttribute(‘tabindex’), 10);
const hasTabIndex = !isNaN(tabIndex) && tabIndex > -1;

if (visible(node)) {
if ([‘input’, ‘select’, ‘textarea’, ‘button’].indexOf(nodeName) > -1) {
return !node.disabled;
} else if (nodeName === ‘a’) {
return (node.getAttribute(‘href’) || hasTabIndex);
}
return node.isContentEditable || hasTabIndex;
}
}

export function getFocusNodeList(node) {
const res = [].slice.call(node.querySelectorAll(‘*’), 0).filter((child) => {
return focusable(child);
});
if (focusable(node)) {
res.unshift(node);
}
return res;
}

let lastFocusElement = null;

export function saveLastFocusNode() {
lastFocusElement = document.activeElement;
}

export function clearLastFocusNode() {
lastFocusElement = null;
}

export function backLastFocusNode() {
if (lastFocusElement) {
try {
// 元素可能已经被移动了
lastFocusElement.focus();

  1. /* eslint-disable no-empty */<br /> } catch (e) {<br /> // empty<br /> }<br /> /* eslint-enable no-empty */<br /> }<br />}

export function limitTabRange(node, e) {
if (e.keyCode === 9) {
const tabNodeList = getFocusNodeList(node);
const lastTabNode = tabNodeList[e.shiftKey ? 0 : tabNodeList.length - 1];
const leavingTab = (lastTabNode === document.activeElement || node === document.activeElement);

  1. if (leavingTab) {<br /> const target = tabNodeList[e.shiftKey ? tabNodeList.length - 1 : 0];<br /> target.focus();<br /> e.preventDefault();<br /> }<br /> }<br />}<br /> |

| :—- | :—- |

getScrollBarSize

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| let cached;

export default function getScrollBarSize(fresh) {
if (fresh || cached === undefined) {
const inner = document.createElement(‘div’);
inner.style.width = ‘100%’;
inner.style.height = ‘200px’;

  1. const outer = document.createElement('div');<br /> const outerStyle = outer.style;
  2. outerStyle.position = 'absolute';<br /> outerStyle.top = 0;<br /> outerStyle.left = 0;<br /> outerStyle.pointerEvents = 'none';<br /> outerStyle.visibility = 'hidden';<br /> outerStyle.width = '200px';<br /> outerStyle.height = '150px';<br /> outerStyle.overflow = 'hidden';
  3. outer.appendChild(inner);
  4. document.body.appendChild(outer);
  5. const widthContained = inner.offsetWidth;<br /> outer.style.overflow = 'scroll';<br /> let widthScroll = inner.offsetWidth;
  6. if (widthContained === widthScroll) {<br /> widthScroll = outer.clientWidth;<br /> }
  7. document.body.removeChild(outer);
  8. cached = widthContained - widthScroll;<br /> }<br /> return cached;<br />}<br /> |

| :—- | :—- |

KeyCode

按键映射,及判断按键类型。

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
| /*
@ignore
some key-codes definition and utils from closure-library
@author yiminghe@gmail.com
*/

const KeyCode = {
/
MAC_ENTER
/
MAC_ENTER: 3,
/

BACKSPACE
/
BACKSPACE: 8,
/
TAB
/
TAB: 9,
/

NUMLOCK on FF/Safari Mac
/
NUM_CENTER: 12, // NUMLOCK on FF/Safari Mac
/
ENTER
/
ENTER: 13,
/

SHIFT
/
SHIFT: 16,
/
CTRL
/
CTRL: 17,
/

ALT
/
ALT: 18,
/
PAUSE
/
PAUSE: 19,
/

CAPS_LOCK
/
CAPS_LOCK: 20,
/
ESC
/
ESC: 27,
/

SPACE
/
SPACE: 32,
/
PAGE_UP
/
PAGE_UP: 33, // also NUM_NORTH_EAST
/

PAGE_DOWN
/
PAGE_DOWN: 34, // also NUM_SOUTH_EAST
/
END
/
END: 35, // also NUM_SOUTH_WEST
/

HOME
/
HOME: 36, // also NUM_NORTH_WEST
/
LEFT
/
LEFT: 37, // also NUM_WEST
/

UP
/
UP: 38, // also NUM_NORTH
/
RIGHT
/
RIGHT: 39, // also NUM_EAST
/

DOWN
/
DOWN: 40, // also NUM_SOUTH
/
PRINT_SCREEN
/
PRINT_SCREEN: 44,
/

INSERT
/
INSERT: 45, // also NUM_INSERT
/
DELETE
/
DELETE: 46, // also NUM_DELETE
/

ZERO
/
ZERO: 48,
/
ONE
/
ONE: 49,
/

TWO
/
TWO: 50,
/
THREE
/
THREE: 51,
/

FOUR
/
FOUR: 52,
/
FIVE
/
FIVE: 53,
/

SIX
/
SIX: 54,
/
SEVEN
/
SEVEN: 55,
/

EIGHT
/
EIGHT: 56,
/
NINE
/
NINE: 57,
/

QUESTION_MARK
/
QUESTION_MARK: 63, // needs localization
/
A
/
A: 65,
/

B
/
B: 66,
/
C
/
C: 67,
/

D
/
D: 68,
/
E
/
E: 69,
/

F
/
F: 70,
/
G
/
G: 71,
/

H
/
H: 72,
/
I
/
I: 73,
/

J
/
J: 74,
/
K
/
K: 75,
/

L
/
L: 76,
/
M
/
M: 77,
/

N
/
N: 78,
/
O
/
O: 79,
/

P
/
P: 80,
/
Q
/
Q: 81,
/

R
/
R: 82,
/
S
/
S: 83,
/

T
/
T: 84,
/
U
/
U: 85,
/

V
/
V: 86,
/
W
/
W: 87,
/

X
/
X: 88,
/
Y
/
Y: 89,
/

Z
/
Z: 90,
/
META
/
META: 91, // WIN_KEY_LEFT
/

WIN_KEY_RIGHT
/
WIN_KEY_RIGHT: 92,
/
CONTEXT_MENU
/
CONTEXT_MENU: 93,
/

NUM_ZERO
/
NUM_ZERO: 96,
/
NUM_ONE
/
NUM_ONE: 97,
/

NUM_TWO
/
NUM_TWO: 98,
/
NUM_THREE
/
NUM_THREE: 99,
/

NUM_FOUR
/
NUM_FOUR: 100,
/
NUM_FIVE
/
NUM_FIVE: 101,
/

NUM_SIX
/
NUM_SIX: 102,
/
NUM_SEVEN
/
NUM_SEVEN: 103,
/

NUM_EIGHT
/
NUM_EIGHT: 104,
/
NUM_NINE
/
NUM_NINE: 105,
/

NUM_MULTIPLY
/
NUM_MULTIPLY: 106,
/
NUM_PLUS
/
NUM_PLUS: 107,
/

NUM_MINUS
/
NUM_MINUS: 109,
/
NUM_PERIOD
/
NUM_PERIOD: 110,
/

NUM_DIVISION
/
NUM_DIVISION: 111,
/
F1
/
F1: 112,
/

F2
/
F2: 113,
/
F3
/
F3: 114,
/

F4
/
F4: 115,
/
F5
/
F5: 116,
/

F6
/
F6: 117,
/
F7
/
F7: 118,
/

F8
/
F8: 119,
/
F9
/
F9: 120,
/

F10
/
F10: 121,
/
F11
/
F11: 122,
/

F12
/
F12: 123,
/
NUMLOCK
/
NUMLOCK: 144,
/

SEMICOLON
/
SEMICOLON: 186, // needs localization
/
DASH
/
DASH: 189, // needs localization
/

EQUALS
/
EQUALS: 187, // needs localization
/
COMMA
/
COMMA: 188, // needs localization
/

PERIOD
/
PERIOD: 190, // needs localization
/
SLASH
/
SLASH: 191, // needs localization
/

APOSTROPHE
/
APOSTROPHE: 192, // needs localization
/
SINGLE_QUOTE
/
SINGLE_QUOTE: 222, // needs localization
/

OPEN_SQUARE_BRACKET
/
OPEN_SQUARE_BRACKET: 219, // needs localization
/
BACKSLASH
/
BACKSLASH: 220, // needs localization
/

CLOSE_SQUARE_BRACKET
/
CLOSE_SQUARE_BRACKET: 221, // needs localization
/
WIN_KEY
/
WIN_KEY: 224,
/

MAC_FF_META
/
MAC_FF_META: 224, // Firefox (Gecko) fires this for the meta key instead of 91
/*
WIN_IME
*/
WIN_IME: 229,
};

/
whether text and modified key is entered at the same time.
/
KeyCode.isTextModifyingKeyEvent = function isTextModifyingKeyEvent(e) {
const keyCode = e.keyCode;
if (e.altKey && !e.ctrlKey || e.metaKey ||
// Function keys don’t generate text
keyCode >= KeyCode.F1 && keyCode <= KeyCode.F12) {
return false;
}

// The following keys are quite harmless, even in combination with
// CTRL, ALT or SHIFT.
switch (keyCode) {
case KeyCode.ALT:
case KeyCode.CAPS_LOCK:
case KeyCode.CONTEXT_MENU:
case KeyCode.CTRL:
case KeyCode.DOWN:
case KeyCode.END:
case KeyCode.ESC:
case KeyCode.HOME:
case KeyCode.INSERT:
case KeyCode.LEFT:
case KeyCode.MAC_FF_META:
case KeyCode.META:
case KeyCode.NUMLOCK:
case KeyCode.NUM_CENTER:
case KeyCode.PAGE_DOWN:
case KeyCode.PAGE_UP:
case KeyCode.PAUSE:
case KeyCode.PRINT_SCREEN:
case KeyCode.RIGHT:
case KeyCode.SHIFT:
case KeyCode.UP:
case KeyCode.WIN_KEY:
case KeyCode.WIN_KEY_RIGHT:
return false;
default:
return true;
}
};

/
whether character is entered.
/
KeyCode.isCharacterKey = function isCharacterKey(keyCode) {
if (keyCode >= KeyCode.ZERO &&
keyCode <= KeyCode.NINE) {
return true;
}

if (keyCode >= KeyCode.NUM_ZERO &&
keyCode <= KeyCode.NUM_MULTIPLY) {
return true;
}

if (keyCode >= KeyCode.A &&
keyCode <= KeyCode.Z) {
return true;
}

// Safari sends zero key code for non-latin characters.
if (window.navigation.userAgent.indexOf(‘WebKit’) !== -1 && keyCode === 0) {
return true;
}

switch (keyCode) {
case KeyCode.SPACE:
case KeyCode.QUESTION_MARK:
case KeyCode.NUM_PLUS:
case KeyCode.NUM_MINUS:
case KeyCode.NUM_PERIOD:
case KeyCode.NUM_DIVISION:
case KeyCode.SEMICOLON:
case KeyCode.DASH:
case KeyCode.EQUALS:
case KeyCode.COMMA:
case KeyCode.PERIOD:
case KeyCode.SLASH:
case KeyCode.APOSTROPHE:
case KeyCode.SINGLE_QUOTE:
case KeyCode.OPEN_SQUARE_BRACKET:
case KeyCode.BACKSLASH:
case KeyCode.CLOSE_SQUARE_BRACKET:
return true;
default:
return false;
}
};

export default KeyCode;
| | :—- | :—- |

pickAttrs

获取节点的指定属性、以及 ‘data-‘|’aria-‘ 前缀的属性。

| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| / eslint-disable max-len /
const attributes = accept acceptCharset accessKey action allowFullScreen allowTransparency<br /> alt async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge<br /> charSet checked classID className colSpan cols content contentEditable contextMenu<br /> controls coords crossOrigin data dateTime default defer dir disabled download draggable<br /> encType form formAction formEncType formMethod formNoValidate formTarget frameBorder<br /> headers height hidden high href hrefLang htmlFor httpEquiv icon id inputMode integrity<br /> is keyParams keyType kind label lang list loop low manifest marginHeight marginWidth max maxLength media<br /> mediaGroup method min minLength multiple muted name noValidate nonce open<br /> optimum pattern placeholder poster preload radioGroup readOnly rel required<br /> reversed role rowSpan rows sandbox scope scoped scrolling seamless selected<br /> shape size sizes span spellCheck src srcDoc srcLang srcSet start step style<br /> summary tabIndex target title type useMap value width wmode wrap
.replace(/\s+/g, ‘ ‘).replace(/\t|\n|\r/g, ‘’).split(‘ ‘);
const eventsName = onCopy onCut onPaste onCompositionEnd onCompositionStart onCompositionUpdate onKeyDown<br /> onKeyPress onKeyUp onFocus onBlur onChange onInput onSubmit onClick onContextMenu onDoubleClick<br /> onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown<br /> onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp onSelect onTouchCancel<br /> onTouchEnd onTouchMove onTouchStart onScroll onWheel onAbort onCanPlay onCanPlayThrough<br /> onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata<br /> onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting onLoad onError
.replace(/\s+/g, ‘ ‘).replace(/\t|\n|\r/g, ‘’).split(‘ ‘);
/ eslint-enable max-len /
const attrsPrefix = [‘data’, ‘aria’];

export default function pickAttrs(props) {
const attrs = {};
for (const key in props) {
if (attributes.indexOf(key) > -1 || eventsName.indexOf(key) > -1) {
attrs[key] = props[key];
/ eslint-disable no-loop-func /
} else if (attrsPrefix.map((prefix) => {
return new RegExp(^${prefix});
}).some((reg) => {
return key.replace(reg, ‘’) !== key;
})) {
/ eslint-enable no-loop-func /
attrs[key] = props[key];
}
}
return attrs;
}
| | :—- | :—- |

其他

  • createChainedFunction(…funcs): 创建任务链函数,逐个调用 funcs 函数。
  • diff(obj1, obj2, depth = 10, path = [], diffList = createArray()): 对比数据,数据内容不等的将塞入 diffList = [{ value1, value2, path }] 中,默认的 diffList 拥有 format, toString 方法进行格式转换或转变为 json 数据。该函数支持嵌套数据对比。
  • guid: 以当前时间生成 guid。
  • deprecated(props, instead, component): 提示 prop 已被移除。
  • warn(msg): 开发环境下提示。