@wordpress/editor 13.25.0 → 13.26.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.
Files changed (223) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE.md +1 -1
  3. package/build/components/document-bar/index.js +19 -7
  4. package/build/components/document-bar/index.js.map +1 -1
  5. package/build/components/document-outline/index.js +82 -1
  6. package/build/components/document-outline/index.js.map +1 -1
  7. package/build/components/document-tools/index.js +160 -0
  8. package/build/components/document-tools/index.js.map +1 -0
  9. package/build/components/editor-canvas/index.js +10 -4
  10. package/build/components/editor-canvas/index.js.map +1 -1
  11. package/build/components/entities-saved-states/index.js +3 -1
  12. package/build/components/entities-saved-states/index.js.map +1 -1
  13. package/build/components/global-keyboard-shortcuts/index.js +12 -2
  14. package/build/components/global-keyboard-shortcuts/index.js.map +1 -1
  15. package/build/components/global-keyboard-shortcuts/register-shortcuts.js +9 -0
  16. package/build/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  17. package/build/components/index.js +56 -8
  18. package/build/components/index.js.map +1 -1
  19. package/build/components/index.native.js +9 -1
  20. package/build/components/index.native.js.map +1 -1
  21. package/build/components/inserter-sidebar/index.js +77 -0
  22. package/build/components/inserter-sidebar/index.js.map +1 -0
  23. package/build/components/list-view-sidebar/index.js +150 -0
  24. package/build/components/list-view-sidebar/index.js.map +1 -0
  25. package/build/components/list-view-sidebar/list-view-outline.js +28 -0
  26. package/build/components/list-view-sidebar/list-view-outline.js.map +1 -0
  27. package/build/components/offline-status/index.native.js +85 -0
  28. package/build/components/offline-status/index.native.js.map +1 -0
  29. package/build/components/page-attributes/panel.js +63 -0
  30. package/build/components/page-attributes/panel.js.map +1 -0
  31. package/build/components/post-discussion/panel.js +59 -0
  32. package/build/components/post-discussion/panel.js.map +1 -0
  33. package/build/components/post-excerpt/check.js +19 -0
  34. package/build/components/post-excerpt/check.js.map +1 -1
  35. package/build/components/post-excerpt/panel.js +55 -0
  36. package/build/components/post-excerpt/panel.js.map +1 -0
  37. package/build/components/post-excerpt/plugin.js +72 -0
  38. package/build/components/post-excerpt/plugin.js.map +1 -0
  39. package/build/components/post-featured-image/index.js +5 -8
  40. package/build/components/post-featured-image/index.js.map +1 -1
  41. package/build/components/post-featured-image/panel.js +60 -0
  42. package/build/components/post-featured-image/panel.js.map +1 -0
  43. package/build/components/post-last-revision/panel.js +27 -0
  44. package/build/components/post-last-revision/panel.js.map +1 -0
  45. package/build/components/post-saved-state/index.js +12 -8
  46. package/build/components/post-saved-state/index.js.map +1 -1
  47. package/build/components/post-taxonomies/panel.js +68 -0
  48. package/build/components/post-taxonomies/panel.js.map +1 -0
  49. package/build/components/post-template/block-theme.js +2 -1
  50. package/build/components/post-template/block-theme.js.map +1 -1
  51. package/build/components/post-template/hooks.js +6 -6
  52. package/build/components/post-template/hooks.js.map +1 -1
  53. package/build/components/post-template/panel.js +1 -2
  54. package/build/components/post-template/panel.js.map +1 -1
  55. package/build/components/post-template/swap-template-button.js +4 -2
  56. package/build/components/post-template/swap-template-button.js.map +1 -1
  57. package/build/components/post-title/index.native.js +25 -14
  58. package/build/components/post-title/index.native.js.map +1 -1
  59. package/build/components/post-view-link/index.js +58 -0
  60. package/build/components/post-view-link/index.js.map +1 -0
  61. package/build/components/post-visibility/check.js +5 -17
  62. package/build/components/post-visibility/check.js.map +1 -1
  63. package/build/components/preview-dropdown/index.js +8 -3
  64. package/build/components/preview-dropdown/index.js.map +1 -1
  65. package/build/components/provider/index.native.js +19 -0
  66. package/build/components/provider/index.native.js.map +1 -1
  67. package/build/components/provider/use-block-editor-settings.js +29 -5
  68. package/build/components/provider/use-block-editor-settings.js.map +1 -1
  69. package/build/private-apis.js +10 -0
  70. package/build/private-apis.js.map +1 -1
  71. package/build/store/actions.js +102 -2
  72. package/build/store/actions.js.map +1 -1
  73. package/build/store/index.js +2 -0
  74. package/build/store/index.js.map +1 -1
  75. package/build/store/private-selectors.js +52 -0
  76. package/build/store/private-selectors.js.map +1 -0
  77. package/build/store/reducer.js +78 -1
  78. package/build/store/reducer.js.map +1 -1
  79. package/build/store/selectors.js +76 -2
  80. package/build/store/selectors.js.map +1 -1
  81. package/build/utils/media-upload/index.js +8 -2
  82. package/build/utils/media-upload/index.js.map +1 -1
  83. package/build-module/components/document-bar/index.js +19 -7
  84. package/build-module/components/document-bar/index.js.map +1 -1
  85. package/build-module/components/document-outline/index.js +82 -1
  86. package/build-module/components/document-outline/index.js.map +1 -1
  87. package/build-module/components/document-tools/index.js +151 -0
  88. package/build-module/components/document-tools/index.js.map +1 -0
  89. package/build-module/components/editor-canvas/index.js +10 -4
  90. package/build-module/components/editor-canvas/index.js.map +1 -1
  91. package/build-module/components/entities-saved-states/index.js +3 -1
  92. package/build-module/components/entities-saved-states/index.js.map +1 -1
  93. package/build-module/components/global-keyboard-shortcuts/index.js +12 -2
  94. package/build-module/components/global-keyboard-shortcuts/index.js.map +1 -1
  95. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js +9 -0
  96. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  97. package/build-module/components/index.js +6 -0
  98. package/build-module/components/index.js.map +1 -1
  99. package/build-module/components/index.native.js +1 -0
  100. package/build-module/components/index.native.js.map +1 -1
  101. package/build-module/components/inserter-sidebar/index.js +70 -0
  102. package/build-module/components/inserter-sidebar/index.js.map +1 -0
  103. package/build-module/components/list-view-sidebar/index.js +142 -0
  104. package/build-module/components/list-view-sidebar/index.js.map +1 -0
  105. package/build-module/components/list-view-sidebar/list-view-outline.js +20 -0
  106. package/build-module/components/list-view-sidebar/list-view-outline.js.map +1 -0
  107. package/build-module/components/offline-status/index.native.js +77 -0
  108. package/build-module/components/offline-status/index.native.js.map +1 -0
  109. package/build-module/components/page-attributes/panel.js +53 -0
  110. package/build-module/components/page-attributes/panel.js.map +1 -0
  111. package/build-module/components/post-discussion/panel.js +50 -0
  112. package/build-module/components/post-discussion/panel.js.map +1 -0
  113. package/build-module/components/post-excerpt/check.js +19 -0
  114. package/build-module/components/post-excerpt/check.js.map +1 -1
  115. package/build-module/components/post-excerpt/panel.js +48 -0
  116. package/build-module/components/post-excerpt/panel.js.map +1 -0
  117. package/build-module/components/post-excerpt/plugin.js +64 -0
  118. package/build-module/components/post-excerpt/plugin.js.map +1 -0
  119. package/build-module/components/post-featured-image/index.js +5 -8
  120. package/build-module/components/post-featured-image/index.js.map +1 -1
  121. package/build-module/components/post-featured-image/panel.js +51 -0
  122. package/build-module/components/post-featured-image/panel.js.map +1 -0
  123. package/build-module/components/post-last-revision/panel.js +18 -0
  124. package/build-module/components/post-last-revision/panel.js.map +1 -0
  125. package/build-module/components/post-saved-state/index.js +12 -8
  126. package/build-module/components/post-saved-state/index.js.map +1 -1
  127. package/build-module/components/post-taxonomies/panel.js +59 -0
  128. package/build-module/components/post-taxonomies/panel.js.map +1 -0
  129. package/build-module/components/post-template/block-theme.js +2 -1
  130. package/build-module/components/post-template/block-theme.js.map +1 -1
  131. package/build-module/components/post-template/hooks.js +6 -6
  132. package/build-module/components/post-template/hooks.js.map +1 -1
  133. package/build-module/components/post-template/panel.js +1 -2
  134. package/build-module/components/post-template/panel.js.map +1 -1
  135. package/build-module/components/post-template/swap-template-button.js +4 -2
  136. package/build-module/components/post-template/swap-template-button.js.map +1 -1
  137. package/build-module/components/post-title/index.native.js +26 -15
  138. package/build-module/components/post-title/index.native.js.map +1 -1
  139. package/build-module/components/post-view-link/index.js +51 -0
  140. package/build-module/components/post-view-link/index.js.map +1 -0
  141. package/build-module/components/post-visibility/check.js +6 -16
  142. package/build-module/components/post-visibility/check.js.map +1 -1
  143. package/build-module/components/preview-dropdown/index.js +8 -3
  144. package/build-module/components/preview-dropdown/index.js.map +1 -1
  145. package/build-module/components/provider/index.native.js +19 -0
  146. package/build-module/components/provider/index.native.js.map +1 -1
  147. package/build-module/components/provider/use-block-editor-settings.js +29 -5
  148. package/build-module/components/provider/use-block-editor-settings.js.map +1 -1
  149. package/build-module/private-apis.js +10 -0
  150. package/build-module/private-apis.js.map +1 -1
  151. package/build-module/store/actions.js +94 -0
  152. package/build-module/store/actions.js.map +1 -1
  153. package/build-module/store/index.js +2 -0
  154. package/build-module/store/index.js.map +1 -1
  155. package/build-module/store/private-selectors.js +43 -0
  156. package/build-module/store/private-selectors.js.map +1 -0
  157. package/build-module/store/reducer.js +74 -1
  158. package/build-module/store/reducer.js.map +1 -1
  159. package/build-module/store/selectors.js +67 -0
  160. package/build-module/store/selectors.js.map +1 -1
  161. package/build-module/utils/media-upload/index.js +8 -2
  162. package/build-module/utils/media-upload/index.js.map +1 -1
  163. package/build-style/style-rtl.css +251 -0
  164. package/build-style/style.css +251 -0
  165. package/package.json +32 -32
  166. package/src/components/document-bar/index.js +39 -28
  167. package/src/components/document-outline/index.js +48 -1
  168. package/src/components/document-outline/style.scss +12 -0
  169. package/src/components/document-tools/index.js +177 -0
  170. package/src/components/document-tools/style.scss +98 -0
  171. package/src/components/editor-canvas/index.js +12 -7
  172. package/src/components/editor-canvas/style.scss +5 -0
  173. package/src/components/entities-saved-states/index.js +3 -1
  174. package/src/components/entities-saved-states/style.scss +4 -0
  175. package/src/components/global-keyboard-shortcuts/index.js +12 -2
  176. package/src/components/global-keyboard-shortcuts/register-shortcuts.js +10 -0
  177. package/src/components/index.js +6 -0
  178. package/src/components/index.native.js +1 -0
  179. package/src/components/inserter-sidebar/index.js +73 -0
  180. package/src/components/inserter-sidebar/style.scss +22 -0
  181. package/src/components/list-view-sidebar/index.js +169 -0
  182. package/src/components/list-view-sidebar/list-view-outline.js +37 -0
  183. package/src/components/list-view-sidebar/style.scss +84 -0
  184. package/src/components/offline-status/index.native.js +101 -0
  185. package/src/components/offline-status/style.native.scss +28 -0
  186. package/src/components/offline-status/test/index.native.js +108 -0
  187. package/src/components/page-attributes/panel.js +62 -0
  188. package/src/components/post-discussion/panel.js +57 -0
  189. package/src/components/post-excerpt/check.js +18 -0
  190. package/src/components/post-excerpt/panel.js +57 -0
  191. package/src/components/post-excerpt/plugin.js +61 -0
  192. package/src/components/post-excerpt/test/plugin.js +36 -0
  193. package/src/components/post-featured-image/index.js +3 -7
  194. package/src/components/post-featured-image/panel.js +55 -0
  195. package/src/components/post-last-revision/panel.js +22 -0
  196. package/src/components/post-last-revision/style.scss +10 -0
  197. package/src/components/post-saved-state/index.js +8 -8
  198. package/src/components/post-taxonomies/panel.js +66 -0
  199. package/src/components/post-template/block-theme.js +2 -1
  200. package/src/components/post-template/hooks.js +6 -6
  201. package/src/components/post-template/panel.js +1 -2
  202. package/src/components/post-template/swap-template-button.js +7 -4
  203. package/src/components/post-title/index.native.js +32 -17
  204. package/src/components/post-title/style.scss +1 -0
  205. package/src/components/post-title/test/__snapshots__/index.native.js.snap +25 -0
  206. package/src/components/post-title/test/index.native.js +78 -0
  207. package/src/components/post-view-link/index.js +47 -0
  208. package/src/components/post-visibility/check.js +10 -15
  209. package/src/components/post-visibility/test/check.js +24 -13
  210. package/src/components/preview-dropdown/index.js +7 -10
  211. package/src/components/provider/index.native.js +29 -2
  212. package/src/components/provider/use-block-editor-settings.js +36 -8
  213. package/src/private-apis.js +10 -0
  214. package/src/store/actions.js +109 -0
  215. package/src/store/index.js +2 -0
  216. package/src/store/private-selectors.js +51 -0
  217. package/src/store/reducer.js +72 -0
  218. package/src/store/selectors.js +80 -0
  219. package/src/store/test/actions.js +56 -0
  220. package/src/store/test/reducer.js +98 -0
  221. package/src/store/test/selectors.js +49 -0
  222. package/src/style.scss +4 -0
  223. package/src/utils/media-upload/index.js +9 -2
