feat: add list element

pr675
Hufe921 3 years ago committed by Hufe
parent 8202c1c67b
commit c2330a8c11

@ -152,6 +152,40 @@
</ul>
</div>
</div>
<div class="menu-item__list">
<i title="列表"></i>
<div class="options">
<ul>
<li>
<label>取消列表</label>
</li>
<li data-list-type="ol" data-list-style='decimal'>
<label>有序列表:</label>
<ol>
<li>________</li>
</ol>
</li>
<li data-list-type="ul" data-list-style='disc'>
<label>实心圆点列表:</label>
<ul style="list-style-type: disc;">
<li>________</li>
</ul>
</li>
<li data-list-type="ul" data-list-style='circle'>
<label>空心圆点列表:</label>
<ul style="list-style-type: circle;">
<li>________</li>
</ul>
</li>
<li data-list-type="ul" data-list-style='square'>
<label>空心方块列表:</label>
<ul style="list-style-type: square;">
<li>________</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="menu-divider"></div>
<div class="menu-item">

@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#3D4757" d="M7 12h7v1H7zm0-5h7v1H7zm0-5h7v1H7zM3 4V2H2V1h2v3h1v1H2V4h1z"/><path d="M2 6h3v1H2V6zm0 3h3v1H2V9z" fill="#4F4F4F"/><path fill="#3D4757" fill-rule="nonzero" d="M4.5 6L5 7l-2.5 3L2 9z"/><path d="M4 14l-1-2H2v-1h3v1H4l1 2v1H2v-1h2z" fill="#3D4757"/></g></svg>

After

Width:  |  Height:  |  Size: 398 B

