feat:add codeblock
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<svg width="14" height="14" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path stroke="#525C6F" stroke-linejoin="round" d="M4 4.5L1.5 7 4 9.5M10 4.5L12.5 7 10 9.5"></path><rect fill="#525C6F" transform="scale(1 -1) rotate(70 16.711 0)" x="2.671" y="6.53" width="8" height="1" rx=".2"></rect></g></svg>
|
||||
|
After Width: | Height: | Size: 326 B |
@@ -3,10 +3,12 @@ import './dialog.css'
|
||||
|
||||
export interface IDialogData {
|
||||
type: string;
|
||||
label: string;
|
||||
label?: string;
|
||||
name: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export interface IDialogConfirm {
|
||||
@@ -27,7 +29,7 @@ export class Dialog {
|
||||
private options: IDialogOptions
|
||||
private mask: HTMLDivElement | null
|
||||
private container: HTMLDivElement | null
|
||||
private inputList: HTMLInputElement[]
|
||||
private inputList: (HTMLInputElement | HTMLTextAreaElement)[]
|
||||
|
||||
constructor(options: IDialogOptions) {
|
||||
this.options = options
|
||||
@@ -77,12 +79,25 @@ export class Dialog {
|
||||
const optionItemContainer = document.createElement('div')
|
||||
optionItemContainer.classList.add('dialog-option__item')
|
||||
// 选项名称
|
||||
const optionName = document.createElement('span')
|
||||
optionName.append(document.createTextNode(option.label))
|
||||
optionItemContainer.append(optionName)
|
||||
if (option.label) {
|
||||
const optionName = document.createElement('span')
|
||||
optionName.append(document.createTextNode(option.label))
|
||||
optionItemContainer.append(optionName)
|
||||
}
|
||||
// 选项输入框
|
||||
const optionInput = document.createElement('input')
|
||||
optionInput.type = option.type
|
||||
let optionInput: HTMLInputElement | HTMLTextAreaElement
|
||||
if (option.type === 'textarea') {
|
||||
optionInput = document.createElement('textarea')
|
||||
} else {
|
||||
optionInput = document.createElement('input')
|
||||
optionInput.type = option.type
|
||||
}
|
||||
if (option.width) {
|
||||
optionInput.style.width = `${option.width}px`
|
||||
}
|
||||
if (option.height) {
|
||||
optionInput.style.height = `${option.height}px`
|
||||
}
|
||||
optionInput.name = option.name
|
||||
optionInput.value = option.value || ''
|
||||
optionInput.placeholder = option.placeholder || ''
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
|
||||
.dialog-option__item {
|
||||
margin-bottom: 18px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -64,21 +63,23 @@
|
||||
color: #3d4757;
|
||||
}
|
||||
|
||||
.dialog-option__item input {
|
||||
.dialog-option__item input,
|
||||
.dialog-option__item textarea {
|
||||
width: 276px;
|
||||
height: 30px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #d3d3d3;
|
||||
min-height: 30px;
|
||||
line-height: 17px;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
user-select: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.dialog-option__item input:focus {
|
||||
.dialog-option__item input:focus,
|
||||
.dialog-option__item textarea:focus {
|
||||
border-color: #4991f2;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ export class Command {
|
||||
private static pageScaleRecovery: Function
|
||||
private static pageScaleMinus: Function
|
||||
private static pageScaleAdd: Function
|
||||
private static insertElementList: Function
|
||||
|
||||
constructor(adapt: CommandAdapt) {
|
||||
Command.cut = adapt.cut.bind(adapt)
|
||||
@@ -94,6 +95,7 @@ export class Command {
|
||||
Command.pageScaleRecovery = adapt.pageScaleRecovery.bind(adapt)
|
||||
Command.pageScaleMinus = adapt.pageScaleMinus.bind(adapt)
|
||||
Command.pageScaleAdd = adapt.pageScaleAdd.bind(adapt)
|
||||
Command.insertElementList = adapt.insertElementList.bind(adapt)
|
||||
}
|
||||
|
||||
// 全局命令
|
||||
@@ -273,4 +275,9 @@ export class Command {
|
||||
return Command.pageScaleAdd()
|
||||
}
|
||||
|
||||
// 通用
|
||||
public executeInsertElementList(payload: IElement[]) {
|
||||
return Command.insertElementList(payload)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1094,4 +1094,24 @@ export class CommandAdapt {
|
||||
}
|
||||
}
|
||||
|
||||
public insertElementList(payload: IElement[]) {
|
||||
if (!payload.length) return
|
||||
const { startIndex, endIndex } = this.range.getRange()
|
||||
if (!~startIndex && !~endIndex) return
|
||||
// 格式化element
|
||||
formatElementList(payload, false)
|
||||
const elementList = this.draw.getElementList()
|
||||
const isCollapsed = startIndex === endIndex
|
||||
let start = startIndex + 1
|
||||
if (!isCollapsed) {
|
||||
elementList.splice(start, endIndex - startIndex)
|
||||
}
|
||||
for (let i = 0; i < payload.length; i++) {
|
||||
elementList.splice(start + i, 0, payload[i])
|
||||
}
|
||||
const curIndex = startIndex + payload.length
|
||||
this.range.setRange(curIndex, curIndex)
|
||||
this.draw.render({ curIndex })
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,8 @@ import { ElementType, IElement } from ".."
|
||||
import { ZERO } from "../dataset/constant/Common"
|
||||
import { EDITOR_ELEMENT_ZIP_ATTR } from "../dataset/constant/Element"
|
||||
|
||||
export function formatElementList(elementList: IElement[]) {
|
||||
if (elementList[0]?.value !== ZERO) {
|
||||
export function formatElementList(elementList: IElement[], isHandleFirstElement = true) {
|
||||
if (isHandleFirstElement && elementList[0]?.value !== ZERO) {
|
||||
elementList.unshift({
|
||||
value: ZERO
|
||||
})
|
||||
|
||||
+49
@@ -1,6 +1,8 @@
|
||||
import './style.css'
|
||||
import prism from "prismjs"
|
||||
import Editor, { ElementType, IElement, RowFlex } from './editor'
|
||||
import { Dialog } from './components/dialog/Dialog'
|
||||
import { formatPrismToken } from './utils/prism'
|
||||
|
||||
window.onload = function () {
|
||||
|
||||
@@ -533,6 +535,53 @@ window.onload = function () {
|
||||
instance.command.executeDeleteWatermark()
|
||||
}
|
||||
}
|
||||
|
||||
const codeblockDom = document.querySelector<HTMLDivElement>('.menu-item__codeblock')!
|
||||
codeblockDom.onclick = function () {
|
||||
console.log('codeblock')
|
||||
new Dialog({
|
||||
title: '代码块',
|
||||
data: [{
|
||||
type: 'textarea',
|
||||
name: 'codeblock',
|
||||
placeholder: '请输入代码',
|
||||
width: 500,
|
||||
height: 300
|
||||
}],
|
||||
onConfirm: (payload) => {
|
||||
const codeblock = payload.find(p => p.name === 'codeblock')?.value
|
||||
if (!codeblock) return
|
||||
const tokenList = prism.tokenize(codeblock, prism.languages.javascript)
|
||||
const formatTokenList = formatPrismToken(tokenList)
|
||||
const elementList: IElement[] = []
|
||||
for (let i = 0; i < formatTokenList.length; i++) {
|
||||
const formatToken = formatTokenList[i]
|
||||
const tokenStringList = formatToken.content.split('')
|
||||
for (let j = 0; j < tokenStringList.length; j++) {
|
||||
const value = tokenStringList[j]
|
||||
const element: IElement = {
|
||||
value
|
||||
}
|
||||
if (formatToken.color) {
|
||||
element.color = formatToken.color
|
||||
}
|
||||
if (formatToken.bold) {
|
||||
element.bold = true
|
||||
}
|
||||
if (formatToken.italic) {
|
||||
element.italic = true
|
||||
}
|
||||
elementList.push(element)
|
||||
}
|
||||
}
|
||||
elementList.unshift({
|
||||
value: '\n'
|
||||
})
|
||||
instance.command.executeInsertElementList(elementList)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const searchCollapseDom = document.querySelector<HTMLDivElement>('.menu-item__search__collapse')
|
||||
const searchInputDom = document.querySelector<HTMLInputElement>('.menu-item__search__collapse__search input')
|
||||
document.querySelector<HTMLDivElement>('.menu-item__search')!.onclick = function () {
|
||||
|
||||
@@ -421,6 +421,10 @@ ul {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-item__codeblock i {
|
||||
background-image: url('./assets/images/codeblock.svg');
|
||||
}
|
||||
|
||||
.menu-item__search {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
interface IPrismKindStyle {
|
||||
color?: string;
|
||||
italic?: boolean;
|
||||
opacity?: number;
|
||||
bold?: boolean;
|
||||
}
|
||||
|
||||
export function getPrismKindStyle(payload: string): IPrismKindStyle | null {
|
||||
switch (payload) {
|
||||
case "comment":
|
||||
case "prolog":
|
||||
case "doctype":
|
||||
case "cdata":
|
||||
return { color: "#008000", italic: true }
|
||||
case "namespace":
|
||||
return { opacity: 0.7 }
|
||||
case "string":
|
||||
return { color: "#A31515" }
|
||||
case "punctuation":
|
||||
case "operator":
|
||||
return { color: "#393A34" }
|
||||
case "url":
|
||||
case "symbol":
|
||||
case "number":
|
||||
case "boolean":
|
||||
case "variable":
|
||||
case "constant":
|
||||
case "inserted":
|
||||
return { color: "#36acaa" }
|
||||
case "atrule":
|
||||
case "keyword":
|
||||
case "attr-value":
|
||||
return { color: "#0000ff" }
|
||||
case "function":
|
||||
return { color: "#b9a40a" }
|
||||
case "deleted":
|
||||
case "tag":
|
||||
return { color: "#9a050f" }
|
||||
case "selector":
|
||||
return { color: "#00009f" }
|
||||
case "important":
|
||||
return { color: "#e90", bold: true }
|
||||
case "italic":
|
||||
return { italic: true }
|
||||
case "class-name":
|
||||
case "property":
|
||||
return { color: "#2B91AF" }
|
||||
case "attr-name":
|
||||
case "regex":
|
||||
case "entity":
|
||||
return { color: "#ff0000" }
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
type IFormatPrismToken = {
|
||||
type?: string;
|
||||
content: string;
|
||||
} & IPrismKindStyle
|
||||
|
||||
export function formatPrismToken(payload: (Prism.Token | string)[]): IFormatPrismToken[] {
|
||||
const formatTokenList: IFormatPrismToken[] = []
|
||||
function format(tokenList: (Prism.Token | string)[]) {
|
||||
for (let i = 0; i < tokenList.length; i++) {
|
||||
const element = tokenList[i]
|
||||
if (typeof element === 'string') {
|
||||
formatTokenList.push({
|
||||
content: element
|
||||
})
|
||||
} else if (Array.isArray(element.content)) {
|
||||
format(element.content)
|
||||
} else {
|
||||
const { type, content } = element
|
||||
if (typeof content === 'string') {
|
||||
formatTokenList.push({
|
||||
type,
|
||||
content,
|
||||
...getPrismKindStyle(type)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
format(payload)
|
||||
return formatTokenList
|
||||
}
|
||||
Reference in New Issue
Block a user