Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Support inline image and small image #1318

Merged
merged 1 commit into from Sep 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 34 additions & 23 deletions src/muya/lib/assets/styles/index.css
Expand Up @@ -755,33 +755,49 @@ span.ag-warn.ag-emoji-marked-text {
vertical-align: middle;
}

.ag-inline-image,
.ag-image-container {
font-size: 0;
line-height: 0;
}

.ag-inline-image {
display: flex;
display: inline-block;
position: relative;
background: var(--codeBlockBgColor);
border-radius: 3px;
overflow: hidden;
margin: 0 auto;
justify-content: space-around;
border-radius: 2px;
}

.ag-inline-image .ag-image-container {
display: inline-block;
position: relative;
overflow: hidden;
}

.ag-inline-image.ag-image-success {
background: transparent;
}

.ag-inline-image a.ag-image-icon-turninto,
.ag-inline-image a.ag-image-icon-delete {
.ag-inline-image .ag-image-buttons {
opacity: 0;
display: none;
flex-direction: row;
justify-content: space-around;
align-items: center;
position: absolute;
border-radius: 6px;
top: 10px;
width: 75px;
height: 40px;
right: 10px;
}

.ag-inline-image a.ag-image-icon-turninto,
.ag-inline-image a.ag-image-icon-delete {
display: flex;
justify-content: space-around;
align-items: center;
width: 30px;
height: 30px;
position: absolute;
top: 15px;
background: rgba(50, 54, 58, .5);
border-radius: 12px;
cursor: pointer;
Expand All @@ -798,14 +814,6 @@ span.ag-warn.ag-emoji-marked-text {
background: rgba(50, 54, 58, .8);
}

.ag-inline-image a.ag-image-icon-turninto {
right: 60px;
}

.ag-inline-image a.ag-image-icon-delete {
right: 15px;
}

.ag-inline-image a.ag-image-icon-success,
.ag-inline-image a.ag-image-icon-fail,
.ag-inline-image a.ag-image-icon-close {
Expand Down Expand Up @@ -883,13 +891,9 @@ span.ag-warn.ag-emoji-marked-text {
display: block;
}

.ag-inline-image.ag-image-success:hover a.ag-image-icon-turninto,
.ag-inline-image.ag-image-success:hover a.ag-image-icon-delete {
.ag-inline-image.ag-image-success:hover span.ag-image-buttons {
opacity: 1;
display: flex;
align-items: center;
justify-content: space-around;
z-index: 1;
}

.ag-inline-image.ag-empty-image:hover a.ag-image-icon-close,
Expand Down Expand Up @@ -924,6 +928,13 @@ span.ag-warn.ag-emoji-marked-text {
color: var(--iconColor);
}

.ag-inline-image.ag-small-image .ag-image-buttons {
top: -40px;
left: 50%;
transform: translateX(-50%);
background: var(--floatBgColor);
}

.ag-image-marked-text ~ img {
display: block;
margin: 0 auto;
Expand Down
1 change: 1 addition & 0 deletions src/muya/lib/config/index.js
Expand Up @@ -96,6 +96,7 @@ export const CLASS_OR_ID = genUpper2LowerKeyHash([
'AG_HTML_PREVIEW',
'AG_HTML_TAG',
'AG_IMAGE_FAIL',
'AG_IMAGE_BUTTONS',
'AG_IMAGE_LOADING',
'AG_EMPTY_IMAGE',
'AG_IMAGE_MARKED_TEXT',
Expand Down
17 changes: 14 additions & 3 deletions src/muya/lib/parser/render/renderInlines/image.js
Expand Up @@ -32,11 +32,13 @@ export default function image (h, cursor, block, token, outerClass) {
}
let id
let isSuccess
let width
let height
let { src } = imageInfo
const alt = token.alt + encodeURI(token.backlash.first)
const { title } = token
if (src) {
({ id, isSuccess } = this.loadImageAsync(imageInfo, alt))
({ id, isSuccess, width, height } = this.loadImageAsync(imageInfo, alt))
}
let wrapperSelector = id
? `span#${id}.${CLASS_OR_ID.AG_INLINE_IMAGE}`
Expand All @@ -48,8 +50,14 @@ export default function image (h, cursor, block, token, outerClass) {
renderIcon(h, 'ag-image-icon-close', DeleteIcon)
]
const toolIcons = [
renderIcon(h, 'ag-image-icon-turninto', ImageEditIcon),
renderIcon(h, 'ag-image-icon-delete', DeleteIcon)
h(`span.${CLASS_OR_ID.AG_IMAGE_BUTTONS}`, {
attrs: {
contenteditable: 'false'
}
}, [
renderIcon(h, 'ag-image-icon-turninto', ImageEditIcon),
renderIcon(h, 'ag-image-icon-delete', DeleteIcon)
])
]
const renderImageContainer = (...args) => {
return h(`span.${CLASS_OR_ID.AG_IMAGE_CONTAINER}`, {}, args)
Expand Down Expand Up @@ -82,6 +90,9 @@ export default function image (h, cursor, block, token, outerClass) {
wrapperSelector += `.${CLASS_OR_ID.AG_IMAGE_LOADING}`
} else if (isSuccess === true) {
wrapperSelector += `.${CLASS_OR_ID.AG_IMAGE_SUCCESS}`
if (typeof width === 'number' && typeof height === 'number' && (width < 100 || height < 100)) {
wrapperSelector += '.ag-small-image'
}
} else {
wrapperSelector += `.${CLASS_OR_ID.AG_IMAGE_FAIL}`
}
Expand Down
16 changes: 13 additions & 3 deletions src/muya/lib/parser/render/renderInlines/loadImageAsync.js
Expand Up @@ -6,11 +6,13 @@ export default function loadImageAsync (imageInfo, alt, className, imageClass) {
const { src, isUnknownType } = imageInfo
let id
let isSuccess
let w
let h

if (!this.loadImageMap.has(src)) {
id = getUniqueId()
loadImage(src, isUnknownType)
.then(url => {
.then(({ url, width, height }) => {
const imageText = document.querySelector(`#${id}`)
const img = document.createElement('img')
img.src = url
Expand All @@ -29,6 +31,10 @@ export default function loadImageAsync (imageInfo, alt, className, imageClass) {
imageContainer.appendChild(img)
imageText.classList.remove('ag-image-loading')
imageText.classList.add('ag-image-success')
// Add `ag-small-image` class name to inline image wrapper if the image is smaller than 100px.
if (width < 100 || height < 100) {
imageText.classList.add('ag-small-image')
}
} else {
insertAfter(img, imageText)
operateClassName(imageText, 'add', className)
Expand All @@ -39,7 +45,9 @@ export default function loadImageAsync (imageInfo, alt, className, imageClass) {
}
this.loadImageMap.set(src, {
id,
isSuccess: true
isSuccess: true,
width,
height
})
})
.catch(() => {
Expand All @@ -64,7 +72,9 @@ export default function loadImageAsync (imageInfo, alt, className, imageClass) {
const imageInfo = this.loadImageMap.get(src)
id = imageInfo.id
isSuccess = imageInfo.isSuccess
w = imageInfo.width
h = imageInfo.height
}

return { id, isSuccess }
return { id, isSuccess, width: w, height: h }
}
6 changes: 5 additions & 1 deletion src/muya/lib/utils/index.js
Expand Up @@ -142,7 +142,11 @@ export const loadImage = async (url, detectContentType = false) => {
return new Promise((resolve, reject) => {
const image = new Image()
image.onload = () => {
resolve(url)
resolve({
url,
width: image.width,
height: image.height
})
}
image.onerror = err => {
reject(err)
Expand Down