-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathindex.js
109 lines (93 loc) · 2.88 KB
/
index.js
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
/**
* External dependencies
*/
import { clone } from 'lodash';
/**
* WordPress dependencies
*/
import { applyFilters, hasFilter } from '@wordpress/hooks';
import { Component } from '@wordpress/element';
import { Autocomplete as OriginalAutocomplete } from '@wordpress/components';
/*
* Use one array instance for fallback rather than inline array literals
* because the latter may cause rerender due to failed prop equality checks.
*/
const completersFallback = [];
/**
* Wrap the default Autocomplete component with one that
* supports a filter hook for customizing its list of autocompleters.
*
* Since there may be many Autocomplete instances at one time, this component
* applies the filter on demand, when the component is first focused after
* receiving a new list of completers.
*
* This function is exported for unit test.
*
* @param {Function} Autocomplete Original component.
* @return {Function} Wrapped component
*/
export function withFilteredAutocompleters( Autocomplete ) {
return class FilteredAutocomplete extends Component {
constructor() {
super();
this.state = { completers: completersFallback };
this.saveParentRef = this.saveParentRef.bind( this );
this.onFocus = this.onFocus.bind( this );
}
componentDidUpdate() {
const hasFocus = this.parentNode.contains( document.activeElement );
/*
* It's possible for props to be updated when the component has focus,
* so here, we ensure new completers are immediately applied while we
* have the focus.
*
* NOTE: This may trigger another render but only when the component has focus.
*/
if ( hasFocus && this.hasStaleCompleters() ) {
this.updateCompletersState();
}
}
onFocus() {
if ( this.hasStaleCompleters() ) {
this.updateCompletersState();
}
}
hasStaleCompleters() {
return (
! ( 'lastFilteredCompletersProp' in this.state ) ||
this.state.lastFilteredCompletersProp !== this.props.completers
);
}
updateCompletersState() {
let { completers: nextCompleters } = this.props;
const lastFilteredCompletersProp = nextCompleters;
if ( hasFilter( 'blocks.Autocomplete.completers' ) ) {
nextCompleters = applyFilters(
'blocks.Autocomplete.completers',
// Provide copies so filters may directly modify them.
nextCompleters && nextCompleters.map( clone )
);
}
this.setState( {
lastFilteredCompletersProp,
completers: nextCompleters || completersFallback,
} );
}
saveParentRef( parentNode ) {
this.parentNode = parentNode;
}
render() {
const { completers } = this.state;
const autocompleteProps = {
...this.props,
completers,
};
return (
<div onFocus={ this.onFocus } ref={ this.saveParentRef }>
<Autocomplete onFocus={ this.onFocus } { ...autocompleteProps } />
</div>
);
}
};
}
export default withFilteredAutocompleters( OriginalAutocomplete );