diff --git a/index.html b/index.html
index 9fafc12..3dd6f39 100644
--- a/index.html
+++ b/index.html
@@ -185,6 +185,12 @@
________
+
+
+
+
diff --git a/src/editor/core/draw/particle/ListParticle.ts b/src/editor/core/draw/particle/ListParticle.ts
index 09418a5..1405683 100644
--- a/src/editor/core/draw/particle/ListParticle.ts
+++ b/src/editor/core/draw/particle/ListParticle.ts
@@ -6,7 +6,7 @@ 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 { IRow, IRowElement } from '../../../interface/Row'
import { getUUID } from '../../../utils'
import { RangeManager } from '../../range/RangeManager'
import { Draw } from '../Draw'
@@ -174,15 +174,7 @@ export class ListParticle {
if (element?.type !== ElementType.TAB) break
tabWidth += defaultTabWidth * scale
}
- let text = ''
- if (startElement.listType === ListType.UL) {
- text =
- ulStyleMapping[(startElement.listStyle)] ||
- ulStyleMapping[UlStyle.DISC]
- } else {
- text = `${listIndex! + 1}${KeyMap.PERIOD}`
- }
- if (!text) return
+ // 列表样式渲染
const {
coordinate: {
leftTop: [startX, startY]
@@ -190,9 +182,37 @@ export class ListParticle {
} = position
const x = startX - offsetX! + tabWidth
const y = startY + ascent
- ctx.save()
- ctx.font = `${defaultSize * scale}px ${defaultFont}`
- ctx.fillText(text, x, y)
- ctx.restore()
+ // 复选框样式特殊处理
+ if (startElement.listStyle === ListStyle.CHECKBOX) {
+ const { width, height, gap } = this.options.checkbox
+ const checkboxRowElement: IRowElement = {
+ ...startElement,
+ checkbox: {
+ value: !!startElement.checkbox?.value
+ },
+ metrics: {
+ ...startElement.metrics,
+ width: (width + gap * 2) * scale,
+ height: height * scale
+ }
+ }
+ this.draw
+ .getCheckboxParticle()
+ .render(ctx, checkboxRowElement, x - gap * scale, y)
+ } else {
+ let text = ''
+ if (startElement.listType === ListType.UL) {
+ text =
+ ulStyleMapping[(startElement.listStyle)] ||
+ ulStyleMapping[UlStyle.DISC]
+ } else {
+ text = `${listIndex! + 1}${KeyMap.PERIOD}`
+ }
+ if (!text) return
+ ctx.save()
+ ctx.font = `${defaultSize * scale}px ${defaultFont}`
+ ctx.fillText(text, x, y)
+ ctx.restore()
+ }
}
}
diff --git a/src/editor/core/position/Position.ts b/src/editor/core/position/Position.ts
index 3259047..5b4510e 100644
--- a/src/editor/core/position/Position.ts
+++ b/src/editor/core/position/Position.ts
@@ -1,4 +1,4 @@
-import { ElementType, RowFlex, VerticalAlign } from '../..'
+import { ElementType, ListStyle, RowFlex, VerticalAlign } from '../..'
import { ZERO } from '../../dataset/constant/Common'
import { ControlComponent } from '../../dataset/enum/Control'
import {
@@ -358,6 +358,7 @@ export class Position {
coordinate: { leftTop, rightTop, leftBottom }
} = positionList[j]
if (positionNo !== pageNo) continue
+ if (pageNo > positionNo) break
// 命中元素
if (
leftTop[0] - left <= x &&
@@ -482,17 +483,15 @@ export class Position {
for (let j = 0; j < lastLetterList.length; j++) {
const {
index,
- pageNo,
+ rowNo,
coordinate: { leftTop, leftBottom }
} = lastLetterList[j]
- if (positionNo !== pageNo) continue
if (y > leftTop[1] && y <= leftBottom[1]) {
- const isHead = x < this.options.margins[3]
+ const headIndex = positionList.findIndex(
+ p => p.pageNo === positionNo && p.rowNo === rowNo
+ )
// 是否在头部
- if (isHead) {
- const headIndex = positionList.findIndex(
- p => p.pageNo === positionNo && p.rowNo === lastLetterList[j].rowNo
- )
+ if (x < this.options.margins[3]) {
// 头部元素为空元素时无需选中
if (~headIndex) {
if (positionList[headIndex].value === ZERO) {
@@ -505,6 +504,17 @@ export class Position {
curPositionIndex = index
}
} else {
+ // 是否是复选框列表
+ if (
+ elementList[headIndex].listStyle === ListStyle.CHECKBOX &&
+ x < leftTop[0]
+ ) {
+ return {
+ index: headIndex,
+ isDirectHit: true,
+ isCheckbox: true
+ }
+ }
curPositionIndex = index
}
isLastArea = true
diff --git a/src/editor/dataset/constant/List.ts b/src/editor/dataset/constant/List.ts
index 49de62e..c240cb9 100644
--- a/src/editor/dataset/constant/List.ts
+++ b/src/editor/dataset/constant/List.ts
@@ -3,7 +3,8 @@ import { ListStyle, ListType, UlStyle } from '../enum/List'
export const ulStyleMapping: Record = {
[UlStyle.DISC]: '•',
[UlStyle.CIRCLE]: '◦',
- [UlStyle.SQUARE]: '▫︎'
+ [UlStyle.SQUARE]: '▫︎',
+ [UlStyle.CHECKBOX]: '☑️'
}
export const listTypeElementMapping: Record = {
@@ -15,5 +16,6 @@ export const listStyleCSSMapping: Record = {
[ListStyle.DISC]: 'disc',
[ListStyle.CIRCLE]: 'circle',
[ListStyle.SQUARE]: 'square',
- [ListStyle.DECIMAL]: 'decimal'
+ [ListStyle.DECIMAL]: 'decimal',
+ [ListStyle.CHECKBOX]: 'checkbox'
}
diff --git a/src/editor/dataset/constant/Regular.ts b/src/editor/dataset/constant/Regular.ts
index 9cd4b2b..8549ac9 100644
--- a/src/editor/dataset/constant/Regular.ts
+++ b/src/editor/dataset/constant/Regular.ts
@@ -1,3 +1,5 @@
+import { ZERO } from './Common'
+
export const NUMBER_REG = /[0-9]/
export const NUMBER_LIKE_REG = /[0-9.]/
export const CHINESE_REG = /[\u4e00-\u9fa5]/
@@ -13,3 +15,5 @@ export const UNICODE_SYMBOL_REG = new RegExp(
export const PUNCTUATION_REG =
/[、,。?!;:……「」“”‘’*()【】〔〕〖〗〘〙〚〛《》———﹝﹞–—\\/·.,!?;:`~<>()[\]{}'"|]/
+
+export const START_LINE_BREAK_REG = new RegExp(`^[${ZERO}\n]`)
diff --git a/src/editor/dataset/enum/List.ts b/src/editor/dataset/enum/List.ts
index a7f4fbf..efffe56 100644
--- a/src/editor/dataset/enum/List.ts
+++ b/src/editor/dataset/enum/List.ts
@@ -6,7 +6,8 @@ export enum ListType {
export enum UlStyle {
DISC = 'disc', // 实心圆点
CIRCLE = 'circle', // 空心圆点
- SQUARE = 'square' // 实心方块
+ SQUARE = 'square', // 实心方块
+ CHECKBOX = 'checkbox' // 复选框
}
export enum OlStyle {
@@ -17,5 +18,6 @@ export enum ListStyle {
DISC = UlStyle.DISC,
CIRCLE = UlStyle.CIRCLE,
SQUARE = UlStyle.SQUARE,
- DECIMAL = OlStyle.DECIMAL
+ DECIMAL = OlStyle.DECIMAL,
+ CHECKBOX = UlStyle.CHECKBOX
}
diff --git a/src/editor/utils/element.ts b/src/editor/utils/element.ts
index d29d793..7c70ab8 100644
--- a/src/editor/utils/element.ts
+++ b/src/editor/utils/element.ts
@@ -30,6 +30,7 @@ import {
listStyleCSSMapping,
listTypeElementMapping
} from '../dataset/constant/List'
+import { START_LINE_BREAK_REG } from '../dataset/constant/Regular'
import {
titleNodeNameMapping,
titleOrderNumberMapping,
@@ -71,7 +72,7 @@ export function formatElementList(
if (
isHandleFirstElement &&
((startElement?.type && startElement.type !== ElementType.TEXT) ||
- (startElement?.value !== ZERO && startElement?.value !== '\n'))
+ !START_LINE_BREAK_REG.test(startElement?.value))
) {
elementList.unshift({
value: ZERO
@@ -431,10 +432,11 @@ export function zipElementList(payload: IElement[]): IElement[] {
let e = 0
while (e < elementList.length) {
let element = elementList[e]
- // 上下文首字符(占位符)
+ // 上下文首字符(占位符)-列表首字符要保留避免是复选框
if (
e === 0 &&
element.value === ZERO &&
+ !element.listId &&
(!element.type || element.type === ElementType.TEXT)
) {
e++
@@ -444,51 +446,55 @@ export function zipElementList(payload: IElement[]): IElement[] {
if (element.titleId && element.level) {
// 标题处理
const titleId = element.titleId
- const level = element.level
- const titleElement: IElement = {
- type: ElementType.TITLE,
- value: '',
- level
- }
- const valueList: IElement[] = []
- while (e < elementList.length) {
- const titleE = elementList[e]
- if (titleId !== titleE.titleId) {
- e--
- break
+ if (titleId) {
+ const level = element.level
+ const titleElement: IElement = {
+ type: ElementType.TITLE,
+ value: '',
+ level
}
- delete titleE.level
- valueList.push(titleE)
- e++
+ const valueList: IElement[] = []
+ while (e < elementList.length) {
+ const titleE = elementList[e]
+ if (titleId !== titleE.titleId) {
+ e--
+ break
+ }
+ delete titleE.level
+ valueList.push(titleE)
+ e++
+ }
+ titleElement.valueList = zipElementList(valueList)
+ element = titleElement
}
- 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
+ if (listId) {
+ const listType = element.listType
+ const listStyle = element.listStyle
+ const listElement: IElement = {
+ type: ElementType.LIST,
+ value: '',
+ listId,
+ listType,
+ listStyle
}
- delete listE.listType
- delete listE.listStyle
- valueList.push(listE)
- e++
+ 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
}
- listElement.valueList = zipElementList(valueList)
- element = listElement
} else if (element.type === ElementType.TABLE) {
// 分页表格先进行合并
if (element.pagingId) {
@@ -532,71 +538,77 @@ export function zipElementList(payload: IElement[]): IElement[] {
} else if (element.type === ElementType.HYPERLINK) {
// 超链接处理
const hyperlinkId = element.hyperlinkId
- const hyperlinkElement: IElement = {
- type: ElementType.HYPERLINK,
- value: '',
- url: element.url
- }
- const valueList: IElement[] = []
- while (e < elementList.length) {
- const hyperlinkE = elementList[e]
- if (hyperlinkId !== hyperlinkE.hyperlinkId) {
- e--
- break
+ if (hyperlinkId) {
+ const hyperlinkElement: IElement = {
+ type: ElementType.HYPERLINK,
+ value: '',
+ url: element.url
}
- delete hyperlinkE.type
- delete hyperlinkE.url
- valueList.push(hyperlinkE)
- e++
+ const valueList: IElement[] = []
+ while (e < elementList.length) {
+ const hyperlinkE = elementList[e]
+ if (hyperlinkId !== hyperlinkE.hyperlinkId) {
+ e--
+ break
+ }
+ delete hyperlinkE.type
+ delete hyperlinkE.url
+ valueList.push(hyperlinkE)
+ e++
+ }
+ hyperlinkElement.valueList = zipElementList(valueList)
+ element = hyperlinkElement
}
- hyperlinkElement.valueList = zipElementList(valueList)
- element = hyperlinkElement
} else if (element.type === ElementType.DATE) {
const dateId = element.dateId
- const dateElement: IElement = {
- type: ElementType.DATE,
- value: '',
- dateFormat: element.dateFormat
- }
- const valueList: IElement[] = []
- while (e < elementList.length) {
- const dateE = elementList[e]
- if (dateId !== dateE.dateId) {
- e--
- break
+ if (dateId) {
+ const dateElement: IElement = {
+ type: ElementType.DATE,
+ value: '',
+ dateFormat: element.dateFormat
}
- delete dateE.type
- delete dateE.dateFormat
- valueList.push(dateE)
- e++
+ const valueList: IElement[] = []
+ while (e < elementList.length) {
+ const dateE = elementList[e]
+ if (dateId !== dateE.dateId) {
+ e--
+ break
+ }
+ delete dateE.type
+ delete dateE.dateFormat
+ valueList.push(dateE)
+ e++
+ }
+ dateElement.valueList = zipElementList(valueList)
+ element = dateElement
}
- dateElement.valueList = zipElementList(valueList)
- element = dateElement
} else if (element.controlId) {
// 控件处理
const controlId = element.controlId
- const control = element.control!
- const controlElement: IElement = {
- type: ElementType.CONTROL,
- value: '',
- control
- }
- const valueList: IElement[] = []
- while (e < elementList.length) {
- const controlE = elementList[e]
- if (controlId !== controlE.controlId) {
- e--
- break
+ if (controlId) {
+ const control = element.control!
+ const controlElement: IElement = {
+ type: ElementType.CONTROL,
+ value: '',
+ control
}
- if (controlE.controlComponent === ControlComponent.VALUE) {
- delete controlE.control
- delete controlE.controlId
- valueList.push(controlE)
+ const valueList: IElement[] = []
+ while (e < elementList.length) {
+ const controlE = elementList[e]
+ if (controlId !== controlE.controlId) {
+ e--
+ break
+ }
+ if (controlE.controlComponent === ControlComponent.VALUE) {
+ delete controlE.control
+ delete controlE.controlId
+ valueList.push(controlE)
+ }
+ e++
}
- e++
+ controlElement.control!.value = zipElementList(valueList)
+ element = controlElement
}
- controlElement.control!.value = zipElementList(valueList)
- element = controlElement
}
// 组合元素
const pickElement = pickElementAttr(element)
@@ -768,6 +780,11 @@ export function splitListElement(
const listElementListMap: Map = new Map()
for (let e = 0; e < elementList.length; e++) {
const element = elementList[e]
+ // 移除列表首行换行字符-如果是复选框直接忽略
+ if (e === 0) {
+ if (element.checkbox) continue
+ element.value = element.value.replace(START_LINE_BREAK_REG, '')
+ }
if (element.listWrap) {
const listElementList = listElementListMap.get(curListIndex) || []
listElementList.push(element)
diff --git a/src/style.css b/src/style.css
index dc2c690..f32bfac 100644
--- a/src/style.css
+++ b/src/style.css
@@ -367,6 +367,10 @@ ul {
margin-left: 18px;
}
+.menu-item__list .options>ul>li[data-list-style='checkbox'] li::marker {
+ font-size: 11px;
+}
+
.menu-item__image i {
background-image: url('./assets/images/image.svg');
}