-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathindex.js
172 lines (154 loc) · 4.74 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
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
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import {
__unstableGetAnimateClassName as getAnimateClassName,
Button,
} from '@wordpress/components';
import { usePrevious, useViewportMatch } from '@wordpress/compose';
import { useDispatch, useSelect } from '@wordpress/data';
import { useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Icon, check, cloud, cloudUpload } from '@wordpress/icons';
import { displayShortcut } from '@wordpress/keycodes';
/**
* Internal dependencies
*/
import { store as editorStore } from '../../store';
/**
* Component showing whether the post is saved or not and providing save
* buttons.
*
* @param {Object} props Component props.
* @param {?boolean} props.forceIsDirty Whether to force the post to be marked
* as dirty.
* @param {?boolean} props.showIconLabels Whether interface buttons show labels instead of icons
* @return {import('react').ComponentType} The component.
*/
export default function PostSavedState( {
forceIsDirty,
showIconLabels = false,
} ) {
const [ forceSavedMessage, setForceSavedMessage ] = useState( false );
const isLargeViewport = useViewportMatch( 'small' );
const {
isAutosaving,
isDirty,
isNew,
isPending,
isPublished,
isSaveable,
isSaving,
isScheduled,
hasPublishAction,
} = useSelect(
( select ) => {
const {
isEditedPostNew,
isCurrentPostPublished,
isCurrentPostScheduled,
isEditedPostDirty,
isSavingPost,
isEditedPostSaveable,
getCurrentPost,
isAutosavingPost,
getEditedPostAttribute,
} = select( editorStore );
return {
isAutosaving: isAutosavingPost(),
isDirty: forceIsDirty || isEditedPostDirty(),
isNew: isEditedPostNew(),
isPending: 'pending' === getEditedPostAttribute( 'status' ),
isPublished: isCurrentPostPublished(),
isSaving: isSavingPost(),
isSaveable: isEditedPostSaveable(),
isScheduled: isCurrentPostScheduled(),
hasPublishAction:
getCurrentPost()?._links?.[ 'wp:action-publish' ] ?? false,
};
},
[ forceIsDirty ]
);
const { savePost } = useDispatch( editorStore );
const wasSaving = usePrevious( isSaving );
useEffect( () => {
let timeoutId;
if ( wasSaving && ! isSaving ) {
setForceSavedMessage( true );
timeoutId = setTimeout( () => {
setForceSavedMessage( false );
}, 1000 );
}
return () => clearTimeout( timeoutId );
}, [ isSaving ] );
// Once the post has been submitted for review this button
// is not needed for the contributor role.
if ( ! hasPublishAction && isPending ) {
return null;
}
if ( isPublished || isScheduled ) {
return null;
}
/* translators: button label text should, if possible, be under 16 characters. */
const label = isPending ? __( 'Save as pending' ) : __( 'Save draft' );
/* translators: button label text should, if possible, be under 16 characters. */
const shortLabel = __( 'Save' );
const isSaved = forceSavedMessage || ( ! isNew && ! isDirty );
const isSavedState = isSaving || isSaved;
const isDisabled = isSaving || isSaved || ! isSaveable;
let text;
if ( isSaving ) {
text = isAutosaving ? __( 'Autosaving' ) : __( 'Saving' );
} else if ( isSaved ) {
text = __( 'Saved' );
} else if ( isLargeViewport ) {
text = label;
} else if ( showIconLabels ) {
text = shortLabel;
}
// Use common Button instance for all saved states so that focus is not
// lost.
return (
<Button
className={
isSaveable || isSaving
? classnames( {
'editor-post-save-draft': ! isSavedState,
'editor-post-saved-state': isSavedState,
'is-saving': isSaving,
'is-autosaving': isAutosaving,
'is-saved': isSaved,
[ getAnimateClassName( {
type: 'loading',
} ) ]: isSaving,
} )
: undefined
}
onClick={ isDisabled ? undefined : () => savePost() }
/*
* We want the tooltip to show the keyboard shortcut only when the
* button does something, i.e. when it's not disabled.
*/
shortcut={ isDisabled ? undefined : displayShortcut.primary( 's' ) }
/*
* Displaying the keyboard shortcut conditionally makes the tooltip
* itself show conditionally. This would trigger a full-rerendering
* of the button that we want to avoid. By setting `showTooltip`,
& the tooltip is always rendered even when there's no keyboard shortcut.
*/
showTooltip
variant="tertiary"
icon={ isLargeViewport ? undefined : cloudUpload }
// Make sure the aria-label has always a value, as the default `text` is undefined on small screens.
label={ text || label }
aria-disabled={ isDisabled }
>
{ isSavedState && <Icon icon={ isSaved ? check : cloud } /> }
{ text }
</Button>
);
}