diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js
index 2e375ec1684833..e120c53daf2c01 100644
--- a/packages/block-editor/src/components/media-placeholder/index.native.js
+++ b/packages/block-editor/src/components/media-placeholder/index.native.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import { View, Text, TouchableWithoutFeedback } from 'react-native';
-import { uniqBy } from 'lodash';
+import { uniqWith } from 'lodash';
/**
* WordPress dependencies
@@ -36,7 +36,9 @@ function MediaPlaceholder( props ) {
} = props;
const setMedia = multiple && addToGallery ?
- ( selected ) => onSelect( uniqBy( [ ...value, ...selected ], 'id' ) ) :
+ ( selected ) => onSelect( uniqWith( [ ...value, ...selected ], ( media1, media2 ) => {
+ return media1.id === media2.id || media1.url === media2.url;
+ } ) ) :
onSelect;
const isOneType = allowedTypes.length === 1;
diff --git a/packages/block-editor/src/components/media-upload/index.native.js b/packages/block-editor/src/components/media-upload/index.native.js
index e25885c73503bb..72a337cc921bd1 100644
--- a/packages/block-editor/src/components/media-upload/index.native.js
+++ b/packages/block-editor/src/components/media-upload/index.native.js
@@ -86,7 +86,15 @@ export class MediaUpload extends React.Component {
}
getMediaOptionsItems() {
- const { allowedTypes = [] } = this.props;
+ const { allowedTypes = [], multiple = false } = this.props;
+
+ // disable upload sources for now when multiple flag is set
+ // eslint-disable-next-line no-undef
+ if ( ! __DEV__ ) {
+ if ( allowedTypes.includes( MEDIA_TYPE_IMAGE ) && multiple ) {
+ return [ siteLibrarySource ];
+ }
+ }
return this.getAllSources().filter( ( source ) => {
return allowedTypes.filter( ( allowedType ) => source.types.includes( allowedType ) ).length > 0;
diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js
index d4f51b1565cffe..2a5767b71be071 100644
--- a/packages/block-library/src/gallery/edit.js
+++ b/packages/block-library/src/gallery/edit.js
@@ -1,7 +1,6 @@
/**
* External dependencies
*/
-import classnames from 'classnames';
import {
every,
filter,
@@ -23,22 +22,21 @@ import {
withNotices,
} from '@wordpress/components';
import {
- BlockIcon,
MediaPlaceholder,
InspectorControls,
- RichText,
} from '@wordpress/block-editor';
-import { Component } from '@wordpress/element';
-import { __, sprintf } from '@wordpress/i18n';
+import { Component, Platform } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob';
import { withSelect } from '@wordpress/data';
+import { withViewportMatch } from '@wordpress/viewport';
/**
* Internal dependencies
*/
-import GalleryImage from './gallery-image';
-import { icon } from './icons';
+import { sharedIcon } from './shared-icon';
import { defaultColumnsNumber, pickRelevantMediaFiles } from './shared';
+import Gallery from './gallery';
const MAX_COLUMNS = 8;
const linkOptions = [
@@ -48,6 +46,18 @@ const linkOptions = [
];
const ALLOWED_MEDIA_TYPES = [ 'image' ];
+const PLACEHOLDER_TEXT = Platform.select( {
+ web: __( 'Drag images, upload new ones or select files from your library.' ),
+ native: __( 'ADD MEDIA' ),
+} );
+
+// currently this is needed for consistent controls UI on mobile
+// this can be removed after control components settle on consistent defaults
+const MOBILE_CONTROL_PROPS = Platform.select( {
+ web: {},
+ native: { separatorType: 'fullWidth' },
+} );
+
class GalleryEdit extends Component {
constructor() {
super( ...arguments );
@@ -227,7 +237,7 @@ class GalleryEdit extends Component {
componentDidMount() {
const { attributes, mediaUpload } = this.props;
const { images } = attributes;
- if ( every( images, ( { url } ) => isBlobURL( url ) ) ) {
+ if ( Platform.OS === 'web' && every( images, ( { url } ) => isBlobURL( url ) ) ) {
const filesList = map( images, ( { url } ) => getBlobByURL( url ) );
forEach( images, ( { url } ) => revokeBlobURL( url ) );
mediaUpload( {
@@ -254,12 +264,9 @@ class GalleryEdit extends Component {
className,
isSelected,
noticeUI,
- setAttributes,
} = this.props;
const {
- align,
columns = defaultColumnsNumber( attributes ),
- caption,
imageCrop,
images,
linkTo,
@@ -274,10 +281,10 @@ class GalleryEdit extends Component {
isAppender={ hasImages }
className={ className }
disableMediaButtons={ hasImages && ! isSelected }
- icon={ ! hasImages && }
+ icon={ ! hasImages && sharedIcon }
labels={ {
title: ! hasImages && __( 'Gallery' ),
- instructions: ! hasImages && __( 'Drag images, upload new ones or select files from your library.' ),
+ instructions: ! hasImages && PLACEHOLDER_TEXT,
} }
onSelect={ this.onSelectImages }
accept="image/*"
@@ -286,25 +293,20 @@ class GalleryEdit extends Component {
value={ hasImagesWithId ? images : undefined }
onError={ this.onUploadError }
notices={ hasImages ? undefined : noticeUI }
+ onFocus={ this.props.onFocus }
/>
);
if ( ! hasImages ) {
return mediaPlaceholder;
}
-
- const captionClassNames = classnames(
- 'blocks-gallery-caption',
- {
- 'screen-reader-text': ! isSelected && RichText.isEmpty( caption ),
- }
- );
return (
<>
{ images.length > 1 && }
{ noticeUI }
-
+
>
);
}
@@ -383,4 +352,5 @@ export default compose( [
return { mediaUpload };
} ),
withNotices,
+ withViewportMatch( { isNarrow: '< small' } ),
] )( GalleryEdit );
diff --git a/packages/block-library/src/gallery/gallery-image.native.js b/packages/block-library/src/gallery/gallery-image.native.js
index 38623d565e3391..60d6138521c5fd 100644
--- a/packages/block-library/src/gallery/gallery-image.native.js
+++ b/packages/block-library/src/gallery/gallery-image.native.js
@@ -6,13 +6,14 @@ import {
requestImageFailedRetryDialog,
requestImageUploadCancelDialog,
} from 'react-native-gutenberg-bridge';
+import { isEmpty } from 'lodash';
/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { Icon } from '@wordpress/components';
-import { __ } from '@wordpress/i18n';
+import { __, sprintf } from '@wordpress/i18n';
import { RichText, MediaUploadProgress } from '@wordpress/block-editor';
import { isURL } from '@wordpress/url';
import { withPreferredColorScheme } from '@wordpress/compose';
@@ -258,7 +259,7 @@ class GalleryImage extends Component {
}
render() {
- const { id, onRemove, getStylesFromColorScheme } = this.props;
+ const { id, onRemove, getStylesFromColorScheme, isSelected } = this.props;
const containerStyle = getStylesFromColorScheme( style.galleryImageContainer,
style.galleryImageContainerDark );
@@ -266,6 +267,9 @@ class GalleryImage extends Component {
return (
);
}
+
+ accessibilityLabelImageContainer() {
+ const { caption, 'aria-label': ariaLabel } = this.props;
+
+ return isEmpty( caption ) ? ariaLabel : ( ariaLabel + '. ' + sprintf(
+ /* translators: accessibility text. %s: image caption. */
+ __( 'Image caption. %s' ), caption
+ ) );
+ }
}
export default withPreferredColorScheme( GalleryImage );
diff --git a/packages/block-library/src/gallery/gallery.js b/packages/block-library/src/gallery/gallery.js
new file mode 100644
index 00000000000000..2f1a53156c522f
--- /dev/null
+++ b/packages/block-library/src/gallery/gallery.js
@@ -0,0 +1,101 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ RichText,
+} from '@wordpress/block-editor';
+import { __, sprintf } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import GalleryImage from './gallery-image';
+import { defaultColumnsNumber } from './shared';
+
+export const Gallery = ( props ) => {
+ const {
+ attributes,
+ className,
+ isSelected,
+ setAttributes,
+ selectedImage,
+ mediaPlaceholder,
+ onMoveBackward,
+ onMoveForward,
+ onRemoveImage,
+ onSelectImage,
+ onSetImageAttributes,
+ onFocusGalleryCaption,
+ } = props;
+
+ const {
+ align,
+ columns = defaultColumnsNumber( attributes ),
+ caption,
+ imageCrop,
+ images,
+ } = attributes;
+
+ const captionClassNames = classnames(
+ 'blocks-gallery-caption',
+ {
+ 'screen-reader-text': ! isSelected && RichText.isEmpty( caption ),
+ }
+ );
+
+ return (
+
+ );
+};
+
+export default Gallery;
diff --git a/packages/block-library/src/gallery/shared-icon.js b/packages/block-library/src/gallery/shared-icon.js
new file mode 100644
index 00000000000000..68ec4aa0f75dda
--- /dev/null
+++ b/packages/block-library/src/gallery/shared-icon.js
@@ -0,0 +1,11 @@
+/**
+ * WordPress dependencies
+ */
+import { BlockIcon } from '@wordpress/block-editor';
+
+/**
+ * Internal dependencies
+ */
+import { icon } from './icons.js';
+
+export const sharedIcon = ;
diff --git a/packages/block-library/src/gallery/shared-icon.native.js b/packages/block-library/src/gallery/shared-icon.native.js
new file mode 100644
index 00000000000000..30ccbf2929dc5e
--- /dev/null
+++ b/packages/block-library/src/gallery/shared-icon.native.js
@@ -0,0 +1,18 @@
+/**
+ * WordPress dependencies
+ */
+import { Icon } from '@wordpress/components';
+import { withPreferredColorScheme } from '@wordpress/compose';
+
+/**
+ * Internal dependencies
+ */
+import { icon } from './icons.js';
+import styles from './styles.scss';
+
+const IconWithColorScheme = withPreferredColorScheme( ( { getStylesFromColorScheme } ) => {
+ const colorSchemeStyles = getStylesFromColorScheme( styles.icon, styles.iconDark );
+ return ;
+} );
+
+export const sharedIcon = ;
diff --git a/packages/block-library/src/gallery/styles.native.scss b/packages/block-library/src/gallery/styles.native.scss
new file mode 100644
index 00000000000000..f5c00393677262
--- /dev/null
+++ b/packages/block-library/src/gallery/styles.native.scss
@@ -0,0 +1,7 @@
+.icon {
+ fill: $gray-dark;
+}
+
+.iconDark {
+ fill: $white;
+}
diff --git a/packages/block-library/src/index.native.js b/packages/block-library/src/index.native.js
index 22e534fb2669e5..bdfc5cbb0fabf6 100644
--- a/packages/block-library/src/index.native.js
+++ b/packages/block-library/src/index.native.js
@@ -146,6 +146,7 @@ export const registerCoreBlocks = () => {
list,
quote,
mediaText,
+ gallery,
// eslint-disable-next-line no-undef
( ( Platform.OS === 'ios' ) || ( !! __DEV__ ) ) ? preformatted : null,
// eslint-disable-next-line no-undef
diff --git a/packages/editor/src/utils/index.native.js b/packages/editor/src/utils/index.native.js
index e69de29bb2d1d6..3a49f66b86c902 100644
--- a/packages/editor/src/utils/index.native.js
+++ b/packages/editor/src/utils/index.native.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import mediaUpload from './media-upload';
+
+export { mediaUpload };
diff --git a/packages/editor/src/utils/media-upload/index.native.js b/packages/editor/src/utils/media-upload/index.native.js
new file mode 100644
index 00000000000000..9b7a53d3376d55
--- /dev/null
+++ b/packages/editor/src/utils/media-upload/index.native.js
@@ -0,0 +1 @@
+export default function() { }