mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 10:08:34 +00:00
feat: enhance element detail
This commit is contained in:
parent
39478d072f
commit
a2979cd5ba
18 changed files with 224 additions and 48 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import type { ActiveStore, StoreSharer, ViewScaleInfo, ViewSizeInfo } from '@idraw/types';
|
||||
import type { ActiveStore, Element, ElementDetailMap, RecursivePartial, StoreSharer, ViewScaleInfo, ViewSizeInfo } from '@idraw/types';
|
||||
import { Store } from '@idraw/util';
|
||||
|
||||
const defaultActiveStorage: ActiveStore = {
|
||||
|
|
@ -12,10 +12,11 @@ const defaultActiveStorage: ActiveStore = {
|
|||
offsetLeft: 0,
|
||||
offsetRight: 0,
|
||||
offsetTop: 0,
|
||||
offsetBottom: 0
|
||||
offsetBottom: 0,
|
||||
overrideElementMap: null
|
||||
};
|
||||
|
||||
export class Sharer implements StoreSharer<Record<string | number | symbol, any>> {
|
||||
export class Sharer implements StoreSharer<Record<string | number | symbol | any, any>> {
|
||||
#activeStore: Store<ActiveStore>;
|
||||
#sharedStore: Store<{
|
||||
[string: string | number | symbol]: any;
|
||||
|
|
@ -96,4 +97,12 @@ export class Sharer implements StoreSharer<Record<string | number | symbol, any>
|
|||
};
|
||||
return sizeInfo;
|
||||
}
|
||||
|
||||
getActiveOverrideElemenentMap(): Record<string, RecursivePartial<Element<keyof ElementDetailMap, Record<string, any>>>> | null {
|
||||
return this.#activeStore.get('overrideElementMap');
|
||||
}
|
||||
|
||||
setActiveOverrideElemenentMap(map: Record<string, RecursivePartial<Element<keyof ElementDetailMap, Record<string, any>>>> | null): void {
|
||||
this.#activeStore.set('overrideElementMap', map);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -705,7 +705,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
|
|||
viewer.drawFrame();
|
||||
return;
|
||||
}
|
||||
} else if (target.elements.length === 1 && target.elements[0]?.type === 'text') {
|
||||
} else if (target.elements.length === 1 && target.elements[0]?.type === 'text' && !target.elements[0]?.operations?.invisible) {
|
||||
eventHub.trigger(middlewareEventTextEdit, {
|
||||
element: target.elements[0],
|
||||
groupQueue: sharer.getSharedStorage(keyGroupQueue) || [],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { BoardMiddleware, CoreEventMap, Element, ElementSize, ViewScaleInfo, ElementPosition } from '@idraw/types';
|
||||
import { limitAngle, getDefaultElementDetailConfig } from '@idraw/util';
|
||||
import { limitAngle, getDefaultElementDetailConfig, enhanceFontFamliy } from '@idraw/util';
|
||||
export const middlewareEventTextEdit = '@middleware/text-edit';
|
||||
export const middlewareEventTextChange = '@middleware/text-change';
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ type ExtendEventMap = Record<typeof middlewareEventTextEdit, TextEditEvent> & Re
|
|||
const defaultElementDetail = getDefaultElementDetailConfig();
|
||||
|
||||
export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap & ExtendEventMap> = (opts) => {
|
||||
const { eventHub, boardContent, viewer } = opts;
|
||||
const { eventHub, boardContent, viewer, sharer } = opts;
|
||||
const canvas = boardContent.boardContext.canvas;
|
||||
// const textarea = document.createElement('textarea');
|
||||
const textarea = document.createElement('div');
|
||||
|
|
@ -53,9 +53,26 @@ export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap
|
|||
resetCanvasWrapper();
|
||||
resetTextArea(e);
|
||||
mask.style.display = 'block';
|
||||
if (activeElem?.uuid) {
|
||||
sharer.setActiveOverrideElemenentMap({
|
||||
[activeElem.uuid]: {
|
||||
operations: { invisible: true }
|
||||
}
|
||||
});
|
||||
viewer.drawFrame();
|
||||
}
|
||||
};
|
||||
|
||||
const hideTextArea = () => {
|
||||
if (activeElem?.uuid) {
|
||||
const map = sharer.getActiveOverrideElemenentMap();
|
||||
if (map) {
|
||||
delete map[activeElem.uuid];
|
||||
}
|
||||
sharer.setActiveOverrideElemenentMap(map);
|
||||
viewer.drawFrame();
|
||||
}
|
||||
|
||||
mask.style.display = 'none';
|
||||
activeElem = null;
|
||||
activePosition = [];
|
||||
|
|
@ -157,11 +174,13 @@ export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap
|
|||
textarea.style.resize = 'none';
|
||||
textarea.style.overflow = 'hidden';
|
||||
textarea.style.wordBreak = 'break-all';
|
||||
textarea.style.background = '#FFFFFF';
|
||||
textarea.style.color = '#333333';
|
||||
// textarea.style.background = '#FFFFFF';
|
||||
textarea.style.background = 'transparent';
|
||||
// textarea.style.color = '#333333';
|
||||
textarea.style.color = `${detail.color || '#333333'}`;
|
||||
textarea.style.fontSize = `${detail.fontSize * scale}px`;
|
||||
textarea.style.lineHeight = `${detail.lineHeight * scale}px`;
|
||||
textarea.style.fontFamily = detail.fontFamily;
|
||||
textarea.style.lineHeight = `${(detail.lineHeight || detail.fontSize) * scale}px`;
|
||||
textarea.style.fontFamily = enhanceFontFamliy(detail.fontFamily);
|
||||
textarea.style.fontWeight = `${detail.fontWeight}`;
|
||||
textarea.style.padding = '0';
|
||||
textarea.style.margin = '0';
|
||||
|
|
|
|||
|
|
@ -29,22 +29,40 @@ export function drawBox(
|
|||
const { parentOpacity } = opts;
|
||||
const opacity = getOpacity(originElem) * parentOpacity;
|
||||
|
||||
drawClipPath(ctx, viewElem, {
|
||||
originElem,
|
||||
calcElemSize,
|
||||
viewScaleInfo,
|
||||
viewSizeInfo,
|
||||
renderContent: () => {
|
||||
ctx.globalAlpha = opacity;
|
||||
drawBoxBackground(ctx, viewElem, { pattern, viewScaleInfo, viewSizeInfo });
|
||||
renderContent?.();
|
||||
drawBoxBorder(ctx, viewElem, { viewScaleInfo, viewSizeInfo });
|
||||
ctx.globalAlpha = parentOpacity;
|
||||
const { clipPath, clipPathStrokeColor, clipPathStrokeWidth } = originElem.detail;
|
||||
|
||||
const mainRender = () => {
|
||||
ctx.globalAlpha = opacity;
|
||||
drawBoxBackground(ctx, viewElem, { pattern, viewScaleInfo, viewSizeInfo });
|
||||
renderContent?.();
|
||||
drawBoxBorder(ctx, viewElem, { viewScaleInfo, viewSizeInfo });
|
||||
ctx.globalAlpha = parentOpacity;
|
||||
};
|
||||
if (clipPath) {
|
||||
drawClipPath(ctx, viewElem, {
|
||||
originElem,
|
||||
calcElemSize,
|
||||
viewScaleInfo,
|
||||
viewSizeInfo,
|
||||
renderContent: () => {
|
||||
mainRender();
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof clipPathStrokeWidth === 'number' && clipPathStrokeWidth > 0 && clipPathStrokeColor) {
|
||||
drawClipPathStroke(ctx, viewElem, {
|
||||
originElem,
|
||||
calcElemSize,
|
||||
viewScaleInfo,
|
||||
viewSizeInfo,
|
||||
parentOpacity
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mainRender();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
function drawClipPath(
|
||||
ctx: ViewContext2D,
|
||||
viewElem: Element<ElementType>,
|
||||
|
|
@ -75,6 +93,56 @@ function drawClipPath(
|
|||
const pathStr = generateSVGPath(clipPath.commands || []);
|
||||
const path2d = new Path2D(pathStr);
|
||||
ctx.clip(path2d);
|
||||
|
||||
ctx.translate(0 - (internalX as number), 0 - (internalY as number));
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
|
||||
rotateElement(ctx, { ...viewElem }, () => {
|
||||
renderContent?.();
|
||||
});
|
||||
|
||||
ctx.restore();
|
||||
} else {
|
||||
renderContent?.();
|
||||
}
|
||||
}
|
||||
|
||||
function drawClipPathStroke(
|
||||
ctx: ViewContext2D,
|
||||
viewElem: Element<ElementType>,
|
||||
opts: {
|
||||
originElem?: Element<ElementType>;
|
||||
calcElemSize?: ElementSize;
|
||||
renderContent?: () => void;
|
||||
viewScaleInfo: ViewScaleInfo;
|
||||
viewSizeInfo: ViewSizeInfo;
|
||||
parentOpacity: number;
|
||||
}
|
||||
) {
|
||||
const { renderContent, originElem, calcElemSize, viewSizeInfo, parentOpacity } = opts;
|
||||
const totalScale = viewSizeInfo.devicePixelRatio;
|
||||
const { clipPath, clipPathStrokeColor, clipPathStrokeWidth } = originElem?.detail || {};
|
||||
if (clipPath && calcElemSize && clipPath.commands && typeof clipPathStrokeWidth === 'number' && clipPathStrokeWidth > 0 && clipPathStrokeColor) {
|
||||
const { x, y, w, h } = calcElemSize;
|
||||
const { originW, originH, originX, originY } = clipPath;
|
||||
const scaleW = w / originW;
|
||||
const scaleH = h / originH;
|
||||
const viewOriginX = originX * scaleW;
|
||||
const viewOriginY = originY * scaleH;
|
||||
const internalX = x - viewOriginX;
|
||||
const internalY = y - viewOriginY;
|
||||
|
||||
ctx.save();
|
||||
ctx.globalAlpha = parentOpacity;
|
||||
ctx.translate(internalX as number, internalY as number);
|
||||
ctx.scale(totalScale * scaleW, totalScale * scaleH);
|
||||
const pathStr = generateSVGPath(clipPath.commands || []);
|
||||
const path2d = new Path2D(pathStr);
|
||||
|
||||
ctx.strokeStyle = clipPathStrokeColor;
|
||||
ctx.lineWidth = clipPathStrokeWidth;
|
||||
ctx.stroke(path2d);
|
||||
|
||||
ctx.translate(0 - (internalX as number), 0 - (internalY as number));
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
|
||||
|
|
|
|||
11
packages/renderer/src/draw/global.ts
Normal file
11
packages/renderer/src/draw/global.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import type { RendererDrawElementOptions, ViewContext2D, ElementGlobalDetail } from '@idraw/types';
|
||||
|
||||
export function drawGlobalBackground(ctx: ViewContext2D, global: ElementGlobalDetail | undefined, opts: RendererDrawElementOptions) {
|
||||
if (typeof global?.background === 'string') {
|
||||
const { viewSizeInfo } = opts;
|
||||
const { width, height } = viewSizeInfo;
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.fillStyle = global.background;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,11 @@ export function drawElement(ctx: ViewContext2D, elem: Element<ElementType>, opts
|
|||
return;
|
||||
}
|
||||
|
||||
const { overrideElementMap } = opts;
|
||||
if (overrideElementMap?.[elem.uuid]?.operations?.invisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
switch (elem.type) {
|
||||
case 'rect': {
|
||||
|
|
@ -71,7 +76,7 @@ export function drawElement(ctx: ViewContext2D, elem: Element<ElementType>, opts
|
|||
|
||||
export function drawGroup(ctx: ViewContext2D, elem: Element<'group'>, opts: RendererDrawElementOptions) {
|
||||
const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
const { x, y, w, h, angle } = calcViewElementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h, angle: elem.angle }, { viewScaleInfo, viewSizeInfo }) || elem;
|
||||
const { x, y, w, h, angle } = calcViewElementSize({ x: elem.x, y: elem.y, w: elem.w, h: elem.h, angle: elem.angle }, { viewScaleInfo }) || elem;
|
||||
const viewElem = { ...elem, ...{ x, y, w, h, angle } };
|
||||
rotateElement(ctx, { x, y, w, h, angle }, () => {
|
||||
ctx.globalAlpha = getOpacity(elem) * parentOpacity;
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ export { drawHTML } from './html';
|
|||
export { drawText } from './text';
|
||||
export { drawElementList } from './elements';
|
||||
export { drawLayout } from './layout';
|
||||
export { drawGlobalBackground } from './global';
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { drawBoxShadow, drawBoxBackground, drawBoxBorder } from './box';
|
|||
|
||||
export function drawLayout(ctx: ViewContext2D, layout: DataLayout, opts: RendererDrawElementOptions, renderContent: (ctx: ViewContext2D) => void) {
|
||||
const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
const elem: Element = { uuid: 'layout', type: 'group', ...layout };
|
||||
const { x, y, w, h } = calcViewElementSize(elem, { viewScaleInfo, viewSizeInfo }) || elem;
|
||||
const elem: Element = { uuid: 'layout', type: 'group', ...layout } as Element;
|
||||
const { x, y, w, h } = calcViewElementSize(elem, { viewScaleInfo }) || elem;
|
||||
const angle = 0;
|
||||
const viewElem: Element = { ...elem, ...{ x, y, w, h, angle } } as Element;
|
||||
ctx.globalAlpha = 1;
|
||||
|
|
@ -20,7 +20,7 @@ export function drawLayout(ctx: ViewContext2D, layout: DataLayout, opts: Rendere
|
|||
if (layout.detail.overflow === 'hidden') {
|
||||
const { viewScaleInfo, viewSizeInfo } = opts;
|
||||
const elem: Element<'group'> = { uuid: 'layout', type: 'group', ...layout } as Element<'group'>;
|
||||
const viewElemSize = calcViewElementSize(elem, { viewScaleInfo, viewSizeInfo }) || elem;
|
||||
const viewElemSize = calcViewElementSize(elem, { viewScaleInfo }) || elem;
|
||||
const viewElem = { ...elem, ...viewElemSize };
|
||||
const { x, y, w, h, radiusList } = calcViewBoxSize(viewElem, {
|
||||
viewScaleInfo,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
import type { Element, RendererDrawElementOptions, ViewContext2D } from '@idraw/types';
|
||||
import { rotateElement, calcViewElementSize } from '@idraw/util';
|
||||
import { rotateElement, calcViewElementSize, enhanceFontFamliy } from '@idraw/util';
|
||||
import { is, isColorStr, getDefaultElementDetailConfig } from '@idraw/util';
|
||||
import { drawBox } from './box';
|
||||
|
||||
const detailConfig = getDefaultElementDetailConfig();
|
||||
|
||||
// TODO
|
||||
function isTextWidthWithinErrorRange(w0: number, w1: number, scale: number): boolean {
|
||||
if (scale < 0.5) {
|
||||
if (w0 < w1 && (w0 - w1) / w0 > -0.15) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return w0 >= w1;
|
||||
}
|
||||
|
||||
export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: RendererDrawElementOptions) {
|
||||
const { viewScaleInfo, viewSizeInfo, parentOpacity } = opts;
|
||||
const { x, y, w, h, angle } = calcViewElementSize(elem, { viewScaleInfo }) || elem;
|
||||
|
|
@ -24,6 +34,10 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render
|
|||
const originFontSize = detail.fontSize || detailConfig.fontSize;
|
||||
const fontSize = originFontSize * viewScaleInfo.scale;
|
||||
|
||||
if (fontSize < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originLineHeight = detail.lineHeight || originFontSize;
|
||||
const lineHeight = originLineHeight * viewScaleInfo.scale;
|
||||
|
||||
|
|
@ -32,7 +46,7 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render
|
|||
ctx.$setFont({
|
||||
fontWeight: detail.fontWeight,
|
||||
fontSize: fontSize,
|
||||
fontFamily: detail.fontFamily
|
||||
fontFamily: enhanceFontFamliy(detail.fontFamily)
|
||||
});
|
||||
let detailText = detail.text.replace(/\r\n/gi, '\n');
|
||||
if (detail.textTransform === 'lowercase') {
|
||||
|
|
@ -46,30 +60,49 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render
|
|||
const lines: { text: string; width: number }[] = [];
|
||||
|
||||
let lineNum = 0;
|
||||
detailTextList.forEach((tempText: string, idx: number) => {
|
||||
detailTextList.forEach((itemText: string, idx: number) => {
|
||||
if (detail.minInlineSize === 'maxContent') {
|
||||
lines.push({
|
||||
text: tempText,
|
||||
width: ctx.$undoPixelRatio(ctx.measureText(tempText).width)
|
||||
text: itemText,
|
||||
width: ctx.$undoPixelRatio(ctx.measureText(itemText).width)
|
||||
});
|
||||
} else {
|
||||
let lineText = '';
|
||||
if (tempText.length > 0) {
|
||||
for (let i = 0; i < tempText.length; i++) {
|
||||
if (ctx.measureText(lineText + (tempText[i] || '')).width <= ctx.$doPixelRatio(w)) {
|
||||
lineText += tempText[i] || '';
|
||||
let splitStr = '';
|
||||
let tempStrList: string[] = itemText.split(splitStr);
|
||||
if (detail.wordBreak === 'normal') {
|
||||
const splitStr = ' ';
|
||||
const wordList = itemText.split(splitStr);
|
||||
tempStrList = [];
|
||||
wordList.forEach((word: string, idx: number) => {
|
||||
tempStrList.push(word);
|
||||
if (idx < wordList.length - 1) {
|
||||
tempStrList.push(splitStr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (tempStrList.length === 1 && detail.overflow === 'visible') {
|
||||
lines.push({
|
||||
text: tempStrList[0],
|
||||
width: ctx.$undoPixelRatio(ctx.measureText(tempStrList[0]).width)
|
||||
});
|
||||
} else if (tempStrList.length > 0) {
|
||||
for (let i = 0; i < tempStrList.length; i++) {
|
||||
if (isTextWidthWithinErrorRange(ctx.$doPixelRatio(w), ctx.measureText(lineText + tempStrList[i]).width, viewScaleInfo.scale)) {
|
||||
lineText += tempStrList[i] || '';
|
||||
} else {
|
||||
lines.push({
|
||||
text: lineText,
|
||||
width: ctx.$undoPixelRatio(ctx.measureText(lineText).width)
|
||||
});
|
||||
lineText = tempText[i] || '';
|
||||
lineText = tempStrList[i] || '';
|
||||
lineNum++;
|
||||
}
|
||||
if ((lineNum + 1) * fontHeight > h) {
|
||||
if ((lineNum + 1) * fontHeight > h && detail.overflow === 'hidden') {
|
||||
break;
|
||||
}
|
||||
if (tempText.length - 1 === i) {
|
||||
if (tempStrList.length - 1 === i) {
|
||||
if ((lineNum + 1) * fontHeight <= h) {
|
||||
lines.push({
|
||||
text: lineText,
|
||||
|
|
@ -92,6 +125,10 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render
|
|||
});
|
||||
|
||||
let startY = 0;
|
||||
let eachLineStartY = 0;
|
||||
if (fontHeight > fontSize) {
|
||||
eachLineStartY = (fontHeight - fontSize) / 2;
|
||||
}
|
||||
if (lines.length * fontHeight < h) {
|
||||
if (elem.detail.verticalAlign === 'top') {
|
||||
startY = 0;
|
||||
|
|
@ -125,7 +162,7 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render
|
|||
} else if (detail.textAlign === 'right') {
|
||||
_x = x + (w - line.width);
|
||||
}
|
||||
ctx.fillText(line.text, _x, _y + fontHeight * i);
|
||||
ctx.fillText(line.text, _x, _y + fontHeight * i + eachLineStartY);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { EventEmitter } from '@idraw/util';
|
||||
import type { DataLayout, LoadItemMap } from '@idraw/types';
|
||||
import { drawElementList, drawLayout } from './draw/index';
|
||||
import { drawElementList, drawLayout, drawGlobalBackground } from './draw/index';
|
||||
import { Loader } from './loader';
|
||||
import type { Data, BoardRenderer, RendererOptions, RendererEventMap, RendererDrawOptions } from '@idraw/types';
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ export class Renderer extends EventEmitter<RendererEventMap> implements BoardRen
|
|||
|
||||
drawData(data: Data, opts: RendererDrawOptions) {
|
||||
const loader = this.#loader;
|
||||
const { calculator } = this.#opts;
|
||||
const { calculator, sharer } = this.#opts;
|
||||
const viewContext = this.#opts.viewContext;
|
||||
viewContext.clearRect(0, 0, viewContext.canvas.width, viewContext.canvas.height);
|
||||
const parentElementSize = {
|
||||
|
|
@ -69,8 +69,10 @@ export class Renderer extends EventEmitter<RendererEventMap> implements BoardRen
|
|||
parentElementSize,
|
||||
elementAssets: data.assets,
|
||||
parentOpacity: 1,
|
||||
overrideElementMap: sharer?.getActiveOverrideElemenentMap(),
|
||||
...opts
|
||||
};
|
||||
drawGlobalBackground(viewContext, data.global, drawOpts);
|
||||
if (data.layout) {
|
||||
drawLayout(viewContext, data.layout as DataLayout, drawOpts, () => {
|
||||
drawElementList(viewContext, data, drawOpts);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { ElementBaseDetail, ElementTextDetail, ElementGroupDetail } from './element';
|
||||
|
||||
export type DefaultElementDetailConfig = Required<Omit<ElementBaseDetail, 'clipPath' | 'background'>> &
|
||||
Required<Pick<ElementTextDetail, 'color' | 'textAlign' | 'verticalAlign' | 'fontSize' | 'fontFamily' | 'fontWeight'>> &
|
||||
Required<Pick<ElementTextDetail, 'color' | 'textAlign' | 'verticalAlign' | 'fontSize' | 'fontFamily' | 'fontWeight' | 'minInlineSize' | 'wordBreak'>> &
|
||||
Required<Pick<ElementGroupDetail, 'overflow'>>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { Element, ElementType, ElementAssets, ElementSize, ElementGroupDetail } from './element';
|
||||
import type { Element, ElementType, ElementAssets, ElementSize, ElementGroupDetail, ElementGlobalDetail } from './element';
|
||||
|
||||
export type DataLayout = Pick<ElementSize, 'x' | 'y' | 'w' | 'h'> & {
|
||||
detail: Pick<
|
||||
|
|
@ -16,11 +16,13 @@ export type DataLayout = Pick<ElementSize, 'x' | 'y' | 'w' | 'h'> & {
|
|||
disabledBottomRight?: boolean;
|
||||
};
|
||||
};
|
||||
export interface Data<E extends Record<string, any> = Record<string, any>> {
|
||||
|
||||
export type Data<E extends Record<string, any> = Record<string, any>> = {
|
||||
elements: Element<ElementType, E>[];
|
||||
assets?: ElementAssets;
|
||||
layout?: DataLayout;
|
||||
}
|
||||
global?: ElementGlobalDetail;
|
||||
};
|
||||
|
||||
export type Matrix = [
|
||||
number,
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ export interface ElementBaseDetail {
|
|||
background?: string | LinearGradientColor | RadialGradientColor;
|
||||
opacity?: number;
|
||||
clipPath?: ElementClipPath;
|
||||
clipPathStrokeWidth?: number;
|
||||
clipPathStrokeColor?: string;
|
||||
}
|
||||
|
||||
// interface ElementRectDetail extends ElementBaseDetail {
|
||||
|
|
@ -106,6 +108,8 @@ export interface ElementTextDetail extends ElementBaseDetail {
|
|||
textShadowBlur?: number;
|
||||
minInlineSize?: 'maxContent' | 'auto';
|
||||
textTransform?: 'none' | 'uppercase' | 'lowercase';
|
||||
wordBreak?: 'break-all' | 'normal'; // default: 'normal'
|
||||
overflow?: 'hidden' | 'visible'; // default: 'hidden'
|
||||
}
|
||||
|
||||
export interface ElementCircleDetail extends ElementBaseDetail {
|
||||
|
|
@ -174,6 +178,10 @@ export interface ElementOperations {
|
|||
deepResize?: boolean;
|
||||
}
|
||||
|
||||
export interface ElementGlobalDetail {
|
||||
background?: string;
|
||||
}
|
||||
|
||||
export interface Element<T extends ElementType = ElementType, E extends Record<string, any> = Record<string, any>> extends ElementSize {
|
||||
uuid: string;
|
||||
name?: string;
|
||||
|
|
@ -181,6 +189,7 @@ export interface Element<T extends ElementType = ElementType, E extends Record<s
|
|||
detail: ElementDetailMap[T];
|
||||
operations?: ElementOperations;
|
||||
extends?: E;
|
||||
global?: ElementGlobalDetail;
|
||||
}
|
||||
|
||||
export type Elements = Element<ElementType>[];
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { ViewScaleInfo, ViewCalculator, ViewSizeInfo } from './view';
|
||||
import type { Element, ElementSize, ElementAssets } from './element';
|
||||
import type { LoaderEventMap, LoadElementType, LoadContent, LoadItemMap } from './loader';
|
||||
import type { UtilEventEmitter } from './util';
|
||||
import type { UtilEventEmitter, RecursivePartial } from './util';
|
||||
import type { StoreSharer } from './store';
|
||||
import { ViewContext2D } from '@idraw/types';
|
||||
|
||||
|
|
@ -43,4 +43,5 @@ export interface RendererDrawElementOptions extends RendererDrawOptions {
|
|||
parentElementSize: ElementSize;
|
||||
elementAssets?: ElementAssets;
|
||||
parentOpacity: number;
|
||||
overrideElementMap?: Record<string, RecursivePartial<Element>> | null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import {
|
|||
ViewScaleInfo,
|
||||
ViewSizeInfo
|
||||
} from './view';
|
||||
import { Element } from './element';
|
||||
import { RecursivePartial } from './util';
|
||||
|
||||
export type ActiveStore = ViewSizeInfo &
|
||||
ViewScaleInfo & {
|
||||
data: Data | null;
|
||||
// selectedViewRectVertexes: ViewRectVertexes | null;
|
||||
overrideElementMap: Record<string, RecursivePartial<Element>> | null;
|
||||
};
|
||||
|
||||
export interface StoreSharer<S extends Record<any, any> = any> {
|
||||
|
|
@ -23,4 +25,6 @@ export interface StoreSharer<S extends Record<any, any> = any> {
|
|||
setActiveViewScaleInfo(viewScaleInfo: ViewScaleInfo): void;
|
||||
setActiveViewSizeInfo(size: ViewSizeInfo): void;
|
||||
getActiveViewSizeInfo(): ViewSizeInfo;
|
||||
setActiveOverrideElemenentMap(map: Record<string, RecursivePartial<Element>> | null): void;
|
||||
getActiveOverrideElemenentMap(): Record<string, RecursivePartial<Element>> | null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,3 +87,4 @@ export { deepResizeGroupElement } from './lib/resize-element';
|
|||
export { calcViewCenterContent, calcViewCenter } from './lib/view-content';
|
||||
export { modifyElement, getModifiedElement } from './lib/modify';
|
||||
// export { ModifyRecorder } from './lib/modify-recorder';
|
||||
export { enhanceFontFamliy } from './lib/text';
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ export function getDefaultElementDetailConfig(): DefaultElementDetailConfig {
|
|||
// lineHeight: 20,
|
||||
fontFamily: 'sans-serif',
|
||||
fontWeight: 400,
|
||||
minInlineSize: 'auto',
|
||||
wordBreak: 'break-all',
|
||||
overflow: 'hidden'
|
||||
};
|
||||
return config;
|
||||
|
|
|
|||
5
packages/util/src/lib/text.ts
Normal file
5
packages/util/src/lib/text.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
const baseFontFamilyList = ['-apple-system', '"system-ui"', ' "Segoe UI"', ' Roboto', '"Helvetica Neue"', 'Arial', '"Noto Sans"', ' sans-serif'];
|
||||
|
||||
export function enhanceFontFamliy(fontFamily?: string): string {
|
||||
return [fontFamily, ...baseFontFamilyList].join(', ');
|
||||
}
|
||||
Loading…
Reference in a new issue