@@ -0,0 +1,73 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useDispatch, useSelect } from '@wordpress/data';
5
+ import { Button, VisuallyHidden } from '@wordpress/components';
6
+ import { __experimentalLibrary as Library } from '@wordpress/block-editor';
7
+ import { close } from '@wordpress/icons';
8
+ import {
9
+ useViewportMatch,
10
+ __experimentalUseDialog as useDialog,
11
+ } from '@wordpress/compose';
12
+ import { __ } from '@wordpress/i18n';
13
+ import { useEffect, useRef } from '@wordpress/element';
14
+ import { store as preferencesStore } from '@wordpress/preferences';
15
+
16
+ /**
17
+ * Internal dependencies
18
+ */
19
+ import { unlock } from '../../lock-unlock';
20
+ import { store as editorStore } from '../../store';
21
+
22
+ export default function InserterSidebar() {
23
+ const { insertionPoint, showMostUsedBlocks } = useSelect( ( select ) => {
24
+ const { getInsertionPoint } = unlock( select( editorStore ) );
25
+ const { get } = select( preferencesStore );
26
+ return {
27
+ insertionPoint: getInsertionPoint(),
28
+ showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ),
29
+ };
30
+ }, [] );
31
+ const { setIsInserterOpened } = useDispatch( editorStore );
32
+
33
+ const isMobileViewport = useViewportMatch( 'medium', '<' );
34
+ const TagName = ! isMobileViewport ? VisuallyHidden : 'div';
35
+ const [ inserterDialogRef, inserterDialogProps ] = useDialog( {
36
+ onClose: () => setIsInserterOpened( false ),
37
+ focusOnMount: null,
38
+ } );
39
+
40
+ const libraryRef = useRef();
41
+ useEffect( () => {
42
+ libraryRef.current.focusSearch();
43
+ }, [] );
44
+
45
+ return (
46
+ <div
47
+ ref={ inserterDialogRef }
48
+ { ...inserterDialogProps }
49
+ className="editor-inserter-sidebar"
50
+ >
51
+ <TagName className="editor-inserter-sidebar__header">
52
+ <Button
53
+ icon={ close }
54
+ label={ __( 'Close block inserter' ) }
55
+ onClick={ () => setIsInserterOpened( false ) }
56
+ />
57
+ </TagName>
58
+ <div className="editor-inserter-sidebar__content">
59
+ <Library
60
+ showMostUsedBlocks={ showMostUsedBlocks }
61
+ showInserterHelpPanel
62
+ shouldFocusBlock={ isMobileViewport }
63
+ rootClientId={ insertionPoint.rootClientId }
64
+ __experimentalInsertionIndex={
65
+ insertionPoint.insertionIndex
66
+ }
67
+ __experimentalFilterValue={ insertionPoint.filterValue }
68
+ ref={ libraryRef }
69
+ />
70
+ </div>
71
+ </div>
72
+ );
73
+ }
@@ -0,0 +1,22 @@
1
+ .editor-inserter-sidebar {
2
+ @include reset;
3
+
4
+ height: 100%;
5
+ display: flex;
6
+ flex-direction: column;
7
+ }
8
+
9
+ .editor-inserter-sidebar__header {
10
+ padding-top: $grid-unit-10;
11
+ padding-right: $grid-unit-10;
12
+ display: flex;
13
+ justify-content: flex-end;
14
+ }
15
+
16
+ .editor-inserter-sidebar__content {
17
+ // Leave space for the close button
18
+ height: calc(100% - #{$button-size} - #{$grid-unit-10});
19
+ @include break-medium() {
20
+ height: 100%;
21
+ }
22
+ }
@@ -0,0 +1,169 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __experimentalListView as ListView } from '@wordpress/block-editor';
5
+ import { Button, TabPanel } from '@wordpress/components';
6
+ import { useFocusOnMount, useMergeRefs } from '@wordpress/compose';
7
+ import { useDispatch, useSelect } from '@wordpress/data';
8
+ import { focus } from '@wordpress/dom';
9
+ import { useCallback, useRef, useState } from '@wordpress/element';
10
+ import { __, _x } from '@wordpress/i18n';
11
+ import { closeSmall } from '@wordpress/icons';
12
+ import { useShortcut } from '@wordpress/keyboard-shortcuts';
13
+ import { ESCAPE } from '@wordpress/keycodes';
14
+
15
+ /**
16
+ * Internal dependencies
17
+ */
18
+ import ListViewOutline from './list-view-outline';
19
+ import { unlock } from '../../lock-unlock';
20
+ import { store as editorStore } from '../../store';
21
+
22
+ export default function ListViewSidebar() {
23
+ const { setIsListViewOpened } = useDispatch( editorStore );
24
+ const { getListViewToggleRef } = unlock( useSelect( editorStore ) );
25
+
26
+ // This hook handles focus when the sidebar first renders.
27
+ const focusOnMountRef = useFocusOnMount( 'firstElement' );
28
+
29
+ // When closing the list view, focus should return to the toggle button.
30
+ const closeListView = useCallback( () => {
31
+ setIsListViewOpened( false );
32
+ getListViewToggleRef().current?.focus();
33
+ }, [ getListViewToggleRef, setIsListViewOpened ] );
34
+
35
+ const closeOnEscape = useCallback(
36
+ ( event ) => {
37
+ if ( event.keyCode === ESCAPE && ! event.defaultPrevented ) {
38
+ event.preventDefault();
39
+ closeListView();
40
+ }
41
+ },
42
+ [ closeListView ]
43
+ );
44
+
45
+ // Use internal state instead of a ref to make sure that the component
46
+ // re-renders when the dropZoneElement updates.
47
+ const [ dropZoneElement, setDropZoneElement ] = useState( null );
48
+ // Tracks our current tab.
49
+ const [ tab, setTab ] = useState( 'list-view' );
50
+
51
+ // This ref refers to the sidebar as a whole.
52
+ const sidebarRef = useRef();
53
+ // This ref refers to the tab panel.
54
+ const tabPanelRef = useRef();
55
+ // This ref refers to the list view application area.
56
+ const listViewRef = useRef();
57
+
58
+ // Must merge the refs together so focus can be handled properly in the next function.
59
+ const listViewContainerRef = useMergeRefs( [
60
+ focusOnMountRef,
61
+ listViewRef,
62
+ setDropZoneElement,
63
+ ] );
64
+
65
+ /*
66
+ * Callback function to handle list view or outline focus.
67
+ *
68
+ * @param {string} currentTab The current tab. Either list view or outline.
69
+ *
70
+ * @return void
71
+ */
72
+ function handleSidebarFocus( currentTab ) {
73
+ // Tab panel focus.
74
+ const tabPanelFocus = focus.tabbable.find( tabPanelRef.current )[ 0 ];
75
+ // List view tab is selected.
76
+ if ( currentTab === 'list-view' ) {
77
+ // Either focus the list view or the tab panel. Must have a fallback because the list view does not render when there are no blocks.
78
+ const listViewApplicationFocus = focus.tabbable.find(
79
+ listViewRef.current
80
+ )[ 0 ];
81
+ const listViewFocusArea = sidebarRef.current.contains(
82
+ listViewApplicationFocus
83
+ )
84
+ ? listViewApplicationFocus
85
+ : tabPanelFocus;
86
+ listViewFocusArea.focus();
87
+ // Outline tab is selected.
88
+ } else {
89
+ tabPanelFocus.focus();
90
+ }
91
+ }
92
+
93
+ const handleToggleListViewShortcut = useCallback( () => {
94
+ // If the sidebar has focus, it is safe to close.
95
+ if (
96
+ sidebarRef.current.contains(
97
+ sidebarRef.current.ownerDocument.activeElement
98
+ )
99
+ ) {
100
+ closeListView();
101
+ } else {
102
+ // If the list view or outline does not have focus, focus should be moved to it.
103
+ handleSidebarFocus( tab );
104
+ }
105
+ }, [ closeListView, tab ] );
106
+
107
+ // This only fires when the sidebar is open because of the conditional rendering.
108
+ // It is the same shortcut to open but that is defined as a global shortcut and only fires when the sidebar is closed.
109
+ useShortcut( 'core/editor/toggle-list-view', handleToggleListViewShortcut );
110
+
111
+ /**
112
+ * Render tab content for a given tab name.
113
+ *
114
+ * @param {string} tabName The name of the tab to render.
115
+ */
116
+ function renderTabContent( tabName ) {
117
+ if ( tabName === 'list-view' ) {
118
+ return (
119
+ <div className="editor-list-view-sidebar__list-view-panel-content">
120
+ <ListView dropZoneElement={ dropZoneElement } />
121
+ </div>
122
+ );
123
+ }
124
+ return <ListViewOutline />;
125
+ }
126
+
127
+ return (
128
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
129
+ <div
130
+ className="editor-list-view-sidebar"
131
+ onKeyDown={ closeOnEscape }
132
+ ref={ sidebarRef }
133
+ >
134
+ <Button
135
+ className="editor-list-view-sidebar__close-button"
136
+ icon={ closeSmall }
137
+ label={ __( 'Close' ) }
138
+ onClick={ closeListView }
139
+ />
140
+ <TabPanel
141
+ className="editor-list-view-sidebar__tab-panel"
142
+ ref={ tabPanelRef }
143
+ onSelect={ ( tabName ) => setTab( tabName ) }
144
+ selectOnMove={ false }
145
+ tabs={ [
146
+ {
147
+ name: 'list-view',
148
+ title: _x( 'List View', 'Post overview' ),
149
+ className: 'editor-list-view-sidebar__panel-tab',
150
+ },
151
+ {
152
+ name: 'outline',
153
+ title: _x( 'Outline', 'Post overview' ),
154
+ className: 'editor-list-view-sidebar__panel-tab',
155
+ },
156
+ ] }
157
+ >
158
+ { ( currentTab ) => (
159
+ <div
160
+ className="editor-list-view-sidebar__list-view-container"
161
+ ref={ listViewContainerRef }
162
+ >
163
+ { renderTabContent( currentTab.name ) }
164
+ </div>
165
+ ) }
166
+ </TabPanel>
167
+ </div>
168
+ );
169
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __experimentalText as Text } from '@wordpress/components';
5
+ import { __ } from '@wordpress/i18n';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import CharacterCount from '../character-count';
11
+ import WordCount from '../word-count';
12
+ import TimeToRead from '../time-to-read';
13
+ import DocumentOutline from '../document-outline';
14
+
15
+ export default function ListViewOutline() {
16
+ return (
17
+ <>
18
+ <div className="editor-list-view-sidebar__outline">
19
+ <div>
20
+ <Text>{ __( 'Characters:' ) }</Text>
21
+ <Text>
22
+ <CharacterCount />
23
+ </Text>
24
+ </div>
25
+ <div>
26
+ <Text>{ __( 'Words:' ) }</Text>
27
+ <WordCount />
28
+ </div>
29
+ <div>
30
+ <Text>{ __( 'Time to read:' ) }</Text>
31
+ <TimeToRead />
32
+ </div>
33
+ </div>
34
+ <DocumentOutline />
35
+ </>
36
+ );
37
+ }
@@ -0,0 +1,84 @@
1
+ .editor-list-view-sidebar {
2
+ height: 100%;
3
+ display: flex;
4
+ flex-direction: column;
5
+
6
+ @include break-medium() {
7
+ // Same width as the Inserter.
8
+ // @see packages/block-editor/src/components/inserter/style.scss
9
+ width: 350px;
10
+ }
11
+
12
+ .editor-list-view-sidebar__close-button {
13
+ position: absolute;
14
+ right: $grid-unit-10;
15
+ top: math.div($grid-unit-60 - $button-size, 2); // ( tab height - button size ) / 2
16
+ z-index: 1;
17
+ background: $white;
18
+ }
19
+
20
+ // The TabPanel style overrides in the following blocks should be removed when the new TabPanel is available.
21
+ .components-tab-panel__tabs {
22
+ border-bottom: $border-width solid $gray-300;
23
+ box-sizing: border-box;
24
+ display: flex;
25
+ width: 100%;
26
+ padding-right: $grid-unit-70;
27
+
28
+ .editor-list-view-sidebar__panel-tab {
29
+ width: 50%;
30
+ margin-bottom: -$border-width;
31
+ }
32
+ }
33
+
34
+ .components-tab-panel__tab-content {
35
+ height: calc(100% - #{$grid-unit-60 - $border-width});
36
+ }
37
+ }
38
+
39
+ .editor-list-view-sidebar__list-view-panel-content,
40
+ .editor-list-view-sidebar__list-view-container > .document-outline {
41
+ height: 100%;
42
+
43
+ // Include custom scrollbars, invisible until hovered.
44
+ @include custom-scrollbars-on-hover(transparent, $gray-600);
45
+ overflow: auto;
46
+
47
+ // Only reserve space for scrollbars when there is content to scroll.
48
+ // This allows items in the list view to have equidistant padding left and right
49
+ // right up until a scrollbar is present.
50
+ scrollbar-gutter: auto;
51
+
52
+ // The table cells use an extra pixels of space left and right. We compensate for that here.
53
+ padding: $grid-unit-10 ($grid-unit-10 - $border-width - $border-width);
54
+ }
55
+
56
+ .editor-list-view-sidebar__list-view-container {
57
+ display: flex;
58
+ flex-direction: column;
59
+ height: 100%;
60
+ }
61
+
62
+ .editor-list-view-sidebar__tab-panel {
63
+ height: 100%;
64
+ }
65
+
66
+ .editor-list-view-sidebar__outline {
67
+ display: flex;
68
+ flex-direction: column;
69
+ gap: $grid-unit-10;
70
+ border-bottom: $border-width solid $gray-300;
71
+ padding: $grid-unit-20;
72
+
73
+ & > div > span:first-child {
74
+ // Width of the text information fields.
75
+ width: 90px;
76
+ display: inline-block;
77
+ }
78
+
79
+ & > div > span {
80
+ font-size: $helptext-font-size;
81
+ line-height: $default-line-height;
82
+ color: $gray-700;
83
+ }
84
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { AccessibilityInfo, Text, View } from 'react-native';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import {
10
+ usePreferredColorSchemeStyle,
11
+ useNetworkConnectivity,
12
+ usePrevious,
13
+ } from '@wordpress/compose';
14
+ import { Icon } from '@wordpress/components';
15
+ import { offline as offlineIcon } from '@wordpress/icons';
16
+ import { __ } from '@wordpress/i18n';
17
+ import { useEffect } from '@wordpress/element';
18
+
19
+ /**
20
+ * Internal dependencies
21
+ */
22
+ import styles from './style.native.scss';
23
+
24
+ /**
25
+ * Conditionally announces messages for screen reader users. This Hook provides
26
+ * two benefits over React Native's `accessibilityLiveRegion`:
27
+ *
28
+ * 1. It works on both iOS and Android.
29
+ * 2. It allows announcing a secondary message when the component is inactive.
30
+ *
31
+ * @param {string} message The message to announce.
32
+ * @param {Object} options Options for the Hook.
33
+ * @param {boolean} [options.isActive] Whether the message should be announced.
34
+ * @param {string} [options.inactiveMessage] The message to announce when inactive.
35
+ */
36
+ function useAccessibilityLiveRegion( message, { isActive, inactiveMessage } ) {
37
+ const { announceForAccessibility } = AccessibilityInfo;
38
+ const prevIsActive = usePrevious( isActive );
39
+
40
+ useEffect( () => {
41
+ const unconditionalMessage = typeof isActive === 'undefined';
42
+ const initialRender = typeof prevIsActive === 'undefined';
43
+
44
+ if (
45
+ unconditionalMessage ||
46
+ ( isActive && ! prevIsActive && ! initialRender )
47
+ ) {
48
+ announceForAccessibility( message );
49
+ } else if ( ! isActive && prevIsActive && inactiveMessage ) {
50
+ announceForAccessibility( inactiveMessage );
51
+ }
52
+ }, [
53
+ message,
54
+ isActive,
55
+ prevIsActive,
56
+ inactiveMessage,
57
+ announceForAccessibility,
58
+ ] );
59
+ }
60
+
61
+ const OfflineStatus = () => {
62
+ const { isConnected } = useNetworkConnectivity();
63
+
64
+ useAccessibilityLiveRegion( __( 'Network connection re-established' ), {
65
+ isActive: isConnected,
66
+ inactiveMessage: __( 'Network connection lost, working offline' ),
67
+ } );
68
+
69
+ const containerStyle = usePreferredColorSchemeStyle(
70
+ styles.offline,
71
+ styles.offline__dark
72
+ );
73
+
74
+ const textStyle = usePreferredColorSchemeStyle(
75
+ styles[ 'offline--text' ],
76
+ styles[ 'offline--text__dark' ]
77
+ );
78
+
79
+ const iconStyle = usePreferredColorSchemeStyle(
80
+ styles[ 'offline--icon' ],
81
+ styles[ 'offline--icon__dark' ]
82
+ );
83
+
84
+ return ! isConnected ? (
85
+ <View
86
+ accessible
87
+ accessibilityRole="alert"
88
+ accessibilityLabel={ __(
89
+ 'Network connection lost, working offline'
90
+ ) }
91
+ style={ containerStyle }
92
+ >
93
+ <View style={ containerStyle }>
94
+ <Icon fill={ iconStyle.fill } icon={ offlineIcon } />
95
+ <Text style={ textStyle }>{ __( 'Working Offline' ) }</Text>
96
+ </View>
97
+ </View>
98
+ ) : null;
99
+ };
100
+
101
+ export default OfflineStatus;
@@ -0,0 +1,28 @@
1
+ .offline {
2
+ background-color: $gray-lighten-30;
3
+ padding: $grid-unit;
4
+ justify-content: center;
5
+ align-items: center;
6
+ flex-direction: row;
7
+ }
8
+
9
+ .offline__dark {
10
+ background-color: $gray-70;
11
+ }
12
+
13
+ .offline--text {
14
+ color: $black;
15
+ padding-left: 3;
16
+ }
17
+
18
+ .offline--text__dark {
19
+ color: $white;
20
+ }
21
+
22
+ .offline--icon {
23
+ fill: $gray-70;
24
+ }
25
+
26
+ .offline--icon__dark {
27
+ fill: $gray-10;
28
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { act, render, screen } from 'test/helpers';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import {
10
+ requestConnectionStatus,
11
+ subscribeConnectionStatus,
12
+ } from '@wordpress/react-native-bridge';
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ import OfflineStatus from '../index';
18
+ import { AccessibilityInfo } from 'react-native';
19
+
20
+ jest.mock( '../style.native.scss', () => ( {
21
+ 'offline--icon': {
22
+ fill: '',
23
+ },
24
+ } ) );
25
+
26
+ describe( 'when network connectivity is unavailable', () => {
27
+ beforeAll( () => {
28
+ requestConnectionStatus.mockImplementation( ( callback ) => {
29
+ callback( false );
30
+ return { remove: jest.fn() };
31
+ } );
32
+ } );
33
+
34
+ it( 'should display a helpful message', () => {
35
+ render( <OfflineStatus /> );
36
+
37
+ expect( screen.getByText( 'Working Offline' ) ).toBeVisible();
38
+ } );
39
+
40
+ it( 'should display an accessible message', () => {
41
+ render( <OfflineStatus /> );
42
+
43
+ expect(
44
+ screen.getByLabelText( 'Network connection lost, working offline' )
45
+ ).toBeVisible();
46
+ } );
47
+
48
+ it( 'should announce network status', () => {
49
+ render( <OfflineStatus /> );
50
+
51
+ expect(
52
+ AccessibilityInfo.announceForAccessibility
53
+ ).toHaveBeenCalledWith( 'Network connection lost, working offline' );
54
+ } );
55
+
56
+ it( 'should announce changes to network status', () => {
57
+ let subscriptionCallback;
58
+ subscribeConnectionStatus.mockImplementation( ( callback ) => {
59
+ subscriptionCallback = callback;
60
+ return { remove: jest.fn() };
61
+ } );
62
+ render( <OfflineStatus /> );
63
+
64
+ act( () => subscriptionCallback( { isConnected: false } ) );
65
+
66
+ expect(
67
+ AccessibilityInfo.announceForAccessibility
68
+ ).toHaveBeenCalledWith( 'Network connection lost, working offline' );
69
+ } );
70
+ } );
71
+
72
+ describe( 'when network connectivity is available', () => {
73
+ beforeAll( () => {
74
+ requestConnectionStatus.mockImplementation( ( callback ) => {
75
+ callback( true );
76
+ return { remove: jest.fn() };
77
+ } );
78
+ } );
79
+
80
+ it( 'should not display a helpful message', () => {
81
+ render( <OfflineStatus /> );
82
+
83
+ expect( screen.queryByText( 'Working Offline' ) ).toBeNull();
84
+ } );
85
+
86
+ it( 'should not announce network status', () => {
87
+ render( <OfflineStatus /> );
88
+
89
+ expect(
90
+ AccessibilityInfo.announceForAccessibility
91
+ ).not.toHaveBeenCalled();
92
+ } );
93
+
94
+ it( 'should announce changes to network status', () => {
95
+ let subscriptionCallback;
96
+ subscribeConnectionStatus.mockImplementation( ( callback ) => {
97
+ subscriptionCallback = callback;
98
+ return { remove: jest.fn() };
99
+ } );
100
+ render( <OfflineStatus /> );
101
+
102
+ act( () => subscriptionCallback( { isConnected: false } ) );
103
+
104
+ expect(
105
+ AccessibilityInfo.announceForAccessibility
106
+ ).toHaveBeenCalledWith( 'Network connection lost, working offline' );
107
+ } );
108
+ } );
@@ -0,0 +1,62 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { PanelBody, PanelRow } from '@wordpress/components';
6
+
7
+ import { useSelect, useDispatch } from '@wordpress/data';
8
+ import { store as coreStore } from '@wordpress/core-data';
9
+
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import { store as editorStore } from '../../store';
14
+ import PageAttributesCheck from './check';
15
+ import PageAttributesOrder from './order';
16
+ import PageAttributesParent from './parent';
17
+
18
+ const PANEL_NAME = 'page-attributes';
19
+
20
+ export function PageAttributesPanel() {
21
+ const { isEnabled, isOpened, postType } = useSelect( ( select ) => {
22
+ const {
23
+ getEditedPostAttribute,
24
+ isEditorPanelEnabled,
25
+ isEditorPanelOpened,
26
+ } = select( editorStore );
27
+ const { getPostType } = select( coreStore );
28
+ return {
29
+ isEnabled: isEditorPanelEnabled( PANEL_NAME ),
30
+ isOpened: isEditorPanelOpened( PANEL_NAME ),
31
+ postType: getPostType( getEditedPostAttribute( 'type' ) ),
32
+ };
33
+ }, [] );
34
+
35
+ const { toggleEditorPanelOpened } = useDispatch( editorStore );
36
+
37
+ if ( ! isEnabled || ! postType ) {
38
+ return null;
39
+ }
40
+
41
+ const onTogglePanel = ( ...args ) =>
42
+ toggleEditorPanelOpened( PANEL_NAME, ...args );
43
+
44
+ return (
45
+ <PageAttributesCheck>
46
+ <PanelBody
47
+ title={
48
+ postType?.labels?.attributes ?? __( 'Page attributes' )
49
+ }
50
+ opened={ isOpened }
51
+ onToggle={ onTogglePanel }
52
+ >
53
+ <PageAttributesParent />
54
+ <PanelRow>
55
+ <PageAttributesOrder />
56
+ </PanelRow>
57
+ </PanelBody>
58
+ </PageAttributesCheck>
59
+ );
60
+ }
61
+
62
+ export default PageAttributesPanel;