info
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
export default function (Quill) {
|
||||
const { Scope, Attributor } = Quill.import('parchment')
|
||||
const config = {
|
||||
scope: Scope.BLOCK,
|
||||
whitelist: ['left', 'right', 'center', 'justify']
|
||||
}
|
||||
const AlignStyle = new Attributor.Style('align', 'text-align', config)
|
||||
return {
|
||||
'formats/align': AlignStyle
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export default function (Quill) {
|
||||
const { Scope, Attributor } = Quill.import('parchment')
|
||||
const config = {
|
||||
scope: Scope.BLOCK
|
||||
}
|
||||
const margin = ['margin', 'marginTop', 'marginBottom', 'marginLeft', 'marginRight']
|
||||
const padding = ['padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight']
|
||||
const result = {}
|
||||
margin.concat(padding).forEach(name => {
|
||||
result[`formats/${name}`] = new Attributor.Style(name, name.replace(/[A-Z]/g, str => '-' + str.toLowerCase()), config)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export default function (Quill) {
|
||||
const { Scope, Attributor } = Quill.import('parchment')
|
||||
const config = {
|
||||
scope: Scope.BLOCK,
|
||||
whitelist: ['left', 'right', 'center', 'justify']
|
||||
}
|
||||
|
||||
const AlignStyle = new Attributor.Style('align', 'text-align', config)
|
||||
return {
|
||||
'formats/align': AlignStyle
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
.divider hr {
|
||||
border-color: rgba(0, 0, 0, .1);
|
||||
border-width: 1px 0 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import './divider.css'
|
||||
export default function (Quill) {
|
||||
const BlockEmbed = Quill.import('blots/block/embed')
|
||||
class Divider extends BlockEmbed {
|
||||
static blotName = 'divider'
|
||||
static tagName = 'hr'
|
||||
|
||||
static create (value) {
|
||||
const node = super.create(value)
|
||||
const wrap = document.createElement('div')
|
||||
|
||||
wrap.classList.add('divider')
|
||||
wrap.setAttribute('contenteditable', 'false')
|
||||
wrap.appendChild(node)
|
||||
|
||||
return wrap
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'formats/divider': Divider
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export default function (Quill) {
|
||||
const { Scope, Attributor } = Quill.import('parchment')
|
||||
const config = {
|
||||
scope: Scope.INLINE
|
||||
}
|
||||
const font = ['font', 'fontSize', 'fontStyle', 'fontVariant', 'fontWeight', 'fontFamily']
|
||||
const result = {}
|
||||
font.forEach(name => {
|
||||
result[`formats/${name}`] = new Attributor.Style(name, name.replace(/[A-Z]/g, str => '-' + str.toLowerCase()), config)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
.image-loading {
|
||||
min-height: 50px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.image-loading-inner {
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import './image.css'
|
||||
|
||||
const ATTRIBUTES = [
|
||||
'alt',
|
||||
'height',
|
||||
'width',
|
||||
'data-custom',
|
||||
'class',
|
||||
'data-local'
|
||||
]
|
||||
|
||||
export default function (Quill) {
|
||||
const Image = Quill.import('formats/image')
|
||||
const BlockEmbed = Quill.import('blots/block/embed')
|
||||
class ExtendImageFormat extends Image {
|
||||
static create (value) {
|
||||
if (typeof value === 'string') {
|
||||
value = {
|
||||
src: value,
|
||||
uploaded: false
|
||||
}
|
||||
}
|
||||
|
||||
const {src, uploaded} = value
|
||||
const node = super.create(src)
|
||||
|
||||
if (uploaded) {
|
||||
node.classList.add('uploaded')
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
static formats (domNode) {
|
||||
return ATTRIBUTES.reduce(function (formats, attribute) {
|
||||
if (domNode.hasAttribute(attribute)) {
|
||||
formats[attribute] = domNode.getAttribute(attribute)
|
||||
}
|
||||
return formats
|
||||
}, {})
|
||||
}
|
||||
|
||||
static sanitize (url) {
|
||||
return url
|
||||
}
|
||||
|
||||
format (name, value) {
|
||||
if (ATTRIBUTES.indexOf(name) > -1) {
|
||||
if (value) {
|
||||
this.domNode.setAttribute(name, value)
|
||||
} else {
|
||||
this.domNode.removeAttribute(name)
|
||||
}
|
||||
} else {
|
||||
super.format(name, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
class ImageLoadingFormat extends BlockEmbed {
|
||||
static create (value) {
|
||||
const node = super.create(value)
|
||||
node.setAttribute('contenteditable', false)
|
||||
node.setAttribute('upload-id', value)
|
||||
|
||||
const loading = document.createElement('div')
|
||||
loading.classList.add('image-loading-inner')
|
||||
loading.innerText = '正在上传'
|
||||
|
||||
node.appendChild(loading)
|
||||
|
||||
return node
|
||||
}
|
||||
}
|
||||
ImageLoadingFormat.blotName = 'image-loading'
|
||||
ImageLoadingFormat.tagName = 'div'
|
||||
ImageLoadingFormat.className = 'image-loading'
|
||||
|
||||
return {
|
||||
'formats/image': ExtendImageFormat,
|
||||
'formats/image-loading': ImageLoadingFormat,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import divider from './divider'
|
||||
import align from './align'
|
||||
import list from './list'
|
||||
import box from './box'
|
||||
import font from './font'
|
||||
import text from './text'
|
||||
import image from './image'
|
||||
import link from './link'
|
||||
import unlockContent from "./unlock-content"
|
||||
import mediaVideo from "./media-video"
|
||||
|
||||
export function register (Quill) {
|
||||
const formats = {
|
||||
divider,
|
||||
align,
|
||||
list,
|
||||
box,
|
||||
font,
|
||||
text,
|
||||
image,
|
||||
link,
|
||||
unlockContent,
|
||||
mediaVideo
|
||||
}
|
||||
const options = {}
|
||||
Object.values(formats).forEach(value => Object.assign(options, value(Quill)))
|
||||
Quill.register(options, true)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export default function (Quill) {
|
||||
const Link = Quill.import('formats/link')
|
||||
Link.sanitize = url => {
|
||||
const anchor = document.createElement('a')
|
||||
anchor.href = url
|
||||
const protocol = anchor.href.slice(0, anchor.href.indexOf(':'))
|
||||
return Link.PROTOCOL_WHITELIST.concat('file').indexOf(protocol) > -1 ? url : Link.SANITIZED_URL
|
||||
}
|
||||
|
||||
return {
|
||||
'formats/link': Link
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
export default function (Quill) {
|
||||
const Parchment = Quill.import('parchment')
|
||||
const Container = Quill.import('blots/container')
|
||||
const ListItem = Quill.import('formats/list/item')
|
||||
|
||||
class List extends Container {
|
||||
static create (value) {
|
||||
const tagName = value === 'ordered' ? 'OL' : 'UL'
|
||||
const node = super.create(tagName)
|
||||
if (value === 'checked' || value === 'unchecked') {
|
||||
node.setAttribute('data-checked', value === 'checked')
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
static formats (domNode) {
|
||||
if (domNode.tagName === 'OL') return 'ordered'
|
||||
if (domNode.tagName === 'UL') {
|
||||
if (domNode.hasAttribute('data-checked')) {
|
||||
return domNode.getAttribute('data-checked') === 'true' ? 'checked' : 'unchecked'
|
||||
} else {
|
||||
return 'bullet'
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
constructor (domNode) {
|
||||
super(domNode)
|
||||
const listEventHandler = (e) => {
|
||||
if (e.target.parentNode !== domNode) return
|
||||
const format = this.statics.formats(domNode)
|
||||
const blot = Parchment.find(e.target)
|
||||
if (format === 'checked') {
|
||||
blot.format('list', 'unchecked')
|
||||
} else if (format === 'unchecked') {
|
||||
blot.format('list', 'checked')
|
||||
}
|
||||
}
|
||||
|
||||
domNode.addEventListener('click', listEventHandler)
|
||||
}
|
||||
|
||||
format (name, value) {
|
||||
if (this.children.length > 0) {
|
||||
this.children.tail.format(name, value)
|
||||
}
|
||||
}
|
||||
|
||||
formats () {
|
||||
// We don't inherit from FormatBlot
|
||||
return { [this.statics.blotName]: this.statics.formats(this.domNode) }
|
||||
}
|
||||
|
||||
insertBefore (blot, ref) {
|
||||
if (blot instanceof ListItem) {
|
||||
super.insertBefore(blot, ref)
|
||||
} else {
|
||||
const index = ref == null ? this.length() : ref.offset(this)
|
||||
const after = this.split(index)
|
||||
after.parent.insertBefore(blot, after)
|
||||
}
|
||||
}
|
||||
|
||||
optimize (context) {
|
||||
super.optimize(context)
|
||||
const next = this.next
|
||||
if (next != null && next.prev === this &&
|
||||
next.statics.blotName === this.statics.blotName &&
|
||||
next.domNode.tagName === this.domNode.tagName &&
|
||||
next.domNode.getAttribute('data-checked') === this.domNode.getAttribute('data-checked')) {
|
||||
next.moveChildren(this)
|
||||
next.remove()
|
||||
}
|
||||
}
|
||||
|
||||
replace (target) {
|
||||
if (target.statics.blotName !== this.statics.blotName) {
|
||||
const item = Parchment.create(this.statics.defaultChild)
|
||||
target.moveChildren(item)
|
||||
this.appendChild(item)
|
||||
}
|
||||
super.replace(target)
|
||||
}
|
||||
}
|
||||
List.blotName = 'list'
|
||||
List.scope = Parchment.Scope.BLOCK_BLOT
|
||||
List.tagName = ['OL', 'UL']
|
||||
List.defaultChild = 'list-item'
|
||||
List.allowedChildren = [ListItem]
|
||||
|
||||
return {
|
||||
'formats/list': List
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,92 @@
|
||||
import './media-video.css'
|
||||
|
||||
const ATTRIBUTES = [
|
||||
'poster',
|
||||
'src',
|
||||
'duration'
|
||||
]
|
||||
|
||||
function mediaDuration (duration) {
|
||||
if (!duration) return
|
||||
const hours = Math.floor(duration / 3600)
|
||||
const minutes = Math.floor((duration - hours * 3600) / 60)
|
||||
const seconds = Math.floor(duration - hours * 3600 - minutes * 60)
|
||||
|
||||
return `${hours ? (hours < 10 ? '0' + hours : hours) + ':' : ''}${minutes ? (minutes < 10 ? '0' + minutes: minutes) + ':' : '00:'}${seconds < 10 ? '0' + seconds : seconds}`
|
||||
}
|
||||
|
||||
export default function (Quill) {
|
||||
const BlockEmbed = Quill.import('blots/block/embed')
|
||||
class MediaVideo extends BlockEmbed {
|
||||
static create (value) {
|
||||
const node = super.create(value)
|
||||
node.setAttribute('contenteditable', false)
|
||||
node.setAttribute('data-src', value.src || '')
|
||||
node.setAttribute('data-poster', value.poster || '')
|
||||
node.setAttribute('data-duration', value.duration || '')
|
||||
|
||||
const poster = document.createElement('div')
|
||||
poster.classList.add('ql-media-video-poster')
|
||||
if (value.poster) {
|
||||
poster.style.backgroundImage = `url(${value.poster})`
|
||||
} else {
|
||||
poster.innerText = '无视频封面'
|
||||
poster.classList.add('no-poster')
|
||||
}
|
||||
|
||||
const tip = document.createElement('div')
|
||||
tip.classList.add('ql-media-video-tip')
|
||||
tip.innerText = (value.duration ? `${mediaDuration(value.duration)} `: '') + '此处不支持视频播放'
|
||||
|
||||
const remove = document.createElement('div')
|
||||
remove.classList.add('ql-media-video-remove')
|
||||
|
||||
node.appendChild(poster)
|
||||
node.appendChild(tip)
|
||||
node.appendChild(remove)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
static formats(domNode) {
|
||||
return ATTRIBUTES.reduce(function(formats, attribute) {
|
||||
if (domNode.dataset[attribute]) {
|
||||
formats[attribute] = domNode.dataset[attribute] || '';
|
||||
}
|
||||
return formats;
|
||||
}, {});
|
||||
}
|
||||
|
||||
format(name, value) {
|
||||
switch (name) {
|
||||
case 'src':
|
||||
this.domNode.setAttribute('data-src', value)
|
||||
break
|
||||
case 'poster':
|
||||
this.domNode.setAttribute('data-poster', value)
|
||||
if (value) {
|
||||
this.domNode.querySelector('.ql-media-video-poster').style.backgroundImage = `url(${value})`
|
||||
this.domNode.querySelector('.ql-media-video-poster').innerText = ''
|
||||
this.domNode.querySelector('.ql-media-video-poster').classList.remove('no-poster')
|
||||
} else {
|
||||
this.domNode.querySelector('.ql-media-video-poster').innerText = '无视频封面'
|
||||
this.domNode.querySelector('.ql-media-video-poster').classList.add('no-poster')
|
||||
}
|
||||
break
|
||||
case 'duration':
|
||||
this.domNode.setAttribute('data-duration', value)
|
||||
this.domNode.querySelector('.ql-media-video-tip').innerText = (value ? `${mediaDuration(value)} `: '') + '此处不支持视频播放'
|
||||
break
|
||||
default:
|
||||
super.format(name, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
MediaVideo.blotName = 'mediaVideo'
|
||||
MediaVideo.className = 'ql-media-video'
|
||||
MediaVideo.tagName = 'div'
|
||||
|
||||
return {
|
||||
'formats/mediaVideo': MediaVideo,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export default function (Quill) {
|
||||
const { Scope, Attributor } = Quill.import('parchment')
|
||||
const config = {
|
||||
scope: Scope.BLOCK
|
||||
}
|
||||
const textIndentStyle = new Attributor.Style('text', 'text-indent', config)
|
||||
return {
|
||||
'formats/textIndent1': textIndentStyle
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
export default function (Quill) {
|
||||
const { Scope, Attributor } = Quill.import('parchment')
|
||||
const text = [{
|
||||
name: 'lineHeight',
|
||||
scope: Scope.BLOCK
|
||||
}, {
|
||||
name: 'letterSpacing',
|
||||
scope: Scope.INLINE
|
||||
}, {
|
||||
name: 'textDecoration',
|
||||
scope: Scope.INLINE
|
||||
}, {
|
||||
name: 'textIndent',
|
||||
scope: Scope.BLOCK
|
||||
}]
|
||||
const result = {}
|
||||
text.forEach(({ name, scope }) => {
|
||||
result[`formats/${name}`] = new Attributor.Style(name, name.replace(/[A-Z]/g, str => '-' + str.toLowerCase()), {
|
||||
scope
|
||||
})
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
.ql-unlock-content {
|
||||
width: 100%;
|
||||
padding: 20px 0;
|
||||
font-size: 17px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
margin-bottom: 10px;
|
||||
background: #f8f8f8;
|
||||
border-radius: 5px;
|
||||
color: #888;
|
||||
}
|
||||
.ql-unlock-content span {
|
||||
user-select: none;
|
||||
}
|
||||
.ql-unlock-content a {
|
||||
color: #269939;
|
||||
margin-left: 5px;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import './unlock-content.css'
|
||||
export default function UnlockContent () {
|
||||
const BlockEmbed = Quill.import('blots/block/embed')
|
||||
|
||||
class UnlockContent extends BlockEmbed {
|
||||
static blotName = 'unlockContent'
|
||||
static tagName = 'div'
|
||||
static className = 'ql-unlock-content'
|
||||
|
||||
static create (value) {
|
||||
const node = super.create(value);
|
||||
const span = document.createElement('span')
|
||||
|
||||
node.setAttribute('contenteditable', 'false')
|
||||
|
||||
span.innerHTML = `<span>以下内容需解锁后查看,广告解锁功能详见</span><a href="https://uniapp.dcloud.net.cn/uniCloud/uni-cms.html#watch-ad-unlock-content" target="_blank">文档</a>`
|
||||
|
||||
node.appendChild(span)
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'formats/unlockContent': UnlockContent
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user