@automattic/social-previews 2.0.0 → 2.0.1-beta.1
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.
- package/dist/cjs/facebook-preview/custom-text.js +4 -3
- package/dist/cjs/facebook-preview/custom-text.js.map +1 -1
- package/dist/cjs/facebook-preview/helpers.js +2 -4
- package/dist/cjs/facebook-preview/helpers.js.map +1 -1
- package/dist/cjs/helpers.js +80 -17
- package/dist/cjs/helpers.js.map +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/instagram-preview/constants.js +6 -0
- package/dist/cjs/instagram-preview/constants.js.map +1 -0
- package/dist/cjs/instagram-preview/icons/bookmark.js +9 -0
- package/dist/cjs/instagram-preview/icons/bookmark.js.map +1 -0
- package/dist/cjs/instagram-preview/icons/comment.js +9 -0
- package/dist/cjs/instagram-preview/icons/comment.js.map +1 -0
- package/dist/cjs/instagram-preview/icons/default-avatar.js +9 -0
- package/dist/cjs/instagram-preview/icons/default-avatar.js.map +1 -0
- package/dist/cjs/instagram-preview/icons/heart.js +9 -0
- package/dist/cjs/instagram-preview/icons/heart.js.map +1 -0
- package/dist/cjs/instagram-preview/icons/menu.js +9 -0
- package/dist/cjs/instagram-preview/icons/menu.js.map +1 -0
- package/dist/cjs/instagram-preview/icons/share.js +9 -0
- package/dist/cjs/instagram-preview/icons/share.js.map +1 -0
- package/dist/cjs/instagram-preview/index.js +6 -0
- package/dist/cjs/instagram-preview/index.js.map +1 -0
- package/dist/cjs/instagram-preview/post-preview.js +25 -0
- package/dist/cjs/instagram-preview/post-preview.js.map +1 -0
- package/dist/cjs/instagram-preview/previews.js +15 -0
- package/dist/cjs/instagram-preview/previews.js.map +1 -0
- package/dist/cjs/instagram-preview/style.scss +102 -0
- package/dist/cjs/instagram-preview/types.js +3 -0
- package/dist/cjs/instagram-preview/types.js.map +1 -0
- package/dist/cjs/linkedin-preview/post-preview.js +5 -10
- package/dist/cjs/linkedin-preview/post-preview.js.map +1 -1
- package/dist/cjs/twitter-preview/post-preview.js +1 -1
- package/dist/cjs/twitter-preview/post-preview.js.map +1 -1
- package/dist/cjs/twitter-preview/text.js +1 -6
- package/dist/cjs/twitter-preview/text.js.map +1 -1
- package/dist/esm/facebook-preview/custom-text.js +6 -5
- package/dist/esm/facebook-preview/custom-text.js.map +1 -1
- package/dist/esm/facebook-preview/helpers.js +1 -2
- package/dist/esm/facebook-preview/helpers.js.map +1 -1
- package/dist/esm/helpers.js +79 -16
- package/dist/esm/helpers.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/instagram-preview/constants.js +3 -0
- package/dist/esm/instagram-preview/constants.js.map +1 -0
- package/dist/esm/instagram-preview/icons/bookmark.js +5 -0
- package/dist/esm/instagram-preview/icons/bookmark.js.map +1 -0
- package/dist/esm/instagram-preview/icons/comment.js +5 -0
- package/dist/esm/instagram-preview/icons/comment.js.map +1 -0
- package/dist/esm/instagram-preview/icons/default-avatar.js +5 -0
- package/dist/esm/instagram-preview/icons/default-avatar.js.map +1 -0
- package/dist/esm/instagram-preview/icons/heart.js +5 -0
- package/dist/esm/instagram-preview/icons/heart.js.map +1 -0
- package/dist/esm/instagram-preview/icons/menu.js +5 -0
- package/dist/esm/instagram-preview/icons/menu.js.map +1 -0
- package/dist/esm/instagram-preview/icons/share.js +5 -0
- package/dist/esm/instagram-preview/icons/share.js.map +1 -0
- package/dist/esm/instagram-preview/index.js +3 -0
- package/dist/esm/instagram-preview/index.js.map +1 -0
- package/dist/esm/instagram-preview/post-preview.js +21 -0
- package/dist/esm/instagram-preview/post-preview.js.map +1 -0
- package/dist/esm/instagram-preview/previews.js +10 -0
- package/dist/esm/instagram-preview/previews.js.map +1 -0
- package/dist/esm/instagram-preview/style.scss +102 -0
- package/dist/esm/instagram-preview/types.js +2 -0
- package/dist/esm/instagram-preview/types.js.map +1 -0
- package/dist/esm/linkedin-preview/post-preview.js +5 -10
- package/dist/esm/linkedin-preview/post-preview.js.map +1 -1
- package/dist/esm/twitter-preview/post-preview.js +1 -1
- package/dist/esm/twitter-preview/post-preview.js.map +1 -1
- package/dist/esm/twitter-preview/text.js +1 -6
- package/dist/esm/twitter-preview/text.js.map +1 -1
- package/dist/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/facebook-preview/custom-text.d.ts.map +1 -1
- package/dist/types/facebook-preview/helpers.d.ts +1 -1
- package/dist/types/facebook-preview/helpers.d.ts.map +1 -1
- package/dist/types/helpers.d.ts +5 -2
- package/dist/types/helpers.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/instagram-preview/constants.d.ts +3 -0
- package/dist/types/instagram-preview/constants.d.ts.map +1 -0
- package/dist/types/instagram-preview/icons/bookmark.d.ts +2 -0
- package/dist/types/instagram-preview/icons/bookmark.d.ts.map +1 -0
- package/dist/types/instagram-preview/icons/comment.d.ts +2 -0
- package/dist/types/instagram-preview/icons/comment.d.ts.map +1 -0
- package/dist/types/instagram-preview/icons/default-avatar.d.ts +2 -0
- package/dist/types/instagram-preview/icons/default-avatar.d.ts.map +1 -0
- package/dist/types/instagram-preview/icons/heart.d.ts +2 -0
- package/dist/types/instagram-preview/icons/heart.d.ts.map +1 -0
- package/dist/types/instagram-preview/icons/menu.d.ts +2 -0
- package/dist/types/instagram-preview/icons/menu.d.ts.map +1 -0
- package/dist/types/instagram-preview/icons/share.d.ts +2 -0
- package/dist/types/instagram-preview/icons/share.d.ts.map +1 -0
- package/dist/types/instagram-preview/index.d.ts +3 -0
- package/dist/types/instagram-preview/index.d.ts.map +1 -0
- package/dist/types/instagram-preview/post-preview.d.ts +4 -0
- package/dist/types/instagram-preview/post-preview.d.ts.map +1 -0
- package/dist/types/instagram-preview/previews.d.ts +3 -0
- package/dist/types/instagram-preview/previews.d.ts.map +1 -0
- package/dist/types/instagram-preview/types.d.ts +8 -0
- package/dist/types/instagram-preview/types.d.ts.map +1 -0
- package/dist/types/linkedin-preview/post-preview.d.ts.map +1 -1
- package/dist/types/twitter-preview/text.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/facebook-preview/custom-text.tsx +8 -6
- package/src/facebook-preview/helpers.ts +1 -7
- package/src/helpers.tsx +186 -0
- package/src/index.ts +1 -0
- package/src/instagram-preview/constants.tsx +2 -0
- package/src/instagram-preview/icons/bookmark.tsx +21 -0
- package/src/instagram-preview/icons/comment.tsx +20 -0
- package/src/instagram-preview/icons/default-avatar.tsx +10 -0
- package/src/instagram-preview/icons/heart.tsx +14 -0
- package/src/instagram-preview/icons/menu.tsx +24 -0
- package/src/instagram-preview/icons/share.tsx +30 -0
- package/src/instagram-preview/index.tsx +2 -0
- package/src/instagram-preview/post-preview.tsx +71 -0
- package/src/instagram-preview/previews.tsx +33 -0
- package/src/instagram-preview/style.scss +102 -0
- package/src/instagram-preview/types.ts +9 -0
- package/src/linkedin-preview/post-preview.tsx +7 -12
- package/src/twitter-preview/post-preview.tsx +1 -1
- package/src/twitter-preview/text.tsx +3 -9
- package/src/helpers.ts +0 -107
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/instagram-preview/constants.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,mBAAmB,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bookmark.d.ts","sourceRoot":"","sources":["../../../../src/instagram-preview/icons/bookmark.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAoB5B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment.d.ts","sourceRoot":"","sources":["../../../../src/instagram-preview/icons/comment.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAmB3B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-avatar.d.ts","sourceRoot":"","sources":["../../../../src/instagram-preview/icons/default-avatar.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EASjC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heart.d.ts","sourceRoot":"","sources":["../../../../src/instagram-preview/icons/heart.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAazB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../../../src/instagram-preview/icons/menu.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,EAAE,KAAK,CAAC,EAuBxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share.d.ts","sourceRoot":"","sources":["../../../../src/instagram-preview/icons/share.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EA6BzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/instagram-preview/index.tsx"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-preview.d.ts","sourceRoot":"","sources":["../../../src/instagram-preview/post-preview.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,cAAc,CAAC;AAEtB,wBAAgB,oBAAoB,CAAE,EACrC,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,OAAO,GACP,EAAE,qBAAqB,eAoDvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"previews.d.ts","sourceRoot":"","sources":["../../../src/instagram-preview/previews.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEtD,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAE,sBAAsB,CA2B/D,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SocialPreviewBaseProps, SocialPreviewsBaseProps } from '../types';
|
|
2
|
+
export declare type InstagramPreviewProps = Pick<SocialPreviewBaseProps, 'image'> & {
|
|
3
|
+
name: string;
|
|
4
|
+
profileImage: string;
|
|
5
|
+
caption?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare type InstagramPreviewsProps = InstagramPreviewProps & SocialPreviewsBaseProps;
|
|
8
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/instagram-preview/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAE3E,oBAAY,qBAAqB,GAAG,IAAI,CAAE,sBAAsB,EAAE,OAAO,CAAE,GAAG;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,oBAAY,sBAAsB,GAAG,qBAAqB,GAAG,uBAAuB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-preview.d.ts","sourceRoot":"","sources":["../../../src/linkedin-preview/post-preview.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,cAAc,CAAC;AAEtB,wBAAgB,mBAAmB,CAAE,EACpC,eAAmB,EACnB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,KAAK,EACL,KAAK,EACL,GAAG,GACH,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"post-preview.d.ts","sourceRoot":"","sources":["../../../src/linkedin-preview/post-preview.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,cAAc,CAAC;AAEtB,wBAAgB,mBAAmB,CAAE,EACpC,eAAmB,EACnB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,KAAK,EACL,KAAK,EACL,GAAG,GACH,EAAE,oBAAoB,eA8FtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/twitter-preview/text.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,eAAO,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/twitter-preview/text.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,eAAO,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAE,SAAS,CAerC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { hasTag } from '../helpers';
|
|
2
|
-
import {
|
|
1
|
+
import { hasTag, preparePreviewText } from '../helpers';
|
|
2
|
+
import { CUSTOM_TEXT_LENGTH } from './helpers';
|
|
3
3
|
|
|
4
4
|
type Props = {
|
|
5
5
|
text: string;
|
|
@@ -25,10 +25,12 @@ const CustomText: React.FC< Props > = ( { text, url, forceUrlDisplay } ) => {
|
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
27
|
<p className="facebook-preview__custom-text">
|
|
28
|
-
<span
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
<span>
|
|
29
|
+
{ preparePreviewText( text, {
|
|
30
|
+
platform: 'facebook',
|
|
31
|
+
maxChars: CUSTOM_TEXT_LENGTH,
|
|
32
|
+
} ) }
|
|
33
|
+
</span>
|
|
32
34
|
{ postLink }
|
|
33
35
|
</p>
|
|
34
36
|
);
|
|
@@ -2,7 +2,7 @@ import { firstValid, hardTruncation, shortEnough, stripHtmlTags, Formatter } fro
|
|
|
2
2
|
|
|
3
3
|
const TITLE_LENGTH = 110;
|
|
4
4
|
const DESCRIPTION_LENGTH = 200;
|
|
5
|
-
const CUSTOM_TEXT_LENGTH = 440;
|
|
5
|
+
export const CUSTOM_TEXT_LENGTH = 440;
|
|
6
6
|
|
|
7
7
|
export const baseDomain: Formatter = ( url ) =>
|
|
8
8
|
url
|
|
@@ -20,9 +20,3 @@ export const facebookDescription: Formatter = ( text ) =>
|
|
|
20
20
|
shortEnough( DESCRIPTION_LENGTH ),
|
|
21
21
|
hardTruncation( DESCRIPTION_LENGTH )
|
|
22
22
|
)( stripHtmlTags( text ) ) || '';
|
|
23
|
-
|
|
24
|
-
export const facebookCustomText: Formatter = ( text, options ) =>
|
|
25
|
-
firstValid(
|
|
26
|
-
shortEnough( CUSTOM_TEXT_LENGTH ),
|
|
27
|
-
hardTruncation( CUSTOM_TEXT_LENGTH )
|
|
28
|
-
)( stripHtmlTags( text, options?.allowedTags ) ) || '';
|
package/src/helpers.tsx
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { createInterpolateElement } from '@wordpress/element';
|
|
2
|
+
import { sprintf } from '@wordpress/i18n';
|
|
3
|
+
|
|
4
|
+
export type Formatter = ( text: string, options?: any ) => string;
|
|
5
|
+
type AugmentFormatterReturnType< T extends Formatter, TNewReturn > = (
|
|
6
|
+
...a: Parameters< T >
|
|
7
|
+
) => ReturnType< T > | TNewReturn;
|
|
8
|
+
type ConditionalFormatter = AugmentFormatterReturnType< Formatter, boolean >;
|
|
9
|
+
type NullableFormatter = AugmentFormatterReturnType< Formatter, undefined >;
|
|
10
|
+
|
|
11
|
+
export const baseDomain = ( url: string ): string =>
|
|
12
|
+
url
|
|
13
|
+
.replace( /^[^/]+[/]*/, '' ) // strip leading protocol
|
|
14
|
+
.replace( /\/.*$/, '' ); // strip everything after the domain
|
|
15
|
+
|
|
16
|
+
export const shortEnough: ( n: number ) => ConditionalFormatter = ( limit ) => ( title ) =>
|
|
17
|
+
title.length <= limit ? title : false;
|
|
18
|
+
|
|
19
|
+
export const truncatedAtSpace: ( a: number, b: number ) => ConditionalFormatter =
|
|
20
|
+
( lower, upper ) => ( fullTitle ) => {
|
|
21
|
+
const title = fullTitle.slice( 0, upper );
|
|
22
|
+
const lastSpace = title.lastIndexOf( ' ' );
|
|
23
|
+
|
|
24
|
+
return lastSpace > lower && lastSpace < upper
|
|
25
|
+
? title.slice( 0, lastSpace ).concat( '…' )
|
|
26
|
+
: false;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const hardTruncation: ( n: number ) => Formatter = ( limit ) => ( title ) =>
|
|
30
|
+
title.slice( 0, limit ).concat( '…' );
|
|
31
|
+
|
|
32
|
+
export const firstValid: ( ...args: ConditionalFormatter[] ) => NullableFormatter =
|
|
33
|
+
( ...predicates ) =>
|
|
34
|
+
( a ) =>
|
|
35
|
+
( predicates.find( ( p ) => false !== p( a ) ) as Formatter )?.( a );
|
|
36
|
+
|
|
37
|
+
export const stripHtmlTags: Formatter = ( description, allowedTags = [] ) => {
|
|
38
|
+
const pattern = new RegExp( `(<([^${ allowedTags.join( '' ) }>]+)>)`, 'gi' );
|
|
39
|
+
|
|
40
|
+
return description ? description.replace( pattern, '' ) : '';
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const hasTag = ( text: string, tag: string ): boolean => {
|
|
44
|
+
const pattern = new RegExp( `<${ tag }[^>]*>`, 'gi' );
|
|
45
|
+
|
|
46
|
+
return pattern.test( text );
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const formatTweetDate = new Intl.DateTimeFormat( 'en-US', {
|
|
50
|
+
// Result: "Apr 7", "Dec 31"
|
|
51
|
+
month: 'short',
|
|
52
|
+
day: 'numeric',
|
|
53
|
+
} ).format;
|
|
54
|
+
|
|
55
|
+
export type Platform = 'twitter' | 'facebook' | 'linkedin' | 'instagram';
|
|
56
|
+
|
|
57
|
+
type PreviewTextOptions = {
|
|
58
|
+
platform: Platform;
|
|
59
|
+
maxChars?: number;
|
|
60
|
+
maxLines?: number;
|
|
61
|
+
hyperlinkUrls?: boolean;
|
|
62
|
+
hyperlinkHashtags?: boolean;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const hashtagUrlMap: Record< Platform, string > = {
|
|
66
|
+
twitter: 'https://twitter.com/hashtag/%s',
|
|
67
|
+
facebook: 'https://www.facebook.com/hashtag/%s',
|
|
68
|
+
linkedin: 'https://www.linkedin.com/feed/hashtag/?keywords=%s',
|
|
69
|
+
instagram: 'https://www.instagram.com/explore/tags/%s',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Prepares the text for the preview.
|
|
74
|
+
*/
|
|
75
|
+
export function preparePreviewText( text: string, options: PreviewTextOptions ): React.ReactNode {
|
|
76
|
+
const {
|
|
77
|
+
platform,
|
|
78
|
+
maxChars,
|
|
79
|
+
maxLines,
|
|
80
|
+
hyperlinkHashtags = true,
|
|
81
|
+
// Instagram doesn't support hyperlink URLs at the moment.
|
|
82
|
+
hyperlinkUrls = 'instagram' !== platform,
|
|
83
|
+
} = options;
|
|
84
|
+
|
|
85
|
+
let result = stripHtmlTags( text );
|
|
86
|
+
|
|
87
|
+
if ( maxChars && result.length > maxChars ) {
|
|
88
|
+
result = result.substring( 0, maxChars );
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if ( maxLines ) {
|
|
92
|
+
const lines = result.split( '\n' );
|
|
93
|
+
|
|
94
|
+
if ( lines.length > maxLines ) {
|
|
95
|
+
result = lines.slice( 0, maxLines ).join( '\n' );
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const componentMap: Record< string, React.ReactNode > = {};
|
|
100
|
+
|
|
101
|
+
if ( hyperlinkUrls ) {
|
|
102
|
+
// Convert URLs to hyperlinks.
|
|
103
|
+
// TODO: Use a better regex here to match the URLs without protocol.
|
|
104
|
+
const urls = result.match( /(https?:\/\/\S+)/g ) || [];
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* BEFORE:
|
|
108
|
+
* result = 'Check out this cool site: https://wordpress.org and this one: https://wordpress.com'
|
|
109
|
+
*/
|
|
110
|
+
urls.forEach( ( url, index ) => {
|
|
111
|
+
// Add the element to the component map.
|
|
112
|
+
componentMap[ `Link${ index }` ] = (
|
|
113
|
+
<a href={ url } rel="noopener noreferrer" target="_blank">
|
|
114
|
+
{ url }
|
|
115
|
+
</a>
|
|
116
|
+
);
|
|
117
|
+
// Replace the URL with the component placeholder.
|
|
118
|
+
result = result.replace( url, `<Link${ index } />` );
|
|
119
|
+
} );
|
|
120
|
+
/**
|
|
121
|
+
* AFTER:
|
|
122
|
+
* result = 'Check out this cool site: <Link0 /> and this one: <Link1 />'
|
|
123
|
+
* componentMap = {
|
|
124
|
+
* Link0: <a href="https://wordpress.org" ...>https://wordpress.org</a>,
|
|
125
|
+
* Link1: <a href="https://wordpress.com" ...>https://wordpress.com</a>
|
|
126
|
+
* }
|
|
127
|
+
*/
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Convert hashtags to hyperlinks.
|
|
131
|
+
if ( hyperlinkHashtags && hashtagUrlMap[ platform ] ) {
|
|
132
|
+
/**
|
|
133
|
+
* We need to ensure that only the standalone hashtags are matched.
|
|
134
|
+
* For example, we don't want to match the hash in the URL.
|
|
135
|
+
* Thus we need to match the whitespace character before the hashtag or the beginning of the string.
|
|
136
|
+
*/
|
|
137
|
+
const hashtags = result.matchAll( /(^|\s)#(\w+)/g );
|
|
138
|
+
|
|
139
|
+
const hashtagUrl = hashtagUrlMap[ platform ];
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* BEFORE:
|
|
143
|
+
* result = `#breaking text with a #hashtag on the #web
|
|
144
|
+
* with a url https://github.com/Automattic/wp-calypso#security that has a hash in it`
|
|
145
|
+
*/
|
|
146
|
+
[ ...hashtags ].forEach( ( [ fullMatch, whitespace, hashtag ], index ) => {
|
|
147
|
+
const url = sprintf( hashtagUrl, hashtag );
|
|
148
|
+
|
|
149
|
+
// Add the element to the component map.
|
|
150
|
+
componentMap[ `Hashtag${ index }` ] = (
|
|
151
|
+
<a href={ url } rel="noopener noreferrer" target="_blank">
|
|
152
|
+
{ `#${ hashtag }` }
|
|
153
|
+
</a>
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// Replace the hashtag with the component placeholder.
|
|
157
|
+
result = result.replace( fullMatch, `${ whitespace }<Hashtag${ index } />` );
|
|
158
|
+
} );
|
|
159
|
+
/**
|
|
160
|
+
* AFTER:
|
|
161
|
+
* result = `<Hashtag0 /> text with a <Hashtag1 /> on the <Hashtag2 />
|
|
162
|
+
* with a url https://github.com/Automattic/wp-calypso#security that has a hash in it`
|
|
163
|
+
*
|
|
164
|
+
* componentMap = {
|
|
165
|
+
* Hashtag0: <a href="https://twitter.com/hashtag/breaking" ...>#breaking</a>,
|
|
166
|
+
* Hashtag1: <a href="https://twitter.com/hashtag/hashtag" ...>#hashtag</a>,
|
|
167
|
+
* Hashtag2: <a href="https://twitter.com/hashtag/web" ...>#web</a>
|
|
168
|
+
* }
|
|
169
|
+
*/
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Convert newlines to <br> tags.
|
|
173
|
+
/**
|
|
174
|
+
* BEFORE:
|
|
175
|
+
* result = 'This is a text\nwith a newline\nin it'
|
|
176
|
+
*/
|
|
177
|
+
result = result.replace( /\n/g, '<br />' );
|
|
178
|
+
componentMap.br = <br />;
|
|
179
|
+
/**
|
|
180
|
+
* AFTER:
|
|
181
|
+
* result = 'This is a text<br />with a newline<br />in it'
|
|
182
|
+
* componentMap = { br: <br /> }
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
return createInterpolateElement( result, componentMap );
|
|
186
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const Bookmark: React.FC = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
color="rgb(38, 38, 38)"
|
|
5
|
+
fill="rgb(38, 38, 38)"
|
|
6
|
+
height="24"
|
|
7
|
+
role="img"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
width="24"
|
|
10
|
+
>
|
|
11
|
+
<polygon
|
|
12
|
+
fill="none"
|
|
13
|
+
points="20 21 12 13.44 4 21 4 3 20 3 20 21"
|
|
14
|
+
stroke="currentColor"
|
|
15
|
+
strokeLinecap="round"
|
|
16
|
+
strokeLinejoin="round"
|
|
17
|
+
strokeWidth="2"
|
|
18
|
+
></polygon>
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const Comment: React.FC = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
color="rgb(38, 38, 38)"
|
|
5
|
+
fill="rgb(38, 38, 38)"
|
|
6
|
+
height="24"
|
|
7
|
+
role="img"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
width="24"
|
|
10
|
+
>
|
|
11
|
+
<path
|
|
12
|
+
d="M20.656 17.008a9.993 9.993 0 1 0-3.59 3.615L22 22Z"
|
|
13
|
+
fill="none"
|
|
14
|
+
stroke="currentColor"
|
|
15
|
+
strokeLinejoin="round"
|
|
16
|
+
strokeWidth="2"
|
|
17
|
+
></path>
|
|
18
|
+
</svg>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const DefaultAvatar: React.FC = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 340 340" width="340" height="340">
|
|
4
|
+
<path
|
|
5
|
+
fill="#DDD"
|
|
6
|
+
d="m169,.5a169,169 0 1,0 2,0zm0,86a76,76 0 1 1-2,0zM57,287q27-35 67-35h92q40,0 67,35a164,164 0 0,1-226,0"
|
|
7
|
+
/>
|
|
8
|
+
</svg>
|
|
9
|
+
);
|
|
10
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const Heart: React.FC = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
color="rgb(38, 38, 38)"
|
|
5
|
+
fill="rgb(38, 38, 38)"
|
|
6
|
+
height="24"
|
|
7
|
+
role="img"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
width="24"
|
|
10
|
+
>
|
|
11
|
+
<path d="M16.792 3.904A4.989 4.989 0 0 1 21.5 9.122c0 3.072-2.652 4.959-5.197 7.222-2.512 2.243-3.865 3.469-4.303 3.752-.477-.309-2.143-1.823-4.303-3.752C5.141 14.072 2.5 12.167 2.5 9.122a4.989 4.989 0 0 1 4.708-5.218 4.21 4.21 0 0 1 3.675 1.941c.84 1.175.98 1.763 1.12 1.763s.278-.588 1.11-1.766a4.17 4.17 0 0 1 3.679-1.938m0-2a6.04 6.04 0 0 0-4.797 2.127 6.052 6.052 0 0 0-4.787-2.127A6.985 6.985 0 0 0 .5 9.122c0 3.61 2.55 5.827 5.015 7.97.283.246.569.494.853.747l1.027.918a44.998 44.998 0 0 0 3.518 3.018 2 2 0 0 0 2.174 0 45.263 45.263 0 0 0 3.626-3.115l.922-.824c.293-.26.59-.519.885-.774 2.334-2.025 4.98-4.32 4.98-7.94a6.985 6.985 0 0 0-6.708-7.218Z"></path>
|
|
12
|
+
</svg>
|
|
13
|
+
);
|
|
14
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const Menu: React.FC = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg width="17" height="5" viewBox="0 0 17 5" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<path
|
|
5
|
+
d="M2.11865 3.5C2.67094 3.5 3.11865 3.05228 3.11865 2.5C3.11865 1.94772 2.67094 1.5 2.11865 1.5C1.56637 1.5 1.11865 1.94772 1.11865 2.5C1.11865 3.05228 1.56637 3.5 2.11865 3.5Z"
|
|
6
|
+
fill="black"
|
|
7
|
+
stroke="black"
|
|
8
|
+
strokeWidth="2"
|
|
9
|
+
/>
|
|
10
|
+
<path
|
|
11
|
+
d="M8.55933 3.5C9.11161 3.5 9.55933 3.05228 9.55933 2.5C9.55933 1.94772 9.11161 1.5 8.55933 1.5C8.00704 1.5 7.55933 1.94772 7.55933 2.5C7.55933 3.05228 8.00704 3.5 8.55933 3.5Z"
|
|
12
|
+
fill="black"
|
|
13
|
+
stroke="black"
|
|
14
|
+
strokeWidth="2"
|
|
15
|
+
/>
|
|
16
|
+
<path
|
|
17
|
+
d="M15 3.5C15.5523 3.5 16 3.05228 16 2.5C16 1.94772 15.5523 1.5 15 1.5C14.4477 1.5 14 1.94772 14 2.5C14 3.05228 14.4477 3.5 15 3.5Z"
|
|
18
|
+
fill="black"
|
|
19
|
+
stroke="black"
|
|
20
|
+
strokeWidth="2"
|
|
21
|
+
/>
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const Share: React.FC = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
color="rgb(38, 38, 38)"
|
|
5
|
+
fill="rgb(38, 38, 38)"
|
|
6
|
+
height="24"
|
|
7
|
+
role="img"
|
|
8
|
+
viewBox="0 0 24 24"
|
|
9
|
+
width="24"
|
|
10
|
+
>
|
|
11
|
+
<line
|
|
12
|
+
fill="none"
|
|
13
|
+
stroke="currentColor"
|
|
14
|
+
strokeLinejoin="round"
|
|
15
|
+
strokeWidth="2"
|
|
16
|
+
x1="22"
|
|
17
|
+
x2="9.218"
|
|
18
|
+
y1="3"
|
|
19
|
+
y2="10.083"
|
|
20
|
+
></line>
|
|
21
|
+
<polygon
|
|
22
|
+
fill="none"
|
|
23
|
+
points="11.698 20.334 22 3.001 2 3.001 9.218 10.084 11.698 20.334"
|
|
24
|
+
stroke="currentColor"
|
|
25
|
+
strokeLinejoin="round"
|
|
26
|
+
strokeWidth="2"
|
|
27
|
+
></polygon>
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
import { preparePreviewText } from '../helpers';
|
|
3
|
+
import { FEED_TEXT_MAX_LENGTH, FEED_TEXT_MAX_LINES } from './constants';
|
|
4
|
+
import { Bookmark as BookmarkIcon } from './icons/bookmark';
|
|
5
|
+
import { Comment as CommentIcon } from './icons/comment';
|
|
6
|
+
import { DefaultAvatar } from './icons/default-avatar';
|
|
7
|
+
import { Heart as HeartIcon } from './icons/heart';
|
|
8
|
+
import { Menu as MenuIcon } from './icons/menu';
|
|
9
|
+
import { Share as ShareIcon } from './icons/share';
|
|
10
|
+
import { InstagramPreviewProps } from './types';
|
|
11
|
+
|
|
12
|
+
import './style.scss';
|
|
13
|
+
|
|
14
|
+
export function InstagramPostPreview( {
|
|
15
|
+
image,
|
|
16
|
+
name,
|
|
17
|
+
profileImage,
|
|
18
|
+
caption,
|
|
19
|
+
}: InstagramPreviewProps ) {
|
|
20
|
+
const username = name || 'username';
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="instagram-preview__wrapper">
|
|
24
|
+
<section className="instagram-preview__container">
|
|
25
|
+
<div className="instagram-preview__header">
|
|
26
|
+
<div className="instagram-preview__header--avatar">
|
|
27
|
+
{ profileImage ? <img src={ profileImage } alt="" /> : <DefaultAvatar /> }
|
|
28
|
+
</div>
|
|
29
|
+
<div className="instagram-preview__header--profile">
|
|
30
|
+
<div className="instagram-preview__header--profile-name">{ username }</div>
|
|
31
|
+
<div className="instagram-preview__header--profile-menu">
|
|
32
|
+
<MenuIcon />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div className="instagram-preview__media">
|
|
37
|
+
<img className="instagram-preview__media--image" src={ image } alt="" />
|
|
38
|
+
</div>
|
|
39
|
+
<div className="instagram-preview__content">
|
|
40
|
+
<section className="instagram-preview__content--actions">
|
|
41
|
+
<div className="instagram-preview__content--actions-primary">
|
|
42
|
+
<HeartIcon />
|
|
43
|
+
<CommentIcon />
|
|
44
|
+
<ShareIcon />
|
|
45
|
+
</div>
|
|
46
|
+
<div className="instagram-preview__content--actions-secondary">
|
|
47
|
+
<BookmarkIcon />
|
|
48
|
+
</div>
|
|
49
|
+
</section>
|
|
50
|
+
<div className="instagram-preview__content--body">
|
|
51
|
+
<div className="instagram-preview__content--name">{ username }</div>
|
|
52
|
+
|
|
53
|
+
{ caption ? (
|
|
54
|
+
<div className="instagram-preview__content--text">
|
|
55
|
+
{ preparePreviewText( caption, {
|
|
56
|
+
platform: 'instagram',
|
|
57
|
+
maxChars: FEED_TEXT_MAX_LENGTH,
|
|
58
|
+
maxLines: FEED_TEXT_MAX_LINES,
|
|
59
|
+
hyperlinkUrls: false,
|
|
60
|
+
} ) }
|
|
61
|
+
</div>
|
|
62
|
+
) : null }
|
|
63
|
+
</div>
|
|
64
|
+
<div className="instagram-preview__content--footer">
|
|
65
|
+
<span>{ __( 'View one comment', 'social-previews' ) }</span>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</section>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
import SectionHeading from '../shared/section-heading';
|
|
3
|
+
import { InstagramPostPreview } from './post-preview';
|
|
4
|
+
import type { InstagramPreviewsProps } from './types';
|
|
5
|
+
|
|
6
|
+
export const InstagramPreviews: React.FC< InstagramPreviewsProps > = ( {
|
|
7
|
+
headingLevel,
|
|
8
|
+
hideLinkPreview,
|
|
9
|
+
hidePostPreview,
|
|
10
|
+
...props
|
|
11
|
+
} ) => {
|
|
12
|
+
return (
|
|
13
|
+
<div className="social-preview instagram-preview">
|
|
14
|
+
{ ! hidePostPreview && (
|
|
15
|
+
<section className="social-preview__section instagram-preview__section">
|
|
16
|
+
<SectionHeading level={ headingLevel }>
|
|
17
|
+
{
|
|
18
|
+
// translators: refers to a social post on Instagram
|
|
19
|
+
__( 'Your post', 'social-previews' )
|
|
20
|
+
}
|
|
21
|
+
</SectionHeading>
|
|
22
|
+
<p className="social-preview__section-desc">
|
|
23
|
+
{ __(
|
|
24
|
+
'This is what your social post will look like on Instagram:',
|
|
25
|
+
'social-previews'
|
|
26
|
+
) }
|
|
27
|
+
</p>
|
|
28
|
+
<InstagramPostPreview { ...props } />
|
|
29
|
+
</section>
|
|
30
|
+
) }
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
};
|