@ -1,4 +1,4 @@
import { IElement, ImageDisplay, INavigateInfo, TableBorder, TitleLevel, VerticalAlign } from '../..'
import { IElement, ImageDisplay, INavigateInfo, ListStyle, ListType, TableBorder, TitleLevel, VerticalAlign } from '../..'
import { EditorMode, PageMode, PaperDirection } from '../../dataset/enum/Editor'
import { RowFlex } from '../../dataset/enum/Row'
import { IDrawImagePayload, IPainterOptions } from '../../interface/Draw'
@ -34,6 +34,7 @@ export class Command {
private static color: CommandAdapt['color']
private static highlight: CommandAdapt['highlight']
private static title: CommandAdapt['title']
private static list: CommandAdapt['list']
private static left: CommandAdapt['rowFlex']
private static center: CommandAdapt['rowFlex']
private static right: CommandAdapt['rowFlex']
@ -111,6 +112,7 @@ export class Command {
Command.color = adapt.color.bind(adapt)
Command.highlight = adapt.highlight.bind(adapt)
Command.title = adapt.title.bind(adapt)
Command.list = adapt.list.bind(adapt)
Command.left = adapt.rowFlex.bind(adapt)
Command.center = adapt.rowFlex.bind(adapt)
Command.right = adapt.rowFlex.bind(adapt)
@ -262,7 +264,7 @@ export class Command {
return Command.highlight(payload)
}
// 标题、对齐方式
// 标题、对齐方式、列表
public executeTitle(payload: TitleLevel | null) {
return Command.title(payload)
}
@ -287,6 +289,10 @@ export class Command {
return Command.rowMargin(payload)
}
public executeList(listType: ListType | null, listStyle?: ListStyle) {
return Command.list(listType, listStyle)
}
// 表格、图片上传、超链接、搜索、打印
public executeInsertTable(row: number, col: number) {
return Command.insertTable(row, col)

@ -6,6 +6,7 @@ import { ControlComponent, ImageDisplay } from '../../dataset/enum/Control'
import { EditorContext, EditorMode, PageMode, PaperDirection } from '../../dataset/enum/Editor'
import { ElementType } from '../../dataset/enum/Element'
import { ElementStyleKey } from '../../dataset/enum/ElementStyle'
import { ListStyle, ListType } from '../../dataset/enum/List'
import { RowFlex } from '../../dataset/enum/Row'
import { TableBorder } from '../../dataset/enum/table/Table'
import { TitleLevel } from '../../dataset/enum/Title'
@ -107,9 +108,9 @@ export class CommandAdapt {
return
}
if (!isCollapsed) {
elementList.splice(startIndex + 1, endIndex - startIndex)
this.draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
} else {
elementList.splice(startIndex, 1)
this.draw.spliceElementList(elementList, startIndex, 1)
}
const curIndex = isCollapsed ? startIndex - 1 : startIndex
this.range.setRange(curIndex, curIndex)
@ -386,25 +387,12 @@ export class CommandAdapt {
if (isReadonly) return
const { startIndex, endIndex } = this.range.getRange()
if (!~startIndex && !~endIndex) return
// 需要改变的元素列表
let changeElementList: IElement[] = []
const elementList = this.draw.getElementList()
if (startIndex === endIndex) {
// 选区行信息
const rangeRow = this.range.getRangeParagraph()
if (!rangeRow) return
const positionList = this.position.getPositionList()
for (let p = 0; p < positionList.length; p++) {
const position = positionList[p]
const rowArray = rangeRow.get(position.pageNo)
if (!rowArray) continue
if (rowArray.includes(position.rowNo)) {
changeElementList.push(elementList[p])
}
}
} else {
changeElementList = elementList.slice(startIndex + 1, endIndex + 1)
}
// 需要改变的元素列表
const changeElementList = startIndex === endIndex
? this.range.getRangeElementList()
: elementList.slice(startIndex + 1, endIndex + 1)
if (!changeElementList || !changeElementList.length) return
// 设置值
const titleId = getUUID()
const titleOptions = this.draw.getOptions().title
@ -432,6 +420,35 @@ export class CommandAdapt {
this.draw.render({ curIndex, isSetCursor })
}
public list(listType: ListType | null, listStyle?: ListStyle) {
const isReadonly = this.draw.isReadonly()
if (isReadonly) return
const { startIndex, endIndex } = this.range.getRange()
if (!~startIndex && !~endIndex) return
// 需要改变的元素列表
const changeElementList = this.range.getRangeElementList()
if (!changeElementList || !changeElementList.length) return
// 设置值
const listId = getUUID()
changeElementList.forEach(el => {
if (listType) {
el.listId = listId
el.listType = listType
el.listStyle = listStyle
} else {
if (el.listId) {
delete el.listId
delete el.listType
delete el.listStyle
}
}
})
// 光标定位
const isSetCursor = startIndex === endIndex
const curIndex = isSetCursor ? endIndex : startIndex
this.draw.render({ curIndex, isSetCursor })
}
public rowFlex(payload: RowFlex) {
const isReadonly = this.draw.isReadonly()
if (isReadonly) return
@ -528,9 +545,9 @@ export class CommandAdapt {
})
const curIndex = startIndex + 1
if (startIndex === endIndex) {
elementList.splice(curIndex, 0, element)
this.draw.spliceElementList(elementList, curIndex, 0, element)
} else {
elementList.splice(curIndex, endIndex - startIndex, element)
this.draw.spliceElementList(elementList, curIndex, endIndex - startIndex, element)
}
this.range.setRange(curIndex, curIndex)
this.draw.render({ curIndex, isSetCursor: false })
@ -1140,9 +1157,9 @@ export class CommandAdapt {
if (!newElementList) return
const start = startIndex + 1
if (startIndex === endIndex) {
elementList.splice(start, 0, ...newElementList)
this.draw.spliceElementList(elementList, start, 0, ...newElementList)
} else {
elementList.splice(start, endIndex - startIndex, ...newElementList)
this.draw.spliceElementList(elementList, start, endIndex - startIndex, ...newElementList)
}
const curIndex = start + newElementList.length - 1
this.range.setRange(curIndex, curIndex)
@ -1192,7 +1209,7 @@ export class CommandAdapt {
const elementList = this.draw.getElementList()
const [leftIndex, rightIndex] = hyperRange
// 删除元素
elementList.splice(leftIndex, rightIndex - leftIndex + 1)
this.draw.spliceElementList(elementList, leftIndex, rightIndex - leftIndex + 1)
this.draw.getHyperlinkParticle().clearHyperlinkPopup()
// 重置画布
const newIndex = leftIndex - 1
@ -1268,10 +1285,10 @@ export class CommandAdapt {
}
// 从行头增加分割线
if (startIndex !== 0 && elementList[startIndex].value === ZERO) {
elementList.splice(startIndex, 1, newElement)
this.draw.spliceElementList(elementList, startIndex, 1, newElement)
curIndex = startIndex - 1
} else {
elementList.splice(startIndex + 1, 0, newElement)
this.draw.spliceElementList(elementList, startIndex + 1, 0, newElement)
curIndex = startIndex
}
}
@ -1339,9 +1356,9 @@ export class CommandAdapt {
}
const curIndex = startIndex + 1
if (startIndex === endIndex) {
elementList.splice(curIndex, 0, element)
this.draw.spliceElementList(elementList, curIndex, 0, element)
} else {
elementList.splice(curIndex, endIndex - startIndex, element)
this.draw.spliceElementList(elementList, curIndex, endIndex - startIndex, element)
}
this.range.setRange(curIndex, curIndex)
this.draw.render({ curIndex })
@ -1411,7 +1428,7 @@ export class CommandAdapt {
const curIndex = index + tableDiffCount
const tableElement = tableElementList[curIndex]
if (curGroupId === match.groupId) {
tableElementList.splice(curIndex, 1)
this.draw.spliceElementList(tableElementList, curIndex, 1)
tableDiffCount--
continue
}
@ -1420,7 +1437,7 @@ export class CommandAdapt {
if (p === 0) {
tableElement.value = value
} else {
tableElementList.splice(curIndex + p, 0, {
this.draw.spliceElementList(tableElementList, curIndex + p, 0, {
...tableElement,
value
})
@ -1440,7 +1457,7 @@ export class CommandAdapt {
firstMatchIndex = m
}
if (curGroupId === match.groupId) {
elementList.splice(curIndex, 1)
this.draw.spliceElementList(elementList, curIndex, 1)
pageDiffCount--
continue
}
@ -1449,7 +1466,7 @@ export class CommandAdapt {
if (p === 0) {
element.value = value
} else {
elementList.splice(curIndex + p, 0, {
this.draw.spliceElementList(elementList, curIndex + p, 0, {
...element,
value
})

@ -52,7 +52,8 @@ import { I18n } from '../i18n/I18n'
import { ImageObserver } from '../observer/ImageObserver'
import { Zone } from '../zone/Zone'
import { Footer } from './frame/Footer'
import { INLINE_ELEMENT_TYPE } from '../../dataset/constant/Element'
import { EDITOR_ELEMENT_CONTEXT_ATTR, INLINE_ELEMENT_TYPE } from '../../dataset/constant/Element'
import { ListParticle } from './particle/ListParticle'
export class Draw {
@ -100,6 +101,7 @@ export class Draw {
private subscriptParticle: SubscriptParticle
private checkboxParticle: CheckboxParticle
private blockParticle: BlockParticle
private listParticle: ListParticle
private control: Control
private workerManager: WorkerManager
private scrollObserver: ScrollObserver
@ -164,6 +166,7 @@ export class Draw {
this.subscriptParticle = new SubscriptParticle()
this.checkboxParticle = new CheckboxParticle(this)
this.blockParticle = new BlockParticle(this)
this.listParticle = new ListParticle(this)
this.control = new Control(this)
this.scrollObserver = new ScrollObserver(this)
@ -435,6 +438,18 @@ export class Draw {
return this.footerElementList
}
public formatElementContext(anchorElement: IElement, targetElement: IElement) {
for (let i = 0; i < EDITOR_ELEMENT_CONTEXT_ATTR.length; i++) {
const attr = EDITOR_ELEMENT_CONTEXT_ATTR[i]
const value = anchorElement[attr] as never
if (value !== undefined) {
targetElement[attr] = value
} else {
delete targetElement[attr]
}
}
}
public insertElementList(payload: IElement[]) {
if (!payload.length) return
const isPartRangeInControlOutside = this.control.isPartRangeInControlOutside()
@ -455,18 +470,9 @@ export class Draw {
const isCollapsed = startIndex === endIndex
const start = startIndex + 1
if (!isCollapsed) {
elementList.splice(start, endIndex - startIndex)
}
const positionContext = this.position.getPositionContext()
for (let i = 0; i < payload.length; i++) {
const element = payload[i]
if (positionContext.isTable) {
element.tdId = positionContext.tdId
element.trId = positionContext.trId
element.tableId = positionContext.tableId
}
elementList.splice(start + i, 0, element)
this.spliceElementList(elementList, start, endIndex - startIndex)
}
this.spliceElementList(elementList, start, 0, ...payload)
curIndex = startIndex + payload.length
}
if (~curIndex) {
@ -477,6 +483,41 @@ export class Draw {
}
}
public spliceElementList(elementList: IElement[], start: number, deleteCount: number, ...items: IElement[]) {
if (deleteCount > 0) {
// 当最后元素与开始元素列表信息不一致时:清除当前列表信息
const endIndex = start + deleteCount - 1
const endElement = elementList[endIndex]
const endElementListId = endElement?.listId
if (endElementListId && elementList[start - 1]?.listId !== endElementListId) {
let startIndex = endIndex
while (startIndex < elementList.length) {
const curElement = elementList[startIndex]
if (curElement.listId !== endElementListId || curElement.value === ZERO) break
delete curElement.listId
delete curElement.listType
delete curElement.listStyle
startIndex++
}
}
}
if (items.length) {
// 格式化新增数据
const endIndex = start + deleteCount - 1
const endElement = elementList[endIndex]
const endNextElement = elementList[endIndex + 1]
const anchorElement = endElement?.value === ZERO && endNextElement && endNextElement.value !== ZERO
? endNextElement
: endElement
if (anchorElement) {
for (let i = 0; i < items.length; i++) {
this.formatElementContext(anchorElement, items[i])
}
}
}
elementList.splice(start, deleteCount, ...items)
}
public getCanvasEvent(): CanvasEvent {
return this.canvasEvent
}
@ -517,6 +558,10 @@ export class Draw {
return this.dateParticle
}
public getListParticle(): ListParticle {
return this.listParticle
}
public getControl(): Control {
return this.control
}
@ -765,6 +810,8 @@ export class Draw {
const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight()
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
// 计算列表偏移宽度
const listStyleMap = this.listParticle.computeListStyle(ctx, elementList)
const rowList: IRow[] = []
if (elementList.length) {
rowList.push({
@ -776,6 +823,9 @@ export class Draw {
rowFlex: elementList?.[1]?.rowFlex
})
}
// 列表位置
let listId: string | undefined
let listIndex = 0
for (let i = 0; i < elementList.length; i++) {
const curRow: IRow = rowList[rowList.length - 1]
const element = elementList[i]
@ -930,7 +980,7 @@ export class Draw {
const cloneElement = deepClone(element)
cloneElement.trList = cloneTrList
cloneElement.id = getUUID()
elementList.splice(i + 1, 0, cloneElement)
this.spliceElementList(elementList, i + 1, 0, cloneElement)
// 换页的是当前行则改变上下文
const positionContext = this.position.getPositionContext()
if (positionContext.isTable && positionContext.trIndex === deleteStart) {
@ -1007,7 +1057,17 @@ export class Draw {
const preElement = elementList[i - 1]
const nextElement = elementList[i + 1]
// 累计行宽 + 当前元素宽度 + 后面标点符号宽度
const curRowWidth = curRow.width + metrics.width + this.textParticle.measurePunctuationWidth(ctx, nextElement)
let curRowWidth = curRow.width + metrics.width + this.textParticle.measurePunctuationWidth(ctx, nextElement)
// 列表信息
if (element.listId) {
curRowWidth += listStyleMap.get(element.listId) || 0
if (element.listId !== listId) {
listIndex = 0
} else if (element.value === ZERO) {
listIndex++
}
}
listId = element.listId
if (
element.type === ElementType.TABLE
|| preElement?.type === ElementType.TABLE
@ -1017,9 +1077,14 @@ export class Draw {
|| element.imgDisplay === ImageDisplay.INLINE
|| curRowWidth > innerWidth
|| (i !== 0 && element.value === ZERO)
|| (preElement?.listId !== element.listId)
) {
// 减小行元素前第一行空行行高
if (curRow.startIndex === 0 && curRow.elementList.length === 1 && INLINE_ELEMENT_TYPE.includes(element.type!)) {
if (
curRow.startIndex === 0 &&
curRow.elementList.length === 1 &&
(INLINE_ELEMENT_TYPE.includes(element.type!) || element.listId)
) {
curRow.height = defaultBasicRowMarginHeight
}
// 两端对齐
@ -1031,7 +1096,7 @@ export class Draw {
}
curRow.width = innerWidth
}
rowList.push({
const row: IRow = {
width: metrics.width,
height,
startIndex: i,
@ -1039,7 +1104,13 @@ export class Draw {
ascent,
rowFlex: elementList[i + 1]?.rowFlex,
isPageBreak: element.type === ElementType.PAGE_BREAK
})
}
if (element.listId) {
row.isList = true
row.offsetX = listStyleMap.get(element.listId!)
row.listIndex = listIndex
}
rowList.push(row)
} else {
curRow.width += metrics.width
if (curRow.height < height) {
@ -1255,6 +1326,10 @@ export class Draw {
}
}
}
// 绘制列表样式
if (curRow.isList) {
this.listParticle.drawListStyle(ctx, curRow, positionList[curRow.startIndex])
}
// 绘制富文本及文字
this._drawRichText(ctx)
// 绘制选区

@ -32,6 +32,10 @@ export class Control {
this.activeControl = null
}
public getDraw(): Draw {
return this.draw
}
// 判断选区部分在控件边界外
public isPartRangeInControlOutside(): boolean {
const { startIndex, endIndex } = this.getRange()
@ -256,7 +260,7 @@ export class Control {
if (!~leftIndex && !~rightIndex) return startIndex
leftIndex = ~leftIndex ? leftIndex : 0
// 删除元素
elementList.splice(leftIndex + 1, rightIndex - leftIndex)
this.draw.spliceElementList(elementList, leftIndex + 1, rightIndex - leftIndex)
return leftIndex
}
@ -273,7 +277,7 @@ export class Control {
const curElement = elementList[index]
if (curElement.controlId !== startElement.controlId) break
if (curElement.controlComponent === ControlComponent.PLACEHOLDER) {
elementList.splice(index, 1)
this.draw.spliceElementList(elementList, index, 1)
} else {
index++
}
@ -289,7 +293,7 @@ export class Control {
const placeholderStrList = splitText(control.placeholder)
for (let p = 0; p < placeholderStrList.length; p++) {
const value = placeholderStrList[p]
elementList.splice(startIndex + p + 1, 0, {
this.draw.spliceElementList(elementList, startIndex + p + 1, 0, {
value,
controlId: startElement.controlId,
type: ElementType.CONTROL,

@ -164,7 +164,8 @@ export class SelectControl implements IControlInstance {
}
if (!~leftIndex || !~rightIndex) return -1
// 删除元素
elementList.splice(leftIndex + 1, rightIndex - leftIndex)
const draw = this.control.getDraw()
draw.spliceElementList(elementList, leftIndex + 1, rightIndex - leftIndex)
// 增加占位符
this.control.addPlaceholder(preIndex)
this.element.control!.code = null
@ -186,8 +187,9 @@ export class SelectControl implements IControlInstance {
const startElement = elementList[startIndex]
const start = startIndex + 1
const data = splitText(valueSet.value)
const draw = this.control.getDraw()
for (let i = 0; i < data.length; i++) {
elementList.splice(start + i, 0, {
draw.spliceElementList(elementList, start + i, 0, {
...startElement,
value: data[i],
controlComponent: ControlComponent.VALUE

@ -62,9 +62,10 @@ export class TextControl implements IControlInstance {
// 收缩边界到Value内
this.control.shrinkBoundary()
const { startIndex, endIndex } = range
const draw = this.control.getDraw()
// 移除选区元素
if (startIndex !== endIndex) {
elementList.splice(startIndex + 1, endIndex - startIndex)
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
} else {
// 移除空白占位符
this.control.removePlaceholder(startIndex)
@ -73,7 +74,7 @@ export class TextControl implements IControlInstance {
const startElement = elementList[startIndex]
const start = range.startIndex + 1
for (let i = 0; i < data.length; i++) {
elementList.splice(start + i, 0, {
draw.spliceElementList(elementList, start + i, 0, {
...startElement,
...data[i],
controlComponent: ControlComponent.VALUE
@ -90,11 +91,12 @@ export class TextControl implements IControlInstance {
const { startIndex, endIndex } = range
const startElement = elementList[startIndex]
const endElement = elementList[endIndex]
const draw = this.control.getDraw()
// backspace
if (evt.key === KeyMap.Backspace) {
// 移除选区元素
if (startIndex !== endIndex) {
elementList.splice(startIndex + 1, endIndex - startIndex)
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
const value = this.getValue()
if (!value.length) {
this.control.addPlaceholder(startIndex)
@ -110,7 +112,7 @@ export class TextControl implements IControlInstance {
return this.control.removeControl(startIndex)
} else {
// 文本
elementList.splice(startIndex, 1)
draw.spliceElementList(elementList, startIndex, 1)
const value = this.getValue()
if (!value.length) {
this.control.addPlaceholder(startIndex - 1)
@ -121,7 +123,7 @@ export class TextControl implements IControlInstance {
} else if (evt.key === KeyMap.Delete) {
// 移除选区元素
if (startIndex !== endIndex) {
elementList.splice(startIndex + 1, endIndex - startIndex)
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
const value = this.getValue()
if (!value.length) {
this.control.addPlaceholder(startIndex)
@ -138,7 +140,7 @@ export class TextControl implements IControlInstance {
return this.control.removeControl(startIndex)
} else {
// 文本
elementList.splice(startIndex + 1, 1)
draw.spliceElementList(elementList, startIndex + 1, 1)
const value = this.getValue()
if (!value.length) {
this.control.addPlaceholder(startIndex)
@ -156,8 +158,9 @@ export class TextControl implements IControlInstance {
if (startIndex === endIndex) {
return startIndex
}
const draw = this.control.getDraw()
const elementList = this.control.getElementList()
elementList.splice(startIndex + 1, endIndex - startIndex)
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
const value = this.getValue()
if (!value.length) {
this.control.addPlaceholder(startIndex)

@ -0,0 +1,97 @@
import { ZERO } from '../../../dataset/constant/Common'
import { ulStyleMapping } from '../../../dataset/constant/List'
import { KeyMap } from '../../../dataset/enum/KeyMap'
import { ListStyle, ListType, UlStyle } from '../../../dataset/enum/List'
import { DeepRequired } from '../../../interface/Common'
import { IEditorOption } from '../../../interface/Editor'
import { IElement, IElementPosition } from '../../../interface/Element'
import { IRow } from '../../../interface/Row'
import { Draw } from '../Draw'
export class ListParticle {
private options: DeepRequired<IEditorOption>
// 非递增样式直接返回默认值
private readonly UN_COUNT_STYLE_WIDTH = 20
private readonly MEASURE_BASE_TEXT = '0'
private readonly LIST_GAP = 10
constructor(draw: Draw) {
this.options = draw.getOptions()
}
public computeListStyle(ctx: CanvasRenderingContext2D, elementList: IElement[]): Map<string, number> {
const listStyleMap = new Map<string, number>()
let start = 0
let curListId = elementList[start].listId
let curElementList: IElement[] = []
const elementLength = elementList.length
while (start < elementLength) {
const curElement = elementList[start]
if (curListId && curListId === curElement.listId) {
curElementList.push(curElement)
} else {
if (curElement.listId && curElement.listId !== curListId) {
// 列表结束
if (curElementList.length) {
const width = this.getListStyleWidth(ctx, curElementList)
listStyleMap.set(curListId!, width)
}
curListId = curElement.listId
curElementList = curListId ? [curElement] : []
}
}
start++
}
if (curElementList.length) {
const width = this.getListStyleWidth(ctx, curElementList)
listStyleMap.set(curListId!, width)
}
return listStyleMap
}
public getListStyleWidth(ctx: CanvasRenderingContext2D, listElementList: IElement[]): number {
const startElement = listElementList[0]
// 非递增样式返回固定值
if (startElement.listStyle && startElement.listStyle !== ListStyle.DECIMAL) {
return this.UN_COUNT_STYLE_WIDTH
}
// 计算列表数量
const count = listElementList.reduce((pre, cur) => {
if (cur.value === ZERO) {
pre += 1
}
return pre
}, 0)
if (!count) return 0
// 以递增样式最大宽度为准
const text = `${this.MEASURE_BASE_TEXT.repeat(String(count).length)}${KeyMap.PERIOD}`
const textMetrics = ctx.measureText(text)
return Math.ceil(textMetrics.width + this.LIST_GAP)
}
public drawListStyle(ctx: CanvasRenderingContext2D, row: IRow, position: IElementPosition) {
const { elementList, offsetX, listIndex, ascent } = row
const startElement = elementList[0]
if (startElement.value !== ZERO) return
let text = ''
if (startElement.listType === ListType.UL) {
if (startElement.listStyle) {
text = ulStyleMapping[<UlStyle><unknown>startElement.listStyle]
}
} else {
text = `${listIndex! + 1}${KeyMap.PERIOD}`
}
if (!text) return
const { coordinate: { leftTop: [startX, startY] } } = position
const x = startX - offsetX!
const y = startY + ascent
const { defaultFont, defaultSize, scale } = this.options
ctx.save()
ctx.font = `${defaultSize * scale}px ${defaultFont}`
ctx.fillText(text, x, y)
ctx.restore()
}
}

@ -50,7 +50,7 @@ export class DateParticle {
const elementList = this.draw.getElementList()
const startElement = elementList[leftIndex + 1]
// 删除旧时间
elementList.splice(leftIndex + 1, rightIndex - leftIndex)
this.draw.spliceElementList(elementList, leftIndex + 1, rightIndex - leftIndex)
this.range.setRange(leftIndex, leftIndex)
// 插入新时间
this.draw.insertElementList([{

@ -41,7 +41,7 @@ export function cut(host: CanvasEvent) {
if (activeControl) {
curIndex = control.cut()
} else {
elementList.splice(start + 1, end - start)
draw.spliceElementList(elementList, start + 1, end - start)
curIndex = start
}
rangeManager.setRange(curIndex, curIndex)

@ -31,13 +31,7 @@ export function input(data: string, host: CanvasEvent) {
const text = data.replaceAll(`\n`, ZERO)
const rangeManager = draw.getRange()
const { startIndex, endIndex } = rangeManager.getRange()
// 表格需要上下文信息
const positionContext = position.getPositionContext()
let restArg = {}
if (positionContext.isTable) {
const { tdId, trId, tableId } = positionContext
restArg = { tdId, trId, tableId }
}
// 格式化元素
const elementList = draw.getElementList()
const endElement = elementList[endIndex]
const endNextElement = elementList[endIndex + 1]
@ -46,8 +40,7 @@ export function input(data: string, host: CanvasEvent) {
: endElement
const inputData: IElement[] = splitText(text).map(value => {
const newElement: IElement = {
value,
...restArg
value
}
const nextElement = elementList[endIndex + 1]
if (
@ -77,12 +70,9 @@ export function input(data: string, host: CanvasEvent) {
} else {
const start = startIndex + 1
if (startIndex !== endIndex) {
elementList.splice(start, endIndex - startIndex)
}
// 禁止直接使用解构存在性能问题
for (let i = 0; i < inputData.length; i++) {
elementList.splice(start + i, 0, inputData[i])
draw.spliceElementList(elementList, start, endIndex - startIndex)
}
draw.spliceElementList(elementList, start, 0, ...inputData)
curIndex = startIndex + inputData.length
}
if (~curIndex) {

@ -46,9 +46,9 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
})
}
if (!isCollapsed) {
elementList.splice(startIndex + 1, endIndex - startIndex)
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
} else {
elementList.splice(index, 1)
draw.spliceElementList(elementList, index, 1)
}
curIndex = isCollapsed ? index - 1 : startIndex
}
@ -63,9 +63,9 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
curIndex = control.removeControl(endIndex + 1)
} else {
if (!isCollapsed) {
elementList.splice(startIndex + 1, endIndex - startIndex)
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
} else {
elementList.splice(index + 1, 1)
draw.spliceElementList(elementList, index + 1, 1)
}
curIndex = isCollapsed ? index : startIndex
}
@ -73,25 +73,17 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) {
draw.render({ curIndex })
} else if (evt.key === KeyMap.Enter) {
if (isReadonly || isPartRangeInControlOutside) return
// 表格需要上下文信息
const positionContext = position.getPositionContext()
let restArg = {}
if (positionContext.isTable) {
const { tdId, trId, tableId } = positionContext
restArg = { tdId, trId, tableId }
}
const enterText: IElement = {
value: ZERO,
...restArg
value: ZERO
}
let curIndex: number
if (activeControl && !control.isRangInPostfix()) {
curIndex = control.setValue([enterText])
} else {
if (isCollapsed) {
elementList.splice(index + 1, 0, enterText)
draw.spliceElementList(elementList, index + 1, 0, enterText)
} else {
elementList.splice(startIndex + 1, endIndex - startIndex, enterText)
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex, enterText)
}
curIndex = index + 1
}

@ -82,17 +82,10 @@ export function mouseup(evt: MouseEvent, host: CanvasEvent) {
// 格式化元素
const editorOptions = draw.getOptions()
const elementList = draw.getElementList()
const anchorElement = elementList[range.startIndex]
let restArg = {}
if (anchorElement.tableId) {
const { tdId, trId, tableId } = anchorElement
restArg = { tdId, trId, tableId }
}
const replaceElementList = dragElementList.map(el => {
if (!el.type || el.type === ElementType.TEXT || el.control?.type === ControlType.TEXT) {
const newElement: IElement = {
value: el.value,
...restArg
value: el.value
}
EDITOR_ELEMENT_STYLE_ATTR.forEach(attr => {
const value = el[attr] as never
@ -123,7 +116,7 @@ export function mouseup(evt: MouseEvent, host: CanvasEvent) {
rangeEnd = activeControl.setValue(replaceElementList)
rangeStart = rangeEnd - replaceLength
} else {
elementList.splice(rangeStart + 1, 0, ...replaceElementList)
draw.spliceElementList(elementList, rangeStart + 1, 0, ...replaceElementList)
}
if (!~rangeEnd) {
draw.render({
@ -146,7 +139,7 @@ export function mouseup(evt: MouseEvent, host: CanvasEvent) {
})
control.getActiveControl()?.cut()
} else {
cacheElementList.splice(cacheRangeStartIndex + 1, cacheRangeEndIndex - cacheRangeStartIndex)
draw.spliceElementList(cacheElementList, cacheRangeStartIndex + 1, cacheRangeEndIndex - cacheRangeStartIndex)
}
// 重设上下文
const startElement = elementList[range.startIndex]

@ -81,6 +81,10 @@ export class Position {
} else if (curRow.rowFlex === RowFlex.RIGHT) {
x += innerWidth - curRow.width
}
// 列表向右移动-留出列表样式位置
if (curRow.isList) {
x += curRow.offsetX || 0
}
// 当前td所在位置
const tablePreX = x
const tablePreY = y

@ -82,10 +82,7 @@ export class RangeManager {
// 向上查找
let start = startIndex
while (start > 0) {
if (
positionList[start].value === ZERO ||
(elementList[start].titleId !== elementList[start - 1]?.titleId)
) break
if (elementList[start].titleId !== elementList[start - 1]?.titleId) break
const { pageNo, rowNo } = positionList[start]
let rowArray = rangeRow.get(pageNo)
if (!rowArray) {
@ -95,15 +92,13 @@ export class RangeManager {
if (!rowArray.includes(rowNo)) {
rowArray.unshift(rowNo)
}
if (positionList[start]?.value === ZERO) break
start--
}
// 向下查找
let end = endIndex
let end = startIndex + 1
while (end < positionList.length) {
if (
positionList[end].value === ZERO ||
elementList[end].titleId !== elementList[end + 1]?.titleId
) break
if (elementList[end].titleId !== elementList[end + 1]?.titleId) break
const { pageNo, rowNo } = positionList[end]
let rowArray = rangeRow.get(pageNo)
if (!rowArray) {
@ -113,11 +108,34 @@ export class RangeManager {
if (!rowArray.includes(rowNo)) {
rowArray.push(rowNo)
}
if (positionList[end].value === ZERO) break
end++
}
return rangeRow
}
// 获取选区元素列表
public getRangeElementList(): IElement[] | null {
const { startIndex, endIndex } = this.range
if (!~startIndex && !~endIndex) return null
// 需要改变的元素列表
const rangeElementList: IElement[] = []
// 选区行信息
const rangeRow = this.getRangeParagraph()
if (!rangeRow) return null
const elementList = this.draw.getElementList()
const positionList = this.position.getPositionList()
for (let p = 0; p < positionList.length; p++) {
const position = positionList[p]
const rowArray = rangeRow.get(position.pageNo)
if (!rowArray) continue
if (rowArray.includes(position.rowNo)) {
rangeElementList.push(elementList[p])
}
}
return rangeElementList
}
public getIsPointInRange(x: number, y: number): boolean {
const { startIndex, endIndex } = this.range
const positionList = this.position.getPositionList()
@ -189,7 +207,7 @@ export class RangeManager {
const elementList = this.draw.getElementList()
const endElement = elementList[index]
const endNextElement = elementList[index + 1]
curElement = endElement.value === ZERO && endNextElement
curElement = endElement.value === ZERO && endNextElement && endNextElement.value !== ZERO
? endNextElement
: endElement
}
@ -211,6 +229,8 @@ export class RangeManager {
const rowMargin = curElement.rowMargin || this.options.defaultRowMargin
const dashArray = curElement.dashArray || []
const level = curElement.level || null
const listType = curElement.listType || null
const listStyle = curElement.listStyle || null
// 菜单
const painter = !!this.draw.getPainterStyle()
const undo = this.historyManager.isCanUndo()
@ -231,7 +251,9 @@ export class RangeManager {
rowFlex,
rowMargin,
dashArray,
level
level,
listType,
listStyle
})
}
@ -259,7 +281,9 @@ export class RangeManager {
rowFlex: null,
rowMargin,
dashArray: [],
level: null
level: null,
listType: null,
listStyle: null
})
}

@ -55,7 +55,18 @@ export const EDITOR_ELEMENT_ZIP_ATTR: Array<keyof IElement> = [
'checkbox',
'dateFormat',
'block',
'level'
'level',
'listType',
'listStyle'
]
export const EDITOR_ELEMENT_CONTEXT_ATTR: Array<keyof IElement> = [
'tdId',
'trId',
'tableId',
'listId',
'listType',
'listStyle'
]
export const TEXTLIKE_ELEMENT_TYPE: ElementType[] = [

@ -0,0 +1,7 @@
import { UlStyle } from '../enum/List'
export const ulStyleMapping: Record<UlStyle, string> = {
[UlStyle.DISC]: '•',
[UlStyle.CIRCLE]: '◦',
[UlStyle.SQUARE]: '▫︎'
}

@ -13,5 +13,6 @@ export enum ElementType {
TAB = 'tab',
DATE = 'date',
BLOCK = 'block',
TITLE = 'title'
TITLE = 'title',
LIST = 'list'
}

@ -0,0 +1,21 @@
export enum ListType {
UL = 'ul',
OL = 'ol'
}
export enum UlStyle {
DISC = 'disc', // 实心圆点
CIRCLE = 'circle', // 空心圆点
SQUARE = 'square', // 实心方块
}
export enum OlStyle {
DECIMAL = 'decimal' // 阿拉伯数字
}
export enum ListStyle {
DISC = UlStyle.DISC,
CIRCLE = UlStyle.CIRCLE,
SQUARE = UlStyle.SQUARE,
DECIMAL = OlStyle.DECIMAL
}

@ -41,6 +41,7 @@ import { MaxHeightRatio, NumberType } from './dataset/enum/Common'
import { ITitleOption } from './interface/Title'
import { defaultTitleOption } from './dataset/constant/Title'
import { TitleLevel } from './dataset/enum/Title'
import { ListStyle, ListType } from './dataset/enum/List'
export default class Editor {
@ -199,7 +200,9 @@ export {
TableBorder,
MaxHeightRatio,
NumberType,
TitleLevel
TitleLevel,
ListType,
ListStyle
}
// 对外类型

@ -1,5 +1,6 @@
import { ControlComponent, ImageDisplay } from '../dataset/enum/Control'
import { ElementType } from '../dataset/enum/Element'
import { ListStyle, ListType } from '../dataset/enum/List'
import { RowFlex } from '../dataset/enum/Row'
import { TitleLevel } from '../dataset/enum/Title'
import { TableBorder } from '../dataset/enum/table/Table'
@ -37,6 +38,13 @@ export interface ITitleElement {
titleId?: string;
}
export interface IListElement {
valueList?: IElement[];
listType?: ListType;
listStyle?: ListStyle;
listId?: string;
}
export interface ITableAttr {
colgroup?: IColgroup[];
trList?: ITr[];
@ -105,6 +113,7 @@ export type IElement = IElementBasic
& IImageElement
& IBlockElement
& ITitleElement
& IListElement
export interface IElementMetrics {
width: number;

@ -1,4 +1,4 @@
import { EditorZone, ElementType, PageMode, TitleLevel } from '..'
import { EditorZone, ElementType, ListStyle, ListType, PageMode, TitleLevel } from '..'
import { RowFlex } from '../dataset/enum/Row'
import { IControl } from './Control'
import { IEditorResult } from './Editor'
@ -20,6 +20,8 @@ export interface IRangeStyle {
rowMargin: number;
dashArray: number[];
level: TitleLevel | null;
listType: ListType | null;
listStyle: ListStyle | null;
}
export type IRangeStyleChange = (payload: IRangeStyle) => void

@ -13,5 +13,8 @@ export interface IRow {
rowFlex?: RowFlex;
startIndex: number;
isPageBreak?: boolean;
isList?: boolean;
listIndex?: number;
offsetX?: number;
elementList: IRowElement[];
}

@ -137,6 +137,27 @@ export function formatElementList(elementList: IElement[], options: IFormatEleme
}
}
i--
} else if (el.type === ElementType.LIST) {
// 移除父节点
elementList.splice(i, 1)
// 格式化元素
const valueList = el.valueList || []
formatElementList(valueList, {
...options
})
// 追加节点
if (valueList.length) {
const listId = getUUID()
for (let v = 0; v < valueList.length; v++) {
const value = valueList[v]
value.listId = listId
value.listType = el.listType
value.listStyle = el.listStyle
elementList.splice(i, 0, value)
i++
}
}
i--
} else if (el.type === ElementType.CONTROL) {
const { prefix, postfix, value, placeholder, code, type, valueSets } = el.control!
const controlId = getUUID()
@ -422,6 +443,32 @@ export function zipElementList(payload: IElement[]): IElement[] {
}
titleElement.valueList = zipElementList(valueList)
element = titleElement
} else if (element.listId && element.listType) {
// 列表处理
const listId = element.listId
const listType = element.listType
const listStyle = element.listStyle
const listElement: IElement = {
type: ElementType.LIST,
value: '',
listId,
listType,
listStyle
}
const valueList: IElement[] = []
while (e < elementList.length) {
const listE = elementList[e]
if (listId !== listE.listId) {
e--
break
}
delete listE.listType
delete listE.listStyle
valueList.push(listE)
e++
}
listElement.valueList = zipElementList(valueList)
element = listElement
} else if (element.type === ElementType.CONTROL) {
// 控件处理
const controlId = element.controlId

@ -1,7 +1,7 @@
import { data, options } from './mock'
import './style.css'
import prism from 'prismjs'
import Editor, { BlockType, Command, ControlType, EditorMode, ElementType, IBlock, IElement, KeyMap, PageMode, PaperDirection, RowFlex, TitleLevel } from './editor'
import Editor, { BlockType, Command, ControlType, EditorMode, ElementType, IBlock, IElement, KeyMap, ListStyle, ListType, PageMode, PaperDirection, RowFlex, TitleLevel } from './editor'
import { Dialog } from './components/dialog/Dialog'
import { formatPrismToken } from './utils/prism'
import { Signature } from './components/signature/Signature'
@ -227,6 +227,19 @@ window.onload = function () {
instance.command.executeRowMargin(Number(li.dataset.rowmargin!))
}
const listDom = document.querySelector<HTMLDivElement>('.menu-item__list')!
const listOptionDom = listDom.querySelector<HTMLDivElement>('.options')!
listDom.onclick = function () {
console.log('list')
listOptionDom.classList.toggle('visible')
}
listOptionDom.onclick = function (evt) {
const li = evt.target as HTMLLIElement
const listType = <ListType>li.dataset.listType || null
const listStyle = <ListStyle><unknown>li.dataset.listStyle
instance.command.executeList(listType, listStyle)
}
// 4. | 表格 | 图片 | 超链接 | 分割线 | 水印 | 代码块 | 分隔符 | 控件 | 复选框 | LaTeX | 日期选择器
const tableDom = document.querySelector<HTMLDivElement>('.menu-item__table')!
const tablePanelContainer = document.querySelector<HTMLDivElement>('.menu-item__table__collapse')!
@ -1080,6 +1093,21 @@ window.onload = function () {
titleSelectDom.innerText = '正文'
titleOptionDom.querySelector('li:first-child')!.classList.add('active')
}
// 列表
listOptionDom.querySelectorAll<HTMLLIElement>('li').forEach(li => li.classList.remove('active'))
if (payload.listType) {
const listType = payload.listType
const listStyle = payload.listType === ListType.OL ? ListStyle.DECIMAL : payload.listType
const curListDom = listOptionDom
.querySelector<HTMLLIElement>(`[data-list-type='${listType}'][data-list-style='${listStyle}']`)
if (curListDom) {
curListDom.classList.add('active')
listDom.classList.add('active')
}
} else {
listDom.classList.remove('active')
}
}
instance.listener.visiblePageNoListChange = function (payload) {

@ -1,6 +1,6 @@
import { ControlType, ElementType, IEditorOption, IElement, TitleLevel } from './editor'
import { ControlType, ElementType, IEditorOption, IElement, ListType, TitleLevel } from './editor'
const text = `主诉:\n发热三天咳嗽五天。\n现病史\n患者于三天前无明显诱因感冒后发现面部水肿无皮疹尿量减少出现乏力在外治疗无好转现来我院就诊。\n既往史\n有糖尿病10年有高血压2年有传染性疾病1年。报告其他既往疾病。\n流行病史\n否认14天内接触过确诊患者、疑似患者、无症状感染者及其密切接触者否认14天内去过以下场所水产、肉类批发市场农贸市场集市大型超市夜市否认14天内与以下场所工作人员密切接触水产、肉类批发市场农贸市场集市大型超市否认14天内周围如家庭、办公室有2例以上聚集性发病否认14天内接触过有发热或呼吸道症状的人员否认14天内自身有发热或呼吸道症状否认14天内接触过纳入隔离观察的人员及其他可能与新冠肺炎关联的情形陪同家属无以上情况。\n体格检查\nT39.5℃P80bpmR20次/分BP120/80mmHg\n辅助检查\n2020年6月10日普放血细胞比容36.50%偏低4050单核细胞绝对值0.75*10/L偏高参考值0.10.6\n门诊诊断\n1.高血压\n2.糖尿病\n3.病毒性感冒\n4.过敏性鼻炎\n5.过敏性鼻息肉\n处置治疗:\n1.超声引导下甲状腺细针穿刺术;\n2.乙型肝炎表面抗体测定;\n3.膜式病变细胞采集术、后颈皮下肤层;\n电子签名:【】\n其他记录`
const text = `主诉:\n发热三天咳嗽五天。\n现病史\n患者于三天前无明显诱因感冒后发现面部水肿无皮疹尿量减少出现乏力在外治疗无好转现来我院就诊。\n既往史\n有糖尿病10年有高血压2年有传染性疾病1年。报告其他既往疾病。\n流行病史\n否认14天内接触过确诊患者、疑似患者、无症状感染者及其密切接触者否认14天内去过以下场所水产、肉类批发市场农贸市场集市大型超市夜市否认14天内与以下场所工作人员密切接触水产、肉类批发市场农贸市场集市大型超市否认14天内周围如家庭、办公室有2例以上聚集性发病否认14天内接触过有发热或呼吸道症状的人员否认14天内自身有发热或呼吸道症状否认14天内接触过纳入隔离观察的人员及其他可能与新冠肺炎关联的情形陪同家属无以上情况。\n体格检查\nT39.5℃P80bpmR20次/分BP120/80mmHg\n辅助检查\n2020年6月10日普放血细胞比容36.50%偏低4050单核细胞绝对值0.75*10/L偏高参考值0.10.6\n门诊诊断处置治疗:电子签名:【】\n其他记录`
// 模拟标题
const titleText = ['主诉:', '现病史:', '既往史:', '流行病史:', '体格检查:', '辅助检查:', '门诊诊断:', '处置治疗:', '电子签名:', '其他记录:']
@ -135,8 +135,27 @@ elementList.splice(429, 0, {
type: ElementType.SUPERSCRIPT
})
// 模拟列表
elementList.splice(450, 0, {
value: '',
type: ElementType.LIST,
listType: ListType.OL,
valueList: [{
value: '高血压\n糖尿病\n病毒性感冒\n过敏性鼻炎\n过敏性鼻息肉'
}]
})
elementList.splice(452, 0, {
value: '',
type: ElementType.LIST,
listType: ListType.OL,
valueList: [{
value: '超声引导下甲状腺细针穿刺术;\n乙型肝炎表面抗体测定\n膜式病变细胞采集术、后颈皮下肤层'
}]
})
// 模拟图片
elementList.splice(543, 0, {
elementList.splice(455, 0, {
value: `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFkAAAAgCAYAAAB5JtSmAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAQ0SURBVGhD7dhrUSNBFAVgvKACEVjAAhJQgAIUYAABGEAABvgfAdn6UnWou01PppOZhIXNj1P9vo9zH5PK1Waz2V5wWlxIPgMuJJ8Bi0h+fn7eXl9fb29ubrYPDw/dO/8DHh8fu/vB4kym4Orqaofb29vund8OSSbhemewSrugBMnG3vlvw9vb265yn56edmtz/t/f33+5C8MkixQSZSsl9UzLOHUmcwTYAN/Rpl5eXnY+pnIB0Xd3d7s5m3rvDsrkCGszNiQ7r/tr4v39fSc/uipOqRcqufTHBiO78GGdzG5xcLtIFmVde7L9NsvXRo9s84+Pj+79pUAwn5GcD1wIz5r+fYGeJdnjGiF9hwL7iWAcfX19/evtKVHJXrtN8Rf4A3TVczqhrut5i1mSZQgnIriSWtdzP2N+EvIhi3/GWqHWtWXuy2IYbheiKarJZIZknkxyrryc2Utrgal+9S8iScUXIx/3kcxfe/jotcuDezLFlIbARDrzHpytXdKnQr4xyc74Vu9YV5Ih2Q/tT7mDSEYw5ZU4wu3nJx64k/1z9umlUG0hah/JSbC6Jzi5exDJWoTHERoBxu8uf/pT1j3HDkUIJitjbRfRA/iwVzlgy1RCfSF5ili9xj7BUWKs9wJZ3MpditYu+lsc+/PRx53cVF9Pdg/syE9Hb6cS75PkmhUEUFofmTvLGEXKimHueJP9Y3swWQwGLUiA9xEbHKuvgs4pPe1+1myTAKlw81buJ8kigjAXKauXPLQPhEYgJSEYsgdTUR0BmTVgc6C359wcvKGnBrGO8dO5VlD1ZZ519nrBHvrwKVMCas9hgL0YUI2wV98fC4FqCWizzXyqF44A0ZKLHkilgvPs1zbiTuZIdZ414KvqGCKZYx4zple+MSrrJVncAyL02/TOqncJwVMglx5zI4QDZ5WPvBGEcNP+7TlEcqJIAQFGsIdQjmZt7MlYA5yiI3pOQTCQXUm2TuVmXgmewxDJQDgl6deJJoU5y7p9uwZagmu1mCvbNoOOBfkhOf6lRZjzPb8qRjBMMiUhM9GNMZQq5/oRXBP7Mlj/i12A7EMIaJGqDcl8I79+/N1xTvdINQ2TDAQSvI9Md479vdqCHKSFQKAfEmgBqCTDkjaSgOZXQkg2jy1ti0xApnBQJo/0obQRipeQXbN3CmxKGQch5xgki4Efghl/kFqzPD//2DnXIodIRpaoETaXxcmwGNO7N4I2Oyuc6b+xK/tL9IH3kY/E+r1JdST4yM+7VUiuJbuPZHBeHZcNvXtziMMV9mRuvUOX8Vg9IFjRx9dUYM3s2oJyNx9ahFfSWwyRHKHG3nmL2q/mojyFVAWnEdi2Hg7OBXwUCCKr1QEtoe0+/9jI3xqIiuF2QRD0zqcwpfQnge9TVSI4tWrNe79shj98F0xDC0N4bTUVF5LPgAvJJ8dm+wcP2iJuZNdC5QAAAABJRU5ErkJggg==`,
width: 89,
height: 32,

@ -304,6 +304,26 @@ ul {
background-image: url('./assets/images/row-margin.svg');
}
.menu-item__list {
position: relative;
}
.menu-item__list i {
background-image: url('./assets/images/list.svg');
}
.menu-item__list .options {
width: 110px;
}
.menu-item__list .options>ul>li * {
pointer-events: none;
}
.menu-item__list .options>ul>li li {
margin-left: 18px;
}
.menu-item__image i {
background-image: url('./assets/images/image.svg');
}

Loading…
Cancel
Save