Align title correctly when first character is emoji
This commit is contained in:
parent
cfcdae8aa0
commit
297bf850c2
|
@ -1,5 +1,7 @@
|
|||
[include]
|
||||
.*/frontend/.*
|
||||
.*/server/.*
|
||||
.*/shared/.*
|
||||
|
||||
[ignore]
|
||||
.*/node_modules/styled-components/.*
|
||||
|
|
|
@ -29,6 +29,7 @@ type Props = {
|
|||
onImageUploadStart: Function,
|
||||
onImageUploadStop: Function,
|
||||
starred: boolean,
|
||||
emoji: string,
|
||||
readOnly: boolean,
|
||||
heading?: ?React.Element<*>,
|
||||
};
|
||||
|
@ -213,6 +214,7 @@ type KeyData = {
|
|||
className={cx(styles.editor, { readOnly: this.props.readOnly })}
|
||||
schema={this.schema}
|
||||
plugins={this.plugins}
|
||||
emoji={this.props.emoji}
|
||||
state={this.state.state}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onChange={this.onChange}
|
||||
|
|
|
@ -25,6 +25,10 @@ type Context = {
|
|||
starred?: boolean,
|
||||
};
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin-left: ${props => (props.hasEmoji ? '-1.2em' : 0)}
|
||||
`;
|
||||
|
||||
const StyledStar = styled(StarIcon)`
|
||||
top: 3px;
|
||||
position: relative;
|
||||
|
@ -61,10 +65,14 @@ function Heading(props: Props, { starred }: Context) {
|
|||
const showStar = readOnly && !!onStar;
|
||||
const showHash = readOnly && !!slugish && !showStar;
|
||||
const Component = component;
|
||||
const emoji = editor.props.emoji || '';
|
||||
const title = node.text.trim();
|
||||
const startsWithEmojiAndSpace =
|
||||
emoji && title.match(new RegExp(`^${emoji}\\s`));
|
||||
|
||||
return (
|
||||
<Component className={styles.title}>
|
||||
{children}
|
||||
<Wrapper hasEmoji={startsWithEmojiAndSpace}>{children}</Wrapper>
|
||||
{showPlaceholder &&
|
||||
<span className={styles.placeholder} contentEditable={false}>
|
||||
{editor.props.placeholder}
|
||||
|
|
|
@ -5,15 +5,11 @@ import invariant from 'invariant';
|
|||
import { client } from 'utils/ApiClient';
|
||||
import stores from 'stores';
|
||||
import ErrorsStore from 'stores/ErrorsStore';
|
||||
import parseTitle from '../../shared/parseTitle';
|
||||
|
||||
import type { User } from 'types';
|
||||
import Collection from './Collection';
|
||||
|
||||
const parseHeader = text => {
|
||||
const firstLine = text.trim().split(/\r?\n/)[0];
|
||||
return firstLine.replace(/^#/, '').trim();
|
||||
};
|
||||
|
||||
const DEFAULT_TITLE = 'Untitled document';
|
||||
|
||||
class Document {
|
||||
|
@ -31,6 +27,7 @@ class Document {
|
|||
html: string;
|
||||
id: string;
|
||||
team: string;
|
||||
emoji: string;
|
||||
private: boolean = false;
|
||||
starred: boolean = false;
|
||||
text: string = '';
|
||||
|
@ -181,7 +178,11 @@ class Document {
|
|||
};
|
||||
|
||||
updateData(data: Object = {}, dirty: boolean = false) {
|
||||
if (data.text) data.title = parseHeader(data.text);
|
||||
if (data.text) {
|
||||
const { title, emoji } = parseTitle(data.text);
|
||||
data.title = title;
|
||||
data.emoji = emoji;
|
||||
}
|
||||
if (dirty) this.hasPendingChanges = true;
|
||||
this.data = data;
|
||||
extendObservable(this, data);
|
||||
|
|
|
@ -203,6 +203,7 @@ type Props = {
|
|||
<Editor
|
||||
key={document.id}
|
||||
text={document.text}
|
||||
emoji={document.emoji}
|
||||
onImageUploadStart={this.onImageUploadStart}
|
||||
onImageUploadStop={this.onImageUploadStop}
|
||||
onChange={this.onChange}
|
||||
|
|
|
@ -8,6 +8,7 @@ import isUUID from 'validator/lib/isUUID';
|
|||
import { DataTypes, sequelize } from '../sequelize';
|
||||
import { convertToMarkdown } from '../../frontend/utils/markdown';
|
||||
import { truncateMarkdown } from '../utils/truncate';
|
||||
import parseTitle from '../../shared/parseTitle';
|
||||
import Revision from './Revision';
|
||||
|
||||
const URL_REGEX = /^[a-zA-Z0-9-]*-([a-zA-Z0-9]{10,15})$/;
|
||||
|
@ -35,15 +36,10 @@ const createUrlId = doc => {
|
|||
return (doc.urlId = doc.urlId || randomstring.generate(10));
|
||||
};
|
||||
|
||||
const extractEmoji = doc => {
|
||||
const regex = emojiRegex();
|
||||
const match = regex.exec(doc.title);
|
||||
|
||||
if (match.length) return match[0];
|
||||
return null;
|
||||
};
|
||||
|
||||
const beforeSave = async doc => {
|
||||
const { emoji } = parseTitle(doc.text);
|
||||
|
||||
doc.emoji = emoji;
|
||||
doc.html = convertToMarkdown(doc.text);
|
||||
doc.preview = truncateMarkdown(doc.text, 160);
|
||||
doc.revisionCount += 1;
|
||||
|
@ -62,7 +58,6 @@ const beforeSave = async doc => {
|
|||
// We'll add the current user as revision hasn't been generated yet
|
||||
ids.push(doc.lastModifiedById);
|
||||
doc.collaboratorIds = _.uniq(ids);
|
||||
doc.emoji = extractEmoji(doc);
|
||||
|
||||
return doc;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// @flow
|
||||
import emojiRegex from 'emoji-regex';
|
||||
|
||||
export default function parseTitle(text: string = '') {
|
||||
const regex = emojiRegex();
|
||||
|
||||
// find and extract title
|
||||
const firstLine = text.trim().split(/\r?\n/)[0];
|
||||
const title = firstLine.replace(/^#/, '').trim();
|
||||
|
||||
// find and extract first emoji
|
||||
const matches = regex.exec(title);
|
||||
const firstEmoji = matches ? matches[0] : null;
|
||||
const startsWithEmoji = firstEmoji && title.startsWith(firstEmoji);
|
||||
const emoji = startsWithEmoji ? firstEmoji : undefined;
|
||||
|
||||
return { title, emoji };
|
||||
}
|
|
@ -26,7 +26,10 @@ module.exports = {
|
|||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel',
|
||||
include: path.join(__dirname, 'frontend'),
|
||||
include: [
|
||||
path.join(__dirname, 'frontend'),
|
||||
path.join(__dirname, 'shared'),
|
||||
]
|
||||
},
|
||||
{ test: /\.json$/, loader: 'json-loader' },
|
||||
// inline base64 URLs for <=8k images, direct URLs for the rest
|
||||
|
|
|
@ -2499,6 +2499,10 @@ emoji-regex@^6.1.0:
|
|||
version "6.4.2"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.4.2.tgz#a30b6fee353d406d96cfb9fa765bdc82897eff6e"
|
||||
|
||||
emoji-regex@^6.5.1:
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2"
|
||||
|
||||
emojilib@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/emojilib/-/emojilib-2.0.2.tgz#df91c45ede69f2d0ffd3d80acf8c72771b2a5ea1"
|
||||
|
|
Reference in New Issue