kopia lustrzana https://github.com/Tldraw/Tldraw
hoist opacity out of props (#1526)
This change hoists opacity out of props and changes it to a number instead of an enum. The change to a number is to make tldraw more flexible for library consumers who might want more expressivity with opacity than our 5 possible values allow. the tldraw editor will now happily respect any opacity between 0 and 1. The limit to our supported values is enforced only in the UI. I think this is limited enough that it's a reasonable tradeoff between in-app simplicity and giving external developers the flexibility they need. There's a new `opacityForNextShape` property on the instance. This works exactly the same way as propsForNextShape does, except... it's just for opacity. With this, there should be no user-facing changes to how opacity works in tldraw. There are also new `opacity`/`setOpacity` APIs in the editor that work with it/selections similar to how props do. @ds300 do you mind reviewing the migrations here? ### Change Type - [x] `major` — Breaking Change ### Test Plan - [x] Unit Tests - [ ] Webdriver tests ### Release Notes [internal only for now]pull/1547/head
rodzic
355ed1de72
commit
f2d8fae6ea
|
@ -1,9 +1,8 @@
|
||||||
import { TLBaseShape, TLOpacityType } from '@tldraw/tldraw'
|
import { TLBaseShape } from '@tldraw/tldraw'
|
||||||
|
|
||||||
export type CardShape = TLBaseShape<
|
export type CardShape = TLBaseShape<
|
||||||
'card',
|
'card',
|
||||||
{
|
{
|
||||||
opacity: TLOpacityType // necessary for all shapes at the moment, others can be whatever you want!
|
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ export class CardShapeUtil extends BaseBoxShapeUtil<CardShape> {
|
||||||
// Default props — used for shapes created with the tool
|
// Default props — used for shapes created with the tool
|
||||||
override defaultProps(): CardShape['props'] {
|
override defaultProps(): CardShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
w: 300,
|
w: 300,
|
||||||
h: 300,
|
h: 300,
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,8 +178,6 @@ export abstract class BaseBoxShapeTool extends StateNode {
|
||||||
static initial: string;
|
static initial: string;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
abstract shapeType: string;
|
abstract shapeType: string;
|
||||||
// (undocumented)
|
|
||||||
styles: ("align" | "arrowheadEnd" | "arrowheadStart" | "color" | "dash" | "fill" | "font" | "geo" | "icon" | "labelColor" | "opacity" | "size" | "spline" | "verticalAlign")[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
|
@ -623,6 +621,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
description: string;
|
description: string;
|
||||||
}>;
|
}>;
|
||||||
get onlySelectedShape(): null | TLShape;
|
get onlySelectedShape(): null | TLShape;
|
||||||
|
// (undocumented)
|
||||||
|
get opacity(): null | number;
|
||||||
get openMenus(): string[];
|
get openMenus(): string[];
|
||||||
packShapes(ids?: TLShapeId[], padding?: number): this;
|
packShapes(ids?: TLShapeId[], padding?: number): this;
|
||||||
get pages(): TLPage[];
|
get pages(): TLPage[];
|
||||||
|
@ -717,6 +717,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
setHoveredId(id?: null | TLShapeId): this;
|
setHoveredId(id?: null | TLShapeId): this;
|
||||||
setInstancePageState(partial: Partial<TLInstancePageState>, ephemeral?: boolean): void;
|
setInstancePageState(partial: Partial<TLInstancePageState>, ephemeral?: boolean): void;
|
||||||
setLocale(locale: string): void;
|
setLocale(locale: string): void;
|
||||||
|
setOpacity(opacity: number, ephemeral?: boolean, squashing?: boolean): this;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
setPenMode(isPenMode: boolean): this;
|
setPenMode(isPenMode: boolean): this;
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
|
@ -915,7 +916,6 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
||||||
fill: "none" | "pattern" | "semi" | "solid";
|
fill: "none" | "pattern" | "semi" | "solid";
|
||||||
dash: "dashed" | "dotted" | "draw" | "solid";
|
dash: "dashed" | "dotted" | "draw" | "solid";
|
||||||
size: "l" | "m" | "s" | "xl";
|
size: "l" | "m" | "s" | "xl";
|
||||||
opacity: "0.1" | "0.25" | "0.5" | "0.75" | "1";
|
|
||||||
font: "draw" | "mono" | "sans" | "serif";
|
font: "draw" | "mono" | "sans" | "serif";
|
||||||
align: "end" | "middle" | "start";
|
align: "end" | "middle" | "start";
|
||||||
verticalAlign: "end" | "middle" | "start";
|
verticalAlign: "end" | "middle" | "start";
|
||||||
|
@ -931,6 +931,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
||||||
index: string;
|
index: string;
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
id: TLShapeId;
|
id: TLShapeId;
|
||||||
typeName: "shape";
|
typeName: "shape";
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
@ -944,7 +945,6 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
||||||
fill: "none" | "pattern" | "semi" | "solid";
|
fill: "none" | "pattern" | "semi" | "solid";
|
||||||
dash: "dashed" | "dotted" | "draw" | "solid";
|
dash: "dashed" | "dotted" | "draw" | "solid";
|
||||||
size: "l" | "m" | "s" | "xl";
|
size: "l" | "m" | "s" | "xl";
|
||||||
opacity: "0.1" | "0.25" | "0.5" | "0.75" | "1";
|
|
||||||
font: "draw" | "mono" | "sans" | "serif";
|
font: "draw" | "mono" | "sans" | "serif";
|
||||||
align: "end" | "middle" | "start";
|
align: "end" | "middle" | "start";
|
||||||
verticalAlign: "end" | "middle" | "start";
|
verticalAlign: "end" | "middle" | "start";
|
||||||
|
@ -960,6 +960,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
||||||
index: string;
|
index: string;
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
id: TLShapeId;
|
id: TLShapeId;
|
||||||
typeName: "shape";
|
typeName: "shape";
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
@ -975,6 +976,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
||||||
index: string;
|
index: string;
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
id: TLShapeId;
|
id: TLShapeId;
|
||||||
typeName: "shape";
|
typeName: "shape";
|
||||||
} | {
|
} | {
|
||||||
|
@ -988,6 +990,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
||||||
index: string;
|
index: string;
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
id: TLShapeId;
|
id: TLShapeId;
|
||||||
typeName: "shape";
|
typeName: "shape";
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
@ -1702,7 +1705,6 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
||||||
font: "draw" | "mono" | "sans" | "serif";
|
font: "draw" | "mono" | "sans" | "serif";
|
||||||
align: "end" | "middle" | "start";
|
align: "end" | "middle" | "start";
|
||||||
verticalAlign: "end" | "middle" | "start";
|
verticalAlign: "end" | "middle" | "start";
|
||||||
opacity: "0.1" | "0.25" | "0.5" | "0.75" | "1";
|
|
||||||
url: string;
|
url: string;
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
@ -1713,6 +1715,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
||||||
index: string;
|
index: string;
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
id: TLShapeId;
|
id: TLShapeId;
|
||||||
typeName: "shape";
|
typeName: "shape";
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
@ -1725,7 +1728,6 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
||||||
font: "draw" | "mono" | "sans" | "serif";
|
font: "draw" | "mono" | "sans" | "serif";
|
||||||
align: "end" | "middle" | "start";
|
align: "end" | "middle" | "start";
|
||||||
verticalAlign: "end" | "middle" | "start";
|
verticalAlign: "end" | "middle" | "start";
|
||||||
opacity: "0.1" | "0.25" | "0.5" | "0.75" | "1";
|
|
||||||
url: string;
|
url: string;
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
@ -1736,6 +1738,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
||||||
index: string;
|
index: string;
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
id: TLShapeId;
|
id: TLShapeId;
|
||||||
typeName: "shape";
|
typeName: "shape";
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
@ -1962,6 +1965,8 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
path: Computed<string>;
|
path: Computed<string>;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
|
shapeType?: string;
|
||||||
|
// (undocumented)
|
||||||
readonly styles: TLStyleType[];
|
readonly styles: TLStyleType[];
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
transition(id: string, info: any): this;
|
transition(id: string, info: any): this;
|
||||||
|
@ -2024,6 +2029,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
||||||
index: string;
|
index: string;
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
props: TLTextShapeProps;
|
props: TLTextShapeProps;
|
||||||
id: TLShapeId;
|
id: TLShapeId;
|
||||||
typeName: "shape";
|
typeName: "shape";
|
||||||
|
@ -2038,7 +2044,6 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
||||||
size: "l" | "m" | "s" | "xl";
|
size: "l" | "m" | "s" | "xl";
|
||||||
font: "draw" | "mono" | "sans" | "serif";
|
font: "draw" | "mono" | "sans" | "serif";
|
||||||
align: "end" | "middle" | "start";
|
align: "end" | "middle" | "start";
|
||||||
opacity: "0.1" | "0.25" | "0.5" | "0.75" | "1";
|
|
||||||
text: string;
|
text: string;
|
||||||
scale: number;
|
scale: number;
|
||||||
autoSize: boolean;
|
autoSize: boolean;
|
||||||
|
@ -2048,6 +2053,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
||||||
index: string;
|
index: string;
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
id: TLShapeId;
|
id: TLShapeId;
|
||||||
typeName: "shape";
|
typeName: "shape";
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
|
|
@ -217,13 +217,6 @@ export const STYLES: TLStyleCollections = {
|
||||||
{ id: 'l', type: 'size', icon: 'size-large' },
|
{ id: 'l', type: 'size', icon: 'size-large' },
|
||||||
{ id: 'xl', type: 'size', icon: 'size-extra-large' },
|
{ id: 'xl', type: 'size', icon: 'size-extra-large' },
|
||||||
],
|
],
|
||||||
opacity: [
|
|
||||||
{ id: '0.1', type: 'opacity', icon: 'color' },
|
|
||||||
{ id: '0.25', type: 'opacity', icon: 'color' },
|
|
||||||
{ id: '0.5', type: 'opacity', icon: 'color' },
|
|
||||||
{ id: '0.75', type: 'opacity', icon: 'color' },
|
|
||||||
{ id: '1', type: 'opacity', icon: 'color' },
|
|
||||||
],
|
|
||||||
font: [
|
font: [
|
||||||
{ id: 'draw', type: 'font', icon: 'font-draw' },
|
{ id: 'draw', type: 'font', icon: 'font-draw' },
|
||||||
{ id: 'sans', type: 'font', icon: 'font-sans' },
|
{ id: 'sans', type: 'font', icon: 'font-sans' },
|
||||||
|
|
|
@ -1151,6 +1151,42 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@computed get opacity(): number | null {
|
||||||
|
if (this.isIn('select') && this.selectedIds.length > 0) {
|
||||||
|
const shapesToCheck: TLShape[] = []
|
||||||
|
const addShape = (shapeId: TLShapeId) => {
|
||||||
|
const shape = this.getShapeById(shapeId)
|
||||||
|
if (!shape) return
|
||||||
|
// For groups, ignore the opacity of the group shape and instead include
|
||||||
|
// the opacity of the group's children. These are the shapes that would have
|
||||||
|
// their opacity changed if the user called `setOpacity` on the current selection.
|
||||||
|
if (shape.type === 'group') {
|
||||||
|
for (const childId of this.getSortedChildIds(shape.id)) {
|
||||||
|
addShape(childId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shapesToCheck.push(shape)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const shapeId of this.selectedIds) {
|
||||||
|
addShape(shapeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
let opacity: number | null = null
|
||||||
|
for (const shape of shapesToCheck) {
|
||||||
|
if (opacity === null) {
|
||||||
|
opacity = shape.opacity
|
||||||
|
} else if (opacity !== shape.opacity) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return opacity
|
||||||
|
} else {
|
||||||
|
return this.instanceState.opacityForNextShape
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of all of the shapes on the current page.
|
* An array of all of the shapes on the current page.
|
||||||
*
|
*
|
||||||
|
@ -2242,8 +2278,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
const shape = this.getShapeById(id)
|
const shape = this.getShapeById(id)
|
||||||
if (!shape) return
|
if (!shape) return
|
||||||
|
|
||||||
// todo: move opacity to a property of shape, rather than a property of props
|
let opacity = shape.opacity * parentOpacity
|
||||||
let opacity = (+(shape.props as { opacity: string }).opacity ?? 1) * parentOpacity
|
|
||||||
let isShapeErasing = false
|
let isShapeErasing = false
|
||||||
|
|
||||||
if (!isAncestorErasing && erasingIdsSet?.has(id)) {
|
if (!isAncestorErasing && erasingIdsSet?.has(id)) {
|
||||||
|
@ -4675,7 +4710,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
// We then look up each key in the tab state's props; and if it's there,
|
// We then look up each key in the tab state's props; and if it's there,
|
||||||
// we use the value from the tab state's props instead of the default.
|
// we use the value from the tab state's props instead of the default.
|
||||||
// Note that props will never include opacity.
|
// Note that props will never include opacity.
|
||||||
const { propsForNextShape } = this.instanceState
|
const { propsForNextShape, opacityForNextShape } = this.instanceState
|
||||||
for (const key in initialProps) {
|
for (const key in initialProps) {
|
||||||
if (key in propsForNextShape) {
|
if (key in propsForNextShape) {
|
||||||
if (key === 'url') continue
|
if (key === 'url') continue
|
||||||
|
@ -4693,6 +4728,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
).create({
|
).create({
|
||||||
...partial,
|
...partial,
|
||||||
index,
|
index,
|
||||||
|
opacity: partial.opacity ?? opacityForNextShape,
|
||||||
parentId: partial.parentId ?? focusLayerId,
|
parentId: partial.parentId ?? focusLayerId,
|
||||||
props: 'props' in partial ? { ...initialProps, ...partial.props } : initialProps,
|
props: 'props' in partial ? { ...initialProps, ...partial.props } : initialProps,
|
||||||
})
|
})
|
||||||
|
@ -7757,11 +7793,75 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current opacity. This will effect any selected shapes, or the
|
||||||
|
* next-created shape.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* editor.setOpacity(0.5)
|
||||||
|
* editor.setOpacity(0.5, true)
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param opacity - The opacity to set. Must be a number between 0 and 1
|
||||||
|
* inclusive.
|
||||||
|
* @param ephemeral - Whether the opacity change is ephemeral. Ephemeral
|
||||||
|
* changes don't get added to the undo/redo stack. Defaults to false.
|
||||||
|
* @param squashing - Whether the opacity change will be squashed into the
|
||||||
|
* existing history entry rather than creating a new one. Defaults to false.
|
||||||
|
*/
|
||||||
|
setOpacity(opacity: number, ephemeral = false, squashing = false) {
|
||||||
|
this.history.batch(() => {
|
||||||
|
if (this.isIn('select')) {
|
||||||
|
const {
|
||||||
|
pageState: { selectedIds },
|
||||||
|
} = this
|
||||||
|
|
||||||
|
const shapesToUpdate: TLShape[] = []
|
||||||
|
|
||||||
|
// We can have many deep levels of grouped shape
|
||||||
|
// Making a recursive function to look through all the levels
|
||||||
|
const addShapeById = (id: TLShape['id']) => {
|
||||||
|
const shape = this.getShapeById(id)
|
||||||
|
if (!shape) return
|
||||||
|
if (this.isShapeOfType(shape, GroupShapeUtil)) {
|
||||||
|
const childIds = this.getSortedChildIds(id)
|
||||||
|
for (const childId of childIds) {
|
||||||
|
addShapeById(childId)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shapesToUpdate.push(shape)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedIds.length > 0) {
|
||||||
|
for (const id of selectedIds) {
|
||||||
|
addShapeById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateShapes(
|
||||||
|
shapesToUpdate.map((shape) => {
|
||||||
|
return {
|
||||||
|
id: shape.id,
|
||||||
|
type: shape.type,
|
||||||
|
opacity,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
ephemeral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateInstanceState({ opacityForNextShape: opacity }, ephemeral, squashing)
|
||||||
|
})
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current props (generally styles).
|
* Set the current props (generally styles).
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
|
||||||
* ```ts
|
* ```ts
|
||||||
* editor.setProp('color', 'red')
|
* editor.setProp('color', 'red')
|
||||||
* editor.setProp('color', 'red', true)
|
* editor.setProp('color', 'red', true)
|
||||||
|
@ -7769,67 +7869,43 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
*
|
*
|
||||||
* @param key - The key to set.
|
* @param key - The key to set.
|
||||||
* @param value - The value to set.
|
* @param value - The value to set.
|
||||||
* @param ephemeral - Whether the style is ephemeral. Defaults to false.
|
* @param ephemeral - Whether the style change is ephemeral. Ephemeral
|
||||||
|
* changes don't get added to the undo/redo stack. Defaults to false.
|
||||||
|
* @param squashing - Whether the style change will be squashed into the
|
||||||
|
* existing history entry rather than creating a new one. Defaults to false.
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
setProp(key: TLShapeProp, value: any, ephemeral = false, squashing = false) {
|
setProp(key: TLShapeProp, value: any, ephemeral = false, squashing = false) {
|
||||||
const children: (TLShape | undefined)[] = []
|
|
||||||
// We can have many deep levels of grouped shape
|
|
||||||
// Making a recursive function to look through all the levels
|
|
||||||
const getChildProp = (id: TLShape['id']) => {
|
|
||||||
const childIds = this.getSortedChildIds(id)
|
|
||||||
for (const childId of childIds) {
|
|
||||||
const childShape = this.getShapeById(childId)
|
|
||||||
if (childShape?.type === 'group') {
|
|
||||||
getChildProp(childShape.id)
|
|
||||||
}
|
|
||||||
children.push(childShape)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.history.batch(() => {
|
this.history.batch(() => {
|
||||||
this.updateInstanceState(
|
|
||||||
{
|
|
||||||
propsForNextShape: setPropsForNextShape(this.instanceState.propsForNextShape, {
|
|
||||||
[key]: value,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
ephemeral,
|
|
||||||
squashing
|
|
||||||
)
|
|
||||||
|
|
||||||
if (this.isIn('select')) {
|
if (this.isIn('select')) {
|
||||||
const {
|
const {
|
||||||
pageState: { selectedIds },
|
pageState: { selectedIds },
|
||||||
} = this
|
} = this
|
||||||
|
|
||||||
if (selectedIds.length > 0) {
|
if (selectedIds.length > 0) {
|
||||||
const shapes = compact(
|
const shapesToUpdate: TLShape[] = []
|
||||||
selectedIds.map((id) => {
|
|
||||||
const shape = this.getShapeById(id)
|
// We can have many deep levels of grouped shape
|
||||||
if (shape?.type === 'group') {
|
// Making a recursive function to look through all the levels
|
||||||
const childIds = this.getSortedChildIds(shape.id)
|
const addShapeById = (id: TLShape['id']) => {
|
||||||
for (const childId of childIds) {
|
const shape = this.getShapeById(id)
|
||||||
const childShape = this.getShapeById(childId)
|
if (!shape) return
|
||||||
if (childShape?.type === 'group') {
|
if (this.isShapeOfType(shape, GroupShapeUtil)) {
|
||||||
getChildProp(childShape.id)
|
const childIds = this.getSortedChildIds(id)
|
||||||
}
|
for (const childId of childIds) {
|
||||||
children.push(childShape)
|
addShapeById(childId)
|
||||||
}
|
|
||||||
return children
|
|
||||||
} else {
|
|
||||||
return shape
|
|
||||||
}
|
}
|
||||||
})
|
} else if (shape!.props[key as keyof TLShape['props']] !== undefined) {
|
||||||
)
|
shapesToUpdate.push(shape)
|
||||||
.flat()
|
}
|
||||||
.filter(
|
}
|
||||||
(shape) =>
|
|
||||||
shape!.props[key as keyof TLShape['props']] !== undefined && shape?.type !== 'group'
|
for (const id of selectedIds) {
|
||||||
) as TLShape[]
|
addShapeById(id)
|
||||||
|
}
|
||||||
|
|
||||||
this.updateShapes(
|
this.updateShapes(
|
||||||
shapes.map((shape) => {
|
shapesToUpdate.map((shape) => {
|
||||||
const props = { ...shape.props, [key]: value }
|
const props = { ...shape.props, [key]: value }
|
||||||
if (key === 'color' && 'labelColor' in props) {
|
if (key === 'color' && 'labelColor' in props) {
|
||||||
props.labelColor = 'black'
|
props.labelColor = 'black'
|
||||||
|
@ -7844,10 +7920,10 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
ephemeral
|
ephemeral
|
||||||
)
|
)
|
||||||
|
|
||||||
if (key !== 'color' && key !== 'opacity') {
|
if (key !== 'color') {
|
||||||
const changes: TLShapePartial[] = []
|
const changes: TLShapePartial[] = []
|
||||||
|
|
||||||
for (const shape of shapes) {
|
for (const shape of shapesToUpdate) {
|
||||||
const currentShape = this.getShapeById(shape.id)
|
const currentShape = this.getShapeById(shape.id)
|
||||||
if (!currentShape) continue
|
if (!currentShape) continue
|
||||||
const util = this.getShapeUtil(currentShape)
|
const util = this.getShapeUtil(currentShape)
|
||||||
|
@ -8903,9 +8979,8 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
index: highestIndex,
|
index: highestIndex,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
props: {
|
opacity: 1,
|
||||||
opacity: '1',
|
props: {},
|
||||||
},
|
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
this.reparentShapesById(sortedShapeIds, groupId)
|
this.reparentShapesById(sortedShapeIds, groupId)
|
||||||
|
|
|
@ -69,7 +69,6 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
||||||
|
|
||||||
override defaultProps(): TLArrowShape['props'] {
|
override defaultProps(): TLArrowShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
size: 'm',
|
size: 'm',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
|
|
|
@ -27,7 +27,6 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil<TLBookmarkShape> {
|
||||||
|
|
||||||
override defaultProps(): TLBookmarkShape['props'] {
|
override defaultProps(): TLBookmarkShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
url: '',
|
url: '',
|
||||||
w: DEFAULT_BOOKMARK_WIDTH,
|
w: DEFAULT_BOOKMARK_WIDTH,
|
||||||
h: DEFAULT_BOOKMARK_HEIGHT,
|
h: DEFAULT_BOOKMARK_HEIGHT,
|
||||||
|
|
|
@ -36,7 +36,6 @@ export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
size: 'm',
|
size: 'm',
|
||||||
opacity: '1',
|
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
isClosed: false,
|
isClosed: false,
|
||||||
isPen: false,
|
isPen: false,
|
||||||
|
|
|
@ -42,7 +42,6 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
|
||||||
|
|
||||||
override defaultProps(): TLEmbedShape['props'] {
|
override defaultProps(): TLEmbedShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
w: 300,
|
w: 300,
|
||||||
h: 300,
|
h: 300,
|
||||||
url: '',
|
url: '',
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
||||||
override canEdit = () => true
|
override canEdit = () => true
|
||||||
|
|
||||||
override defaultProps(): TLFrameShape['props'] {
|
override defaultProps(): TLFrameShape['props'] {
|
||||||
return { opacity: '1', w: 160 * 2, h: 90 * 2, name: '' }
|
return { w: 160 * 2, h: 90 * 2, name: '' }
|
||||||
}
|
}
|
||||||
|
|
||||||
override render(shape: TLFrameShape) {
|
override render(shape: TLFrameShape) {
|
||||||
|
@ -56,7 +56,6 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
|
||||||
rect.setAttribute('width', shape.props.w.toString())
|
rect.setAttribute('width', shape.props.w.toString())
|
||||||
rect.setAttribute('height', shape.props.h.toString())
|
rect.setAttribute('height', shape.props.h.toString())
|
||||||
rect.setAttribute('fill', colors.solid)
|
rect.setAttribute('fill', colors.solid)
|
||||||
rect.setAttribute('opacity', shape.props.opacity)
|
|
||||||
rect.setAttribute('stroke', colors.fill.black)
|
rect.setAttribute('stroke', colors.fill.black)
|
||||||
rect.setAttribute('stroke-width', '1')
|
rect.setAttribute('stroke-width', '1')
|
||||||
rect.setAttribute('rx', '1')
|
rect.setAttribute('rx', '1')
|
||||||
|
|
|
@ -55,7 +55,6 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
size: 'm',
|
size: 'm',
|
||||||
opacity: '1',
|
|
||||||
font: 'draw',
|
font: 'draw',
|
||||||
text: '',
|
text: '',
|
||||||
align: 'middle',
|
align: 'middle',
|
||||||
|
|
|
@ -16,7 +16,7 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
|
||||||
canBind = () => false
|
canBind = () => false
|
||||||
|
|
||||||
defaultProps(): TLGroupShape['props'] {
|
defaultProps(): TLGroupShape['props'] {
|
||||||
return { opacity: '1' }
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
getBounds(shape: TLGroupShape): Box2d {
|
getBounds(shape: TLGroupShape): Box2d {
|
||||||
|
|
|
@ -27,7 +27,6 @@ export class HighlightShapeUtil extends ShapeUtil<TLHighlightShape> {
|
||||||
segments: [],
|
segments: [],
|
||||||
color: 'black',
|
color: 'black',
|
||||||
size: 'm',
|
size: 'm',
|
||||||
opacity: '1',
|
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
isPen: false,
|
isPen: false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,6 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
|
||||||
|
|
||||||
override defaultProps(): TLImageShape['props'] {
|
override defaultProps(): TLImageShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
w: 100,
|
w: 100,
|
||||||
h: 100,
|
h: 100,
|
||||||
assetId: null,
|
assetId: null,
|
||||||
|
|
|
@ -36,7 +36,6 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
||||||
|
|
||||||
override defaultProps(): TLLineShape['props'] {
|
override defaultProps(): TLLineShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
size: 'm',
|
size: 'm',
|
||||||
color: 'black',
|
color: 'black',
|
||||||
|
|
|
@ -5,6 +5,7 @@ Object {
|
||||||
"id": "shape:line1",
|
"id": "shape:line1",
|
||||||
"index": "a1",
|
"index": "a1",
|
||||||
"isLocked": false,
|
"isLocked": false,
|
||||||
|
"opacity": 1,
|
||||||
"parentId": "page:id50",
|
"parentId": "page:id50",
|
||||||
"props": Object {
|
"props": Object {
|
||||||
"color": "black",
|
"color": "black",
|
||||||
|
@ -27,7 +28,6 @@ Object {
|
||||||
"y": 0,
|
"y": 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"opacity": "1",
|
|
||||||
"size": "m",
|
"size": "m",
|
||||||
"spline": "line",
|
"spline": "line",
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,7 +21,6 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
||||||
|
|
||||||
defaultProps(): TLNoteShape['props'] {
|
defaultProps(): TLNoteShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
color: 'black',
|
color: 'black',
|
||||||
size: 'm',
|
size: 'm',
|
||||||
text: '',
|
text: '',
|
||||||
|
|
|
@ -26,7 +26,6 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
||||||
|
|
||||||
defaultProps(): TLTextShape['props'] {
|
defaultProps(): TLTextShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
color: 'black',
|
color: 'black',
|
||||||
size: 'm',
|
size: 'm',
|
||||||
w: 8,
|
w: 8,
|
||||||
|
|
|
@ -18,7 +18,6 @@ export class VideoShapeUtil extends BaseBoxShapeUtil<TLVideoShape> {
|
||||||
|
|
||||||
override defaultProps(): TLVideoShape['props'] {
|
override defaultProps(): TLVideoShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
w: 100,
|
w: 100,
|
||||||
h: 100,
|
h: 100,
|
||||||
assetId: null,
|
assetId: null,
|
||||||
|
|
|
@ -12,7 +12,6 @@ export class ArrowShapeTool extends StateNode {
|
||||||
|
|
||||||
styles = [
|
styles = [
|
||||||
'color',
|
'color',
|
||||||
'opacity',
|
|
||||||
'dash',
|
'dash',
|
||||||
'size',
|
'size',
|
||||||
'arrowheadStart',
|
'arrowheadStart',
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { TLStyleType } from '@tldraw/tlschema'
|
|
||||||
import { StateNode } from '../StateNode'
|
import { StateNode } from '../StateNode'
|
||||||
import { Idle } from './children/Idle'
|
import { Idle } from './children/Idle'
|
||||||
import { Pointing } from './children/Pointing'
|
import { Pointing } from './children/Pointing'
|
||||||
|
@ -10,6 +9,4 @@ export abstract class BaseBoxShapeTool extends StateNode {
|
||||||
static children = () => [Idle, Pointing]
|
static children = () => [Idle, Pointing]
|
||||||
|
|
||||||
abstract shapeType: string
|
abstract shapeType: string
|
||||||
|
|
||||||
styles = ['opacity'] as TLStyleType[]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ export class DrawShapeTool extends StateNode {
|
||||||
static initial = 'idle'
|
static initial = 'idle'
|
||||||
static children = () => [Idle, Drawing]
|
static children = () => [Idle, Drawing]
|
||||||
|
|
||||||
styles = ['color', 'opacity', 'dash', 'fill', 'size'] as TLStyleType[]
|
styles = ['color', 'dash', 'fill', 'size'] as TLStyleType[]
|
||||||
|
shapeType = 'draw'
|
||||||
|
|
||||||
onExit = () => {
|
onExit = () => {
|
||||||
const drawingState = this.children!['drawing'] as Drawing
|
const drawingState = this.children!['drawing'] as Drawing
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { TLStyleType } from '@tldraw/tlschema'
|
|
||||||
import { BaseBoxShapeTool } from '../BaseBoxShapeTool/BaseBoxShapeTool'
|
import { BaseBoxShapeTool } from '../BaseBoxShapeTool/BaseBoxShapeTool'
|
||||||
|
|
||||||
export class FrameShapeTool extends BaseBoxShapeTool {
|
export class FrameShapeTool extends BaseBoxShapeTool {
|
||||||
|
@ -6,6 +5,4 @@ export class FrameShapeTool extends BaseBoxShapeTool {
|
||||||
static initial = 'idle'
|
static initial = 'idle'
|
||||||
|
|
||||||
shapeType = 'frame'
|
shapeType = 'frame'
|
||||||
|
|
||||||
styles = ['opacity'] as TLStyleType[]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ export class GeoShapeTool extends StateNode {
|
||||||
|
|
||||||
styles = [
|
styles = [
|
||||||
'color',
|
'color',
|
||||||
'opacity',
|
|
||||||
'dash',
|
'dash',
|
||||||
'fill',
|
'fill',
|
||||||
'size',
|
'size',
|
||||||
|
@ -20,4 +19,5 @@ export class GeoShapeTool extends StateNode {
|
||||||
'align',
|
'align',
|
||||||
'verticalAlign',
|
'verticalAlign',
|
||||||
] as TLStyleType[]
|
] as TLStyleType[]
|
||||||
|
shapeType = 'geo'
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ export class HighlightShapeTool extends StateNode {
|
||||||
static initial = 'idle'
|
static initial = 'idle'
|
||||||
static children = () => [Idle, Drawing]
|
static children = () => [Idle, Drawing]
|
||||||
|
|
||||||
styles = ['color', 'opacity', 'size'] as TLStyleType[]
|
styles = ['color', 'size'] as TLStyleType[]
|
||||||
|
shapeType = 'highlight'
|
||||||
|
|
||||||
onExit = () => {
|
onExit = () => {
|
||||||
const drawingState = this.children!['drawing'] as Drawing
|
const drawingState = this.children!['drawing'] as Drawing
|
||||||
|
|
|
@ -11,5 +11,5 @@ export class LineShapeTool extends StateNode {
|
||||||
|
|
||||||
shapeType = 'line'
|
shapeType = 'line'
|
||||||
|
|
||||||
styles = ['color', 'opacity', 'dash', 'size', 'spline'] as TLStyleType[]
|
styles = ['color', 'dash', 'size', 'spline'] as TLStyleType[]
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,6 @@ export class NoteShapeTool extends StateNode {
|
||||||
static initial = 'idle'
|
static initial = 'idle'
|
||||||
static children = () => [Idle, Pointing]
|
static children = () => [Idle, Pointing]
|
||||||
|
|
||||||
styles = ['color', 'opacity', 'size', 'align', 'verticalAlign', 'font'] as TLStyleType[]
|
styles = ['color', 'size', 'align', 'verticalAlign', 'font'] as TLStyleType[]
|
||||||
|
shapeType = 'note'
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ export class SelectTool extends StateNode {
|
||||||
DraggingHandle,
|
DraggingHandle,
|
||||||
]
|
]
|
||||||
|
|
||||||
styles = ['color', 'opacity', 'dash', 'fill', 'size'] as TLStyleType[]
|
styles = ['color', 'dash', 'fill', 'size'] as TLStyleType[]
|
||||||
|
|
||||||
onExit = () => {
|
onExit = () => {
|
||||||
if (this.editor.pageState.editingId) {
|
if (this.editor.pageState.editingId) {
|
||||||
|
|
|
@ -70,6 +70,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
|
||||||
current: Atom<StateNode | undefined>
|
current: Atom<StateNode | undefined>
|
||||||
type: TLStateNodeType
|
type: TLStateNodeType
|
||||||
readonly styles: TLStyleType[] = []
|
readonly styles: TLStyleType[] = []
|
||||||
|
shapeType?: string
|
||||||
initial?: string
|
initial?: string
|
||||||
children?: Record<string, StateNode>
|
children?: Record<string, StateNode>
|
||||||
parent: StateNode
|
parent: StateNode
|
||||||
|
|
|
@ -9,5 +9,6 @@ export class TextShapeTool extends StateNode {
|
||||||
|
|
||||||
static children = () => [Idle, Pointing]
|
static children = () => [Idle, Pointing]
|
||||||
|
|
||||||
styles = ['color', 'opacity', 'font', 'align', 'size'] as TLStyleType[]
|
styles = ['color', 'font', 'align', 'size'] as TLStyleType[]
|
||||||
|
shapeType = 'text'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { PageRecordType, createShapeId } from '@tldraw/tlschema'
|
import { PageRecordType, createShapeId } from '@tldraw/tlschema'
|
||||||
import { structuredClone } from '@tldraw/utils'
|
import { structuredClone } from '@tldraw/utils'
|
||||||
import { TestEditor } from './TestEditor'
|
import { TestEditor } from './TestEditor'
|
||||||
|
import { TL } from './jsx'
|
||||||
|
|
||||||
let editor: TestEditor
|
let editor: TestEditor
|
||||||
|
|
||||||
|
@ -156,6 +157,98 @@ describe('Editor.setProp', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Editor.opacity', () => {
|
||||||
|
it('should return the current opacity', () => {
|
||||||
|
expect(editor.opacity).toBe(1)
|
||||||
|
editor.setOpacity(0.5)
|
||||||
|
expect(editor.opacity).toBe(0.5)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return opacity for a single selected shape', () => {
|
||||||
|
const { A } = editor.createShapesFromJsx(<TL.geo ref="A" opacity={0.3} x={0} y={0} />)
|
||||||
|
editor.setSelectedIds([A])
|
||||||
|
expect(editor.opacity).toBe(0.3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return opacity for multiple selected shapes', () => {
|
||||||
|
const { A, B } = editor.createShapesFromJsx([
|
||||||
|
<TL.geo ref="A" opacity={0.3} x={0} y={0} />,
|
||||||
|
<TL.geo ref="B" opacity={0.3} x={0} y={0} />,
|
||||||
|
])
|
||||||
|
editor.setSelectedIds([A, B])
|
||||||
|
expect(editor.opacity).toBe(0.3)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return null when multiple selected shapes have different opacity', () => {
|
||||||
|
const { A, B } = editor.createShapesFromJsx([
|
||||||
|
<TL.geo ref="A" opacity={0.3} x={0} y={0} />,
|
||||||
|
<TL.geo ref="B" opacity={0.5} x={0} y={0} />,
|
||||||
|
])
|
||||||
|
editor.setSelectedIds([A, B])
|
||||||
|
expect(editor.opacity).toBe(null)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores the opacity of groups and returns the opacity of their children', () => {
|
||||||
|
const ids = editor.createShapesFromJsx([
|
||||||
|
<TL.group ref="group" x={0} y={0}>
|
||||||
|
<TL.geo ref="A" opacity={0.3} x={0} y={0} />
|
||||||
|
</TL.group>,
|
||||||
|
])
|
||||||
|
editor.setSelectedIds([ids.group])
|
||||||
|
expect(editor.opacity).toBe(0.3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Editor.setOpacity', () => {
|
||||||
|
it('should set opacity for selected shapes', () => {
|
||||||
|
const ids = editor.createShapesFromJsx([
|
||||||
|
<TL.geo ref="A" opacity={0.3} x={0} y={0} />,
|
||||||
|
<TL.geo ref="B" opacity={0.4} x={0} y={0} />,
|
||||||
|
])
|
||||||
|
|
||||||
|
editor.setSelectedIds([ids.A, ids.B])
|
||||||
|
editor.setOpacity(0.5)
|
||||||
|
|
||||||
|
expect(editor.getShapeById(ids.A)!.opacity).toBe(0.5)
|
||||||
|
expect(editor.getShapeById(ids.B)!.opacity).toBe(0.5)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should traverse into groups and set opacity in their children', () => {
|
||||||
|
const ids = editor.createShapesFromJsx([
|
||||||
|
<TL.geo ref="boxA" x={0} y={0} />,
|
||||||
|
<TL.group ref="groupA" x={0} y={0}>
|
||||||
|
<TL.geo ref="boxB" x={0} y={0} />
|
||||||
|
<TL.group ref="groupB" x={0} y={0}>
|
||||||
|
<TL.geo ref="boxC" x={0} y={0} />
|
||||||
|
<TL.geo ref="boxD" x={0} y={0} />
|
||||||
|
</TL.group>
|
||||||
|
</TL.group>,
|
||||||
|
])
|
||||||
|
|
||||||
|
editor.setSelectedIds([ids.groupA])
|
||||||
|
editor.setOpacity(0.5)
|
||||||
|
|
||||||
|
// a wasn't selected...
|
||||||
|
expect(editor.getShapeById(ids.boxA)!.opacity).toBe(1)
|
||||||
|
|
||||||
|
// b, c, & d were within a selected group...
|
||||||
|
expect(editor.getShapeById(ids.boxB)!.opacity).toBe(0.5)
|
||||||
|
expect(editor.getShapeById(ids.boxC)!.opacity).toBe(0.5)
|
||||||
|
expect(editor.getShapeById(ids.boxD)!.opacity).toBe(0.5)
|
||||||
|
|
||||||
|
// groups get skipped
|
||||||
|
expect(editor.getShapeById(ids.groupA)!.opacity).toBe(1)
|
||||||
|
expect(editor.getShapeById(ids.groupB)!.opacity).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('stores opacity on opacityForNextShape', () => {
|
||||||
|
editor.setOpacity(0.5)
|
||||||
|
expect(editor.instanceState.opacityForNextShape).toBe(0.5)
|
||||||
|
editor.setOpacity(0.6)
|
||||||
|
expect(editor.instanceState.opacityForNextShape).toBe(0.6)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('Editor.TickManager', () => {
|
describe('Editor.TickManager', () => {
|
||||||
it('Does not produce NaN values when elapsed is 0', () => {
|
it('Does not produce NaN values when elapsed is 0', () => {
|
||||||
// a helper that calls update pointer velocity with a given elapsed time.
|
// a helper that calls update pointer velocity with a given elapsed time.
|
|
@ -1,5 +1,5 @@
|
||||||
import { act, render, screen } from '@testing-library/react'
|
import { act, render, screen } from '@testing-library/react'
|
||||||
import { TLBaseShape, TLOpacityType, createShapeId } from '@tldraw/tlschema'
|
import { TLBaseShape, createShapeId } from '@tldraw/tlschema'
|
||||||
import { TldrawEditor } from '../TldrawEditor'
|
import { TldrawEditor } from '../TldrawEditor'
|
||||||
import { Canvas } from '../components/Canvas'
|
import { Canvas } from '../components/Canvas'
|
||||||
import { HTMLContainer } from '../components/HTMLContainer'
|
import { HTMLContainer } from '../components/HTMLContainer'
|
||||||
|
@ -137,7 +137,8 @@ describe('<TldrawEditor />', () => {
|
||||||
type: 'geo',
|
type: 'geo',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
props: { geo: 'rectangle', w: 100, h: 100, opacity: '1' },
|
opacity: 1,
|
||||||
|
props: { geo: 'rectangle', w: 100, h: 100 },
|
||||||
})
|
})
|
||||||
|
|
||||||
// Is the shape's component rendering?
|
// Is the shape's component rendering?
|
||||||
|
@ -165,7 +166,6 @@ describe('Custom shapes', () => {
|
||||||
{
|
{
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
opacity: TLOpacityType
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
@ -178,7 +178,6 @@ describe('Custom shapes', () => {
|
||||||
|
|
||||||
override defaultProps(): CardShape['props'] {
|
override defaultProps(): CardShape['props'] {
|
||||||
return {
|
return {
|
||||||
opacity: '1',
|
|
||||||
w: 300,
|
w: 300,
|
||||||
h: 300,
|
h: 300,
|
||||||
}
|
}
|
||||||
|
@ -262,7 +261,8 @@ describe('Custom shapes', () => {
|
||||||
type: 'card',
|
type: 'card',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
props: { w: 100, h: 100, opacity: '1' },
|
opacity: 1,
|
||||||
|
props: { w: 100, h: 100 },
|
||||||
})
|
})
|
||||||
|
|
||||||
// Is the shape's component rendering?
|
// Is the shape's component rendering?
|
||||||
|
|
|
@ -6,6 +6,7 @@ Array [
|
||||||
"id": "shape:boxA",
|
"id": "shape:boxA",
|
||||||
"index": "a1",
|
"index": "a1",
|
||||||
"isLocked": false,
|
"isLocked": false,
|
||||||
|
"opacity": 1,
|
||||||
"parentId": "wahtever",
|
"parentId": "wahtever",
|
||||||
"props": Object {
|
"props": Object {
|
||||||
"align": "middle",
|
"align": "middle",
|
||||||
|
@ -17,7 +18,6 @@ Array [
|
||||||
"growY": 0,
|
"growY": 0,
|
||||||
"h": 100,
|
"h": 100,
|
||||||
"labelColor": "black",
|
"labelColor": "black",
|
||||||
"opacity": "1",
|
|
||||||
"size": "m",
|
"size": "m",
|
||||||
"text": "",
|
"text": "",
|
||||||
"url": "",
|
"url": "",
|
||||||
|
@ -34,6 +34,7 @@ Array [
|
||||||
"id": "shape:boxB",
|
"id": "shape:boxB",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isLocked": false,
|
"isLocked": false,
|
||||||
|
"opacity": 1,
|
||||||
"parentId": "wahtever",
|
"parentId": "wahtever",
|
||||||
"props": Object {
|
"props": Object {
|
||||||
"align": "middle",
|
"align": "middle",
|
||||||
|
@ -45,7 +46,6 @@ Array [
|
||||||
"growY": 0,
|
"growY": 0,
|
||||||
"h": 100,
|
"h": 100,
|
||||||
"labelColor": "black",
|
"labelColor": "black",
|
||||||
"opacity": "1",
|
|
||||||
"size": "m",
|
"size": "m",
|
||||||
"text": "",
|
"text": "",
|
||||||
"url": "",
|
"url": "",
|
||||||
|
@ -62,6 +62,7 @@ Array [
|
||||||
"id": "shape:boxC",
|
"id": "shape:boxC",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isLocked": false,
|
"isLocked": false,
|
||||||
|
"opacity": 1,
|
||||||
"parentId": "wahtever",
|
"parentId": "wahtever",
|
||||||
"props": Object {
|
"props": Object {
|
||||||
"align": "middle",
|
"align": "middle",
|
||||||
|
@ -73,7 +74,6 @@ Array [
|
||||||
"growY": 0,
|
"growY": 0,
|
||||||
"h": 100,
|
"h": 100,
|
||||||
"labelColor": "black",
|
"labelColor": "black",
|
||||||
"opacity": "1",
|
|
||||||
"size": "m",
|
"size": "m",
|
||||||
"text": "",
|
"text": "",
|
||||||
"url": "",
|
"url": "",
|
||||||
|
@ -95,6 +95,7 @@ Array [
|
||||||
"id": "shape:boxA",
|
"id": "shape:boxA",
|
||||||
"index": "a1",
|
"index": "a1",
|
||||||
"isLocked": false,
|
"isLocked": false,
|
||||||
|
"opacity": 1,
|
||||||
"parentId": "wahtever",
|
"parentId": "wahtever",
|
||||||
"props": Object {
|
"props": Object {
|
||||||
"align": "middle",
|
"align": "middle",
|
||||||
|
@ -106,7 +107,6 @@ Array [
|
||||||
"growY": 0,
|
"growY": 0,
|
||||||
"h": 100,
|
"h": 100,
|
||||||
"labelColor": "black",
|
"labelColor": "black",
|
||||||
"opacity": "1",
|
|
||||||
"size": "m",
|
"size": "m",
|
||||||
"text": "",
|
"text": "",
|
||||||
"url": "",
|
"url": "",
|
||||||
|
@ -123,6 +123,7 @@ Array [
|
||||||
"id": "shape:boxB",
|
"id": "shape:boxB",
|
||||||
"index": "a2",
|
"index": "a2",
|
||||||
"isLocked": false,
|
"isLocked": false,
|
||||||
|
"opacity": 1,
|
||||||
"parentId": "wahtever",
|
"parentId": "wahtever",
|
||||||
"props": Object {
|
"props": Object {
|
||||||
"align": "middle",
|
"align": "middle",
|
||||||
|
@ -134,7 +135,6 @@ Array [
|
||||||
"growY": 0,
|
"growY": 0,
|
||||||
"h": 100,
|
"h": 100,
|
||||||
"labelColor": "black",
|
"labelColor": "black",
|
||||||
"opacity": "1",
|
|
||||||
"size": "m",
|
"size": "m",
|
||||||
"text": "",
|
"text": "",
|
||||||
"url": "",
|
"url": "",
|
||||||
|
@ -151,6 +151,7 @@ Array [
|
||||||
"id": "shape:boxC",
|
"id": "shape:boxC",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isLocked": false,
|
"isLocked": false,
|
||||||
|
"opacity": 1,
|
||||||
"parentId": "wahtever",
|
"parentId": "wahtever",
|
||||||
"props": Object {
|
"props": Object {
|
||||||
"align": "middle",
|
"align": "middle",
|
||||||
|
@ -162,7 +163,6 @@ Array [
|
||||||
"growY": 0,
|
"growY": 0,
|
||||||
"h": 100,
|
"h": 100,
|
||||||
"labelColor": "black",
|
"labelColor": "black",
|
||||||
"opacity": "1",
|
|
||||||
"size": "m",
|
"size": "m",
|
||||||
"text": "",
|
"text": "",
|
||||||
"url": "",
|
"url": "",
|
||||||
|
|
|
@ -33,9 +33,9 @@ it('Creates shapes with the current style', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Creates shapes with the current opacity', () => {
|
it('Creates shapes with the current opacity', () => {
|
||||||
editor.setProp('opacity', '0.5')
|
editor.setOpacity(0.5)
|
||||||
editor.createShapes([{ id: ids.box3, type: 'geo' }])
|
editor.createShapes([{ id: ids.box3, type: 'geo' }])
|
||||||
expect(editor.getShapeById<TLGeoShape>(ids.box3)!.props.opacity).toEqual('0.5')
|
expect(editor.getShapeById<TLGeoShape>(ids.box3)!.opacity).toEqual(0.5)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Creates shapes at the correct index', () => {
|
it('Creates shapes at the correct index', () => {
|
||||||
|
|
|
@ -30,13 +30,13 @@ it('updates shapes', () => {
|
||||||
id: ids.box1,
|
id: ids.box1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
type: 'geo',
|
type: 'geo',
|
||||||
|
opacity: 1,
|
||||||
props: {
|
props: {
|
||||||
h: 100,
|
h: 100,
|
||||||
w: 100,
|
w: 100,
|
||||||
color: 'black',
|
color: 'black',
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
opacity: '1',
|
|
||||||
size: 'm',
|
size: 'm',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -49,13 +49,13 @@ it('updates shapes', () => {
|
||||||
id: ids.box1,
|
id: ids.box1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
type: 'geo',
|
type: 'geo',
|
||||||
|
opacity: 1,
|
||||||
props: {
|
props: {
|
||||||
h: 100,
|
h: 100,
|
||||||
w: 100,
|
w: 100,
|
||||||
color: 'black',
|
color: 'black',
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
opacity: '1',
|
|
||||||
size: 'm',
|
size: 'm',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -68,13 +68,13 @@ it('updates shapes', () => {
|
||||||
id: ids.box1,
|
id: ids.box1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
type: 'geo',
|
type: 'geo',
|
||||||
|
opacity: 1,
|
||||||
props: {
|
props: {
|
||||||
h: 100,
|
h: 100,
|
||||||
w: 100,
|
w: 100,
|
||||||
color: 'black',
|
color: 'black',
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
opacity: '1',
|
|
||||||
size: 'm',
|
size: 'm',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,6 +20,7 @@ type CommonProps = {
|
||||||
isLocked?: number
|
isLocked?: number
|
||||||
ref?: string
|
ref?: string
|
||||||
children?: JSX.Element | JSX.Element[]
|
children?: JSX.Element | JSX.Element[]
|
||||||
|
opacity?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShapeByType<Type extends TLDefaultShape['type']> = Extract<TLDefaultShape, { type: Type }>
|
type ShapeByType<Type extends TLDefaultShape['type']> = Extract<TLDefaultShape, { type: Type }>
|
||||||
|
@ -89,7 +90,7 @@ export function shapesFromJsx(shapes: JSX.Element | Array<JSX.Element>) {
|
||||||
if (key === 'x' || key === 'y' || key === 'ref' || key === 'id' || key === 'children') {
|
if (key === 'x' || key === 'y' || key === 'ref' || key === 'id' || key === 'children') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (key === 'rotation' || key === 'isLocked') {
|
if (key === 'rotation' || key === 'isLocked' || key === 'opacity') {
|
||||||
shapePartial[key] = value as any
|
shapePartial[key] = value as any
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ describe('Editor.props', () => {
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
fill: 'none',
|
fill: 'none',
|
||||||
size: 'm',
|
size: 'm',
|
||||||
opacity: '1',
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -32,12 +31,6 @@ describe('Editor.props', () => {
|
||||||
font: 'draw',
|
font: 'draw',
|
||||||
geo: 'rectangle',
|
geo: 'rectangle',
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
// h: 100,
|
|
||||||
// w: 100,
|
|
||||||
// growY: 0,
|
|
||||||
opacity: '1',
|
|
||||||
// text: '',
|
|
||||||
// url: '',
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -53,12 +46,6 @@ describe('Editor.props', () => {
|
||||||
font: 'draw',
|
font: 'draw',
|
||||||
geo: 'rectangle',
|
geo: 'rectangle',
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
// h: 100, // blacklisted
|
|
||||||
// w: 100, // blacklisted
|
|
||||||
// growY: 0, // blacklist
|
|
||||||
opacity: '1',
|
|
||||||
// text: '', // blacklisted
|
|
||||||
// url: '', // blacklisted
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -82,13 +69,7 @@ describe('Editor.props', () => {
|
||||||
size: 'm',
|
size: 'm',
|
||||||
font: 'draw',
|
font: 'draw',
|
||||||
geo: 'rectangle',
|
geo: 'rectangle',
|
||||||
opacity: '1',
|
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
// h: null, // mixed! but also blacklisted
|
|
||||||
// w: null, // mixed! but also blacklisted
|
|
||||||
// growY: 0, // blacklist
|
|
||||||
// text: '', // blacklisted
|
|
||||||
// url: '', // blacklist
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -111,7 +92,6 @@ describe('Editor.props', () => {
|
||||||
align: 'start',
|
align: 'start',
|
||||||
text: 'hello world this is a long sentence that should wrap',
|
text: 'hello world this is a long sentence that should wrap',
|
||||||
w: 100,
|
w: 100,
|
||||||
opacity: '0.5',
|
|
||||||
url: 'https://aol.com',
|
url: 'https://aol.com',
|
||||||
verticalAlign: 'start',
|
verticalAlign: 'start',
|
||||||
},
|
},
|
||||||
|
@ -128,10 +108,7 @@ describe('Editor.props', () => {
|
||||||
geo: null,
|
geo: null,
|
||||||
size: null,
|
size: null,
|
||||||
font: null,
|
font: null,
|
||||||
opacity: null,
|
|
||||||
verticalAlign: null,
|
verticalAlign: null,
|
||||||
// growY: null, // blacklist
|
|
||||||
// url: null, // blacklist
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -405,7 +405,8 @@ describe('When in readonly mode', () => {
|
||||||
type: 'embed',
|
type: 'embed',
|
||||||
x: 100,
|
x: 100,
|
||||||
y: 100,
|
y: 100,
|
||||||
props: { opacity: '1', w: 100, h: 100, url: '', doesResize: false },
|
opacity: 1,
|
||||||
|
props: { w: 100, h: 100, url: '', doesResize: false },
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
editor.setReadOnly(true)
|
editor.setReadOnly(true)
|
||||||
|
|
|
@ -5,6 +5,7 @@ Object {
|
||||||
"id": "shape:lineA",
|
"id": "shape:lineA",
|
||||||
"index": "a3",
|
"index": "a3",
|
||||||
"isLocked": false,
|
"isLocked": false,
|
||||||
|
"opacity": 1,
|
||||||
"parentId": "shape:boxA",
|
"parentId": "shape:boxA",
|
||||||
"props": Object {
|
"props": Object {
|
||||||
"color": "black",
|
"color": "black",
|
||||||
|
@ -13,7 +14,6 @@ Object {
|
||||||
"isClosed": false,
|
"isClosed": false,
|
||||||
"isComplete": false,
|
"isComplete": false,
|
||||||
"isPen": false,
|
"isPen": false,
|
||||||
"opacity": "1",
|
|
||||||
"segments": Array [
|
"segments": Array [
|
||||||
Object {
|
Object {
|
||||||
"points": Array [
|
"points": Array [
|
||||||
|
|
|
@ -20,7 +20,6 @@ const imageWidth = 1200
|
||||||
const imageHeight = 800
|
const imageHeight = 800
|
||||||
|
|
||||||
const imageProps = {
|
const imageProps = {
|
||||||
opacity: '1',
|
|
||||||
assetId: null,
|
assetId: null,
|
||||||
playing: true,
|
playing: true,
|
||||||
url: '',
|
url: '',
|
||||||
|
|
|
@ -1896,10 +1896,10 @@ describe('Group opacity', () => {
|
||||||
it("should set the group's opacity to max even if the selected style panel opacity is lower", () => {
|
it("should set the group's opacity to max even if the selected style panel opacity is lower", () => {
|
||||||
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0)])
|
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0)])
|
||||||
editor.select(ids.boxA, ids.boxB)
|
editor.select(ids.boxA, ids.boxB)
|
||||||
editor.setProp('opacity', '0.5')
|
editor.setOpacity(0.5)
|
||||||
editor.groupShapes()
|
editor.groupShapes()
|
||||||
const group = editor.getShapeById(onlySelectedId())!
|
const group = editor.getShapeById(onlySelectedId())!
|
||||||
assert(editor.isShapeOfType(group, GroupShapeUtil))
|
assert(editor.isShapeOfType(group, GroupShapeUtil))
|
||||||
expect(group.props.opacity).toBe('1')
|
expect(group.opacity).toBe(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -292,7 +292,6 @@ export async function createShapesFromFiles(
|
||||||
props: {
|
props: {
|
||||||
w: asset.props!.w,
|
w: asset.props!.w,
|
||||||
h: asset.props!.h,
|
h: asset.props!.h,
|
||||||
opacity: '1',
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +402,6 @@ export function createEmbedShapeAtPoint(
|
||||||
h: props.height,
|
h: props.height,
|
||||||
doesResize: props.doesResize,
|
doesResize: props.doesResize,
|
||||||
url,
|
url,
|
||||||
opacity: '1',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -430,10 +428,10 @@ export async function createBookmarkShapeAtPoint(editor: Editor, url: string, po
|
||||||
type: 'bookmark',
|
type: 'bookmark',
|
||||||
x: point.x - 150,
|
x: point.x - 150,
|
||||||
y: point.y - 160,
|
y: point.y - 160,
|
||||||
|
opacity: 1,
|
||||||
props: {
|
props: {
|
||||||
assetId: existing.id,
|
assetId: existing.id,
|
||||||
url: existing.props.src!,
|
url: existing.props.src!,
|
||||||
opacity: '1',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
@ -450,9 +448,9 @@ export async function createBookmarkShapeAtPoint(editor: Editor, url: string, po
|
||||||
type: 'bookmark',
|
type: 'bookmark',
|
||||||
x: point.x,
|
x: point.x,
|
||||||
y: point.y,
|
y: point.y,
|
||||||
|
opacity: 1,
|
||||||
props: {
|
props: {
|
||||||
url: url,
|
url: url,
|
||||||
opacity: '1',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -480,9 +478,9 @@ export async function createBookmarkShapeAtPoint(editor: Editor, url: string, po
|
||||||
{
|
{
|
||||||
id: shapeId,
|
id: shapeId,
|
||||||
type: 'bookmark',
|
type: 'bookmark',
|
||||||
|
opacity: 1,
|
||||||
props: {
|
props: {
|
||||||
assetId: assetId,
|
assetId: assetId,
|
||||||
opacity: '1',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
@ -531,11 +529,11 @@ export async function createAssetShapeAtPoint(
|
||||||
type: 'image',
|
type: 'image',
|
||||||
x: point.x - width / 2,
|
x: point.x - width / 2,
|
||||||
y: point.y - height / 2,
|
y: point.y - height / 2,
|
||||||
|
opacity: 1,
|
||||||
props: {
|
props: {
|
||||||
assetId: asset.id,
|
assetId: asset.id,
|
||||||
w: width,
|
w: width,
|
||||||
h: height,
|
h: height,
|
||||||
opacity: '1',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -83,6 +83,7 @@ export function createShapeValidator<Type extends string, Props extends object>(
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
type: Type;
|
type: Type;
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
|
opacity: number;
|
||||||
props: Props;
|
props: Props;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
@ -450,7 +451,7 @@ export const LANGUAGES: readonly [{
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
export const opacityValidator: T.Validator<"0.1" | "0.25" | "0.5" | "0.75" | "1">;
|
export const opacityValidator: T.Validator<number>;
|
||||||
|
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
export const pageIdValidator: T.Validator<TLPageId>;
|
export const pageIdValidator: T.Validator<TLPageId>;
|
||||||
|
@ -500,9 +501,6 @@ export const TL_FONT_TYPES: Set<"draw" | "mono" | "sans" | "serif">;
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const TL_GEO_TYPES: Set<"arrow-down" | "arrow-left" | "arrow-right" | "arrow-up" | "check-box" | "diamond" | "ellipse" | "hexagon" | "octagon" | "oval" | "pentagon" | "rectangle" | "rhombus-2" | "rhombus" | "star" | "trapezoid" | "triangle" | "x-box">;
|
export const TL_GEO_TYPES: Set<"arrow-down" | "arrow-left" | "arrow-right" | "arrow-up" | "check-box" | "diamond" | "ellipse" | "hexagon" | "octagon" | "oval" | "pentagon" | "rectangle" | "rhombus-2" | "rhombus" | "star" | "trapezoid" | "triangle" | "x-box">;
|
||||||
|
|
||||||
// @public (undocumented)
|
|
||||||
export const TL_OPACITY_TYPES: Set<"0.1" | "0.25" | "0.5" | "0.75" | "1">;
|
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const TL_SIZE_TYPES: Set<"l" | "m" | "s" | "xl">;
|
export const TL_SIZE_TYPES: Set<"l" | "m" | "s" | "xl">;
|
||||||
|
|
||||||
|
@ -510,7 +508,7 @@ export const TL_SIZE_TYPES: Set<"l" | "m" | "s" | "xl">;
|
||||||
export const TL_SPLINE_TYPES: Set<"cubic" | "line">;
|
export const TL_SPLINE_TYPES: Set<"cubic" | "line">;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const TL_STYLE_TYPES: Set<"align" | "arrowheadEnd" | "arrowheadStart" | "color" | "dash" | "fill" | "font" | "geo" | "icon" | "labelColor" | "opacity" | "size" | "spline" | "verticalAlign">;
|
export const TL_STYLE_TYPES: Set<"align" | "arrowheadEnd" | "arrowheadStart" | "color" | "dash" | "fill" | "font" | "geo" | "icon" | "labelColor" | "size" | "spline" | "verticalAlign">;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export interface TLAlignStyle extends TLBaseStyle {
|
export interface TLAlignStyle extends TLBaseStyle {
|
||||||
|
@ -552,7 +550,6 @@ export type TLArrowShapeProps = {
|
||||||
fill: TLFillType;
|
fill: TLFillType;
|
||||||
dash: TLDashType;
|
dash: TLDashType;
|
||||||
size: TLSizeType;
|
size: TLSizeType;
|
||||||
opacity: TLOpacityType;
|
|
||||||
arrowheadStart: TLArrowheadType;
|
arrowheadStart: TLArrowheadType;
|
||||||
arrowheadEnd: TLArrowheadType;
|
arrowheadEnd: TLArrowheadType;
|
||||||
font: TLFontType;
|
font: TLFontType;
|
||||||
|
@ -612,6 +609,8 @@ export interface TLBaseShape<Type extends string, Props extends object> extends
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
isLocked: boolean;
|
isLocked: boolean;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
|
opacity: TLOpacityType;
|
||||||
|
// (undocumented)
|
||||||
parentId: TLParentId;
|
parentId: TLParentId;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
props: Props;
|
props: Props;
|
||||||
|
@ -816,7 +815,6 @@ export type TLImageShape = TLBaseShape<'image', TLImageShapeProps>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type TLImageShapeProps = {
|
export type TLImageShapeProps = {
|
||||||
opacity: TLOpacityType;
|
|
||||||
url: string;
|
url: string;
|
||||||
playing: boolean;
|
playing: boolean;
|
||||||
w: number;
|
w: number;
|
||||||
|
@ -848,6 +846,8 @@ export interface TLInstance extends BaseRecord<'instance', TLInstanceId> {
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
isToolLocked: boolean;
|
isToolLocked: boolean;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
|
opacityForNextShape: TLOpacityType;
|
||||||
|
// (undocumented)
|
||||||
propsForNextShape: TLInstancePropsForNextShape;
|
propsForNextShape: TLInstancePropsForNextShape;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
screenBounds: Box2dModel;
|
screenBounds: Box2dModel;
|
||||||
|
@ -938,15 +938,7 @@ export type TLNullableShapeProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export interface TLOpacityStyle extends TLBaseStyle {
|
export type TLOpacityType = number;
|
||||||
// (undocumented)
|
|
||||||
id: TLOpacityType;
|
|
||||||
// (undocumented)
|
|
||||||
type: 'opacity';
|
|
||||||
}
|
|
||||||
|
|
||||||
// @public (undocumented)
|
|
||||||
export type TLOpacityType = SetValue<typeof TL_OPACITY_TYPES>;
|
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
export interface TLPage extends BaseRecord<'page', TLPageId> {
|
export interface TLPage extends BaseRecord<'page', TLPageId> {
|
||||||
|
@ -995,7 +987,7 @@ export type TLShapePartial<T extends TLShape = TLShape> = T extends T ? {
|
||||||
export type TLShapeProp = keyof TLShapeProps;
|
export type TLShapeProp = keyof TLShapeProps;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type TLShapeProps = SmooshedUnionObject<TLShape['props']>;
|
export type TLShapeProps = Identity<UnionToIntersection<TLDefaultShape['props']>>;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export interface TLSizeStyle extends TLBaseStyle {
|
export interface TLSizeStyle extends TLBaseStyle {
|
||||||
|
@ -1052,8 +1044,6 @@ export interface TLStyleCollections {
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
geo: TLGeoStyle[];
|
geo: TLGeoStyle[];
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
opacity: TLOpacityStyle[];
|
|
||||||
// (undocumented)
|
|
||||||
size: TLSizeStyle[];
|
size: TLSizeStyle[];
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
spline: TLSplineStyle[];
|
spline: TLSplineStyle[];
|
||||||
|
@ -1062,7 +1052,7 @@ export interface TLStyleCollections {
|
||||||
}
|
}
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type TLStyleItem = TLAlignStyle | TLArrowheadEndStyle | TLArrowheadStartStyle | TLColorStyle | TLDashStyle | TLFillStyle | TLFontStyle | TLGeoStyle | TLOpacityStyle | TLSizeStyle | TLSplineStyle | TLVerticalAlignStyle;
|
export type TLStyleItem = TLAlignStyle | TLArrowheadEndStyle | TLArrowheadStartStyle | TLColorStyle | TLDashStyle | TLFillStyle | TLFontStyle | TLGeoStyle | TLSizeStyle | TLSplineStyle | TLVerticalAlignStyle;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type TLStyleProps = Pick<TLShapeProps, TLStyleType>;
|
export type TLStyleProps = Pick<TLShapeProps, TLStyleType>;
|
||||||
|
@ -1079,7 +1069,6 @@ export type TLTextShapeProps = {
|
||||||
size: TLSizeType;
|
size: TLSizeType;
|
||||||
font: TLFontType;
|
font: TLFontType;
|
||||||
align: TLAlignType;
|
align: TLAlignType;
|
||||||
opacity: TLOpacityType;
|
|
||||||
w: number;
|
w: number;
|
||||||
text: string;
|
text: string;
|
||||||
scale: number;
|
scale: number;
|
||||||
|
|
|
@ -132,7 +132,7 @@ export function createTLSchema(
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
}).withDefaultProperties(() => ({ x: 0, y: 0, rotation: 0, isLocked: false }))
|
}).withDefaultProperties(() => ({ x: 0, y: 0, rotation: 0, isLocked: false, opacity: 1 }))
|
||||||
|
|
||||||
return StoreSchema.create<TLRecord, TLStoreProps>(
|
return StoreSchema.create<TLRecord, TLStoreProps>(
|
||||||
{
|
{
|
||||||
|
|
|
@ -127,12 +127,7 @@ export {
|
||||||
} from './styles/TLFontStyle'
|
} from './styles/TLFontStyle'
|
||||||
export { TL_GEO_TYPES, geoValidator, type TLGeoStyle, type TLGeoType } from './styles/TLGeoStyle'
|
export { TL_GEO_TYPES, geoValidator, type TLGeoStyle, type TLGeoType } from './styles/TLGeoStyle'
|
||||||
export { iconValidator, type TLIconStyle, type TLIconType } from './styles/TLIconStyle'
|
export { iconValidator, type TLIconStyle, type TLIconType } from './styles/TLIconStyle'
|
||||||
export {
|
export { opacityValidator, type TLOpacityType } from './styles/TLOpacityStyle'
|
||||||
TL_OPACITY_TYPES,
|
|
||||||
opacityValidator,
|
|
||||||
type TLOpacityStyle,
|
|
||||||
type TLOpacityType,
|
|
||||||
} from './styles/TLOpacityStyle'
|
|
||||||
export {
|
export {
|
||||||
TL_SIZE_TYPES,
|
TL_SIZE_TYPES,
|
||||||
sizeValidator,
|
sizeValidator,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { documentMigrations } from './records/TLDocument'
|
||||||
import { instanceMigrations, instanceTypeVersions } from './records/TLInstance'
|
import { instanceMigrations, instanceTypeVersions } from './records/TLInstance'
|
||||||
import { instancePageStateMigrations, instancePageStateVersions } from './records/TLPageState'
|
import { instancePageStateMigrations, instancePageStateVersions } from './records/TLPageState'
|
||||||
import { instancePresenceMigrations, instancePresenceVersions } from './records/TLPresence'
|
import { instancePresenceMigrations, instancePresenceVersions } from './records/TLPresence'
|
||||||
import { TLShape, rootShapeMigrations } from './records/TLShape'
|
import { TLShape, rootShapeMigrations, Versions as rootShapeVersions } from './records/TLShape'
|
||||||
import { arrowShapeMigrations } from './shapes/TLArrowShape'
|
import { arrowShapeMigrations } from './shapes/TLArrowShape'
|
||||||
import { bookmarkShapeMigrations } from './shapes/TLBookmarkShape'
|
import { bookmarkShapeMigrations } from './shapes/TLBookmarkShape'
|
||||||
import { drawShapeMigrations } from './shapes/TLDrawShape'
|
import { drawShapeMigrations } from './shapes/TLDrawShape'
|
||||||
|
@ -1044,6 +1044,72 @@ describe('Adds NoteShape vertical alignment', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('hoist opacity', () => {
|
||||||
|
test('hoists opacity from a shape to another', () => {
|
||||||
|
const { up, down } = rootShapeMigrations.migrators[rootShapeVersions.HoistOpacity]
|
||||||
|
const before = {
|
||||||
|
type: 'myShape',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
props: {
|
||||||
|
color: 'red',
|
||||||
|
opacity: '0.5',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const after = {
|
||||||
|
type: 'myShape',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
opacity: 0.5,
|
||||||
|
props: {
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const afterWithNonMatchingOpacity = {
|
||||||
|
type: 'myShape',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
opacity: 0.6,
|
||||||
|
props: {
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(up(before)).toEqual(after)
|
||||||
|
expect(down(after)).toEqual(before)
|
||||||
|
expect(down(afterWithNonMatchingOpacity)).toEqual(before)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('hoists opacity from propsForNextShape', () => {
|
||||||
|
const { up, down } = instanceMigrations.migrators[instanceTypeVersions.HoistOpacity]
|
||||||
|
const before = {
|
||||||
|
isToolLocked: true,
|
||||||
|
propsForNextShape: {
|
||||||
|
color: 'black',
|
||||||
|
opacity: '0.5',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const after = {
|
||||||
|
isToolLocked: true,
|
||||||
|
opacityForNextShape: 0.5,
|
||||||
|
propsForNextShape: {
|
||||||
|
color: 'black',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const afterWithNonMatchingOpacity = {
|
||||||
|
isToolLocked: true,
|
||||||
|
opacityForNextShape: 0.6,
|
||||||
|
propsForNextShape: {
|
||||||
|
color: 'black',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(up(before)).toEqual(after)
|
||||||
|
expect(down(after)).toEqual(before)
|
||||||
|
expect(down(afterWithNonMatchingOpacity)).toEqual(before)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
/* --- PUT YOUR MIGRATIONS TESTS ABOVE HERE --- */
|
/* --- PUT YOUR MIGRATIONS TESTS ABOVE HERE --- */
|
||||||
|
|
||||||
for (const migrator of allMigrators) {
|
for (const migrator of allMigrators) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { fillValidator } from '../styles/TLFillStyle'
|
||||||
import { fontValidator } from '../styles/TLFontStyle'
|
import { fontValidator } from '../styles/TLFontStyle'
|
||||||
import { geoValidator } from '../styles/TLGeoStyle'
|
import { geoValidator } from '../styles/TLGeoStyle'
|
||||||
import { iconValidator } from '../styles/TLIconStyle'
|
import { iconValidator } from '../styles/TLIconStyle'
|
||||||
import { opacityValidator } from '../styles/TLOpacityStyle'
|
import { opacityValidator, TLOpacityType } from '../styles/TLOpacityStyle'
|
||||||
import { sizeValidator } from '../styles/TLSizeStyle'
|
import { sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { splineValidator } from '../styles/TLSplineStyle'
|
import { splineValidator } from '../styles/TLSplineStyle'
|
||||||
import { verticalAlignValidator } from '../styles/TLVerticalAlignStyle'
|
import { verticalAlignValidator } from '../styles/TLVerticalAlignStyle'
|
||||||
|
@ -34,6 +34,7 @@ export interface TLInstance extends BaseRecord<'instance', TLInstanceId> {
|
||||||
currentPageId: TLPageId
|
currentPageId: TLPageId
|
||||||
followingUserId: string | null
|
followingUserId: string | null
|
||||||
brush: Box2dModel | null
|
brush: Box2dModel | null
|
||||||
|
opacityForNextShape: TLOpacityType
|
||||||
propsForNextShape: TLInstancePropsForNextShape
|
propsForNextShape: TLInstancePropsForNextShape
|
||||||
cursor: TLCursor
|
cursor: TLCursor
|
||||||
scribble: TLScribble | null
|
scribble: TLScribble | null
|
||||||
|
@ -62,13 +63,13 @@ export const instanceTypeValidator: T.Validator<TLInstance> = T.model(
|
||||||
currentPageId: pageIdValidator,
|
currentPageId: pageIdValidator,
|
||||||
followingUserId: T.string.nullable(),
|
followingUserId: T.string.nullable(),
|
||||||
brush: T.boxModel.nullable(),
|
brush: T.boxModel.nullable(),
|
||||||
|
opacityForNextShape: opacityValidator,
|
||||||
propsForNextShape: T.object({
|
propsForNextShape: T.object({
|
||||||
color: colorValidator,
|
color: colorValidator,
|
||||||
labelColor: colorValidator,
|
labelColor: colorValidator,
|
||||||
dash: dashValidator,
|
dash: dashValidator,
|
||||||
fill: fillValidator,
|
fill: fillValidator,
|
||||||
size: sizeValidator,
|
size: sizeValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
font: fontValidator,
|
font: fontValidator,
|
||||||
align: alignValidator,
|
align: alignValidator,
|
||||||
verticalAlign: verticalAlignValidator,
|
verticalAlign: verticalAlignValidator,
|
||||||
|
@ -104,13 +105,14 @@ const Versions = {
|
||||||
AddScribbleDelay: 10,
|
AddScribbleDelay: 10,
|
||||||
RemoveUserId: 11,
|
RemoveUserId: 11,
|
||||||
AddIsPenModeAndIsGridMode: 12,
|
AddIsPenModeAndIsGridMode: 12,
|
||||||
|
HoistOpacity: 13,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export { Versions as instanceTypeVersions }
|
export { Versions as instanceTypeVersions }
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export const instanceMigrations = defineMigrations({
|
export const instanceMigrations = defineMigrations({
|
||||||
currentVersion: Versions.AddIsPenModeAndIsGridMode,
|
currentVersion: Versions.HoistOpacity,
|
||||||
migrators: {
|
migrators: {
|
||||||
[Versions.AddTransparentExportBgs]: {
|
[Versions.AddTransparentExportBgs]: {
|
||||||
up: (instance: TLInstance) => {
|
up: (instance: TLInstance) => {
|
||||||
|
@ -256,6 +258,29 @@ export const instanceMigrations = defineMigrations({
|
||||||
return instance
|
return instance
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[Versions.HoistOpacity]: {
|
||||||
|
up: ({ propsForNextShape: { opacity, ...propsForNextShape }, ...instance }: any) => {
|
||||||
|
return { ...instance, opacityForNextShape: Number(opacity ?? '1'), propsForNextShape }
|
||||||
|
},
|
||||||
|
down: ({ opacityForNextShape: opacity, ...instance }: any) => {
|
||||||
|
return {
|
||||||
|
...instance,
|
||||||
|
propsForNextShape: {
|
||||||
|
...instance.propsForNextShape,
|
||||||
|
opacity:
|
||||||
|
opacity < 0.175
|
||||||
|
? '0.1'
|
||||||
|
: opacity < 0.375
|
||||||
|
? '0.25'
|
||||||
|
: opacity < 0.625
|
||||||
|
? '0.5'
|
||||||
|
: opacity < 0.875
|
||||||
|
? '0.75'
|
||||||
|
: '1',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -267,8 +292,8 @@ export const InstanceRecordType = createRecordType<TLInstance>('instance', {
|
||||||
}).withDefaultProperties(
|
}).withDefaultProperties(
|
||||||
(): Omit<TLInstance, 'typeName' | 'id' | 'currentPageId'> => ({
|
(): Omit<TLInstance, 'typeName' | 'id' | 'currentPageId'> => ({
|
||||||
followingUserId: null,
|
followingUserId: null,
|
||||||
|
opacityForNextShape: 1,
|
||||||
propsForNextShape: {
|
propsForNextShape: {
|
||||||
opacity: '1',
|
|
||||||
color: 'black',
|
color: 'black',
|
||||||
labelColor: 'black',
|
labelColor: 'black',
|
||||||
dash: 'draw',
|
dash: 'draw',
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { TLLineShape } from '../shapes/TLLineShape'
|
||||||
import { TLNoteShape } from '../shapes/TLNoteShape'
|
import { TLNoteShape } from '../shapes/TLNoteShape'
|
||||||
import { TLTextShape } from '../shapes/TLTextShape'
|
import { TLTextShape } from '../shapes/TLTextShape'
|
||||||
import { TLVideoShape } from '../shapes/TLVideoShape'
|
import { TLVideoShape } from '../shapes/TLVideoShape'
|
||||||
import { SmooshedUnionObject } from '../util-types'
|
|
||||||
import { TLPageId } from './TLPage'
|
import { TLPageId } from './TLPage'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,8 +63,15 @@ export type TLShapePartial<T extends TLShape = TLShape> = T extends T
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLShapeId = RecordId<TLUnknownShape>
|
export type TLShapeId = RecordId<TLUnknownShape>
|
||||||
|
|
||||||
|
// evil type shit that will get deleted in the next PR
|
||||||
|
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
|
||||||
|
? I
|
||||||
|
: never
|
||||||
|
|
||||||
|
type Identity<T> = { [K in keyof T]: T[K] }
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLShapeProps = SmooshedUnionObject<TLShape['props']>
|
export type TLShapeProps = Identity<UnionToIntersection<TLDefaultShape['props']>>
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLShapeProp = keyof TLShapeProps
|
export type TLShapeProp = keyof TLShapeProps
|
||||||
|
@ -76,13 +82,14 @@ export type TLParentId = TLPageId | TLShapeId
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLNullableShapeProps = { [K in TLShapeProp]?: TLShapeProps[K] | null }
|
export type TLNullableShapeProps = { [K in TLShapeProp]?: TLShapeProps[K] | null }
|
||||||
|
|
||||||
const Versions = {
|
export const Versions = {
|
||||||
AddIsLocked: 1,
|
AddIsLocked: 1,
|
||||||
|
HoistOpacity: 2,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const rootShapeMigrations = defineMigrations({
|
export const rootShapeMigrations = defineMigrations({
|
||||||
currentVersion: Versions.AddIsLocked,
|
currentVersion: Versions.HoistOpacity,
|
||||||
migrators: {
|
migrators: {
|
||||||
[Versions.AddIsLocked]: {
|
[Versions.AddIsLocked]: {
|
||||||
up: (record) => {
|
up: (record) => {
|
||||||
|
@ -98,6 +105,33 @@ export const rootShapeMigrations = defineMigrations({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[Versions.HoistOpacity]: {
|
||||||
|
up: ({ props: { opacity, ...props }, ...record }) => {
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
opacity: Number(opacity ?? '1'),
|
||||||
|
props,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
down: ({ opacity, ...record }) => {
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
props: {
|
||||||
|
...record.props,
|
||||||
|
opacity:
|
||||||
|
opacity < 0.175
|
||||||
|
? '0.1'
|
||||||
|
: opacity < 0.375
|
||||||
|
? '0.25'
|
||||||
|
: opacity < 0.625
|
||||||
|
? '0.5'
|
||||||
|
: opacity < 0.875
|
||||||
|
? '0.75'
|
||||||
|
: '1',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
||||||
import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
||||||
import { TLFillType, fillValidator } from '../styles/TLFillStyle'
|
import { TLFillType, fillValidator } from '../styles/TLFillStyle'
|
||||||
import { TLFontType, fontValidator } from '../styles/TLFontStyle'
|
import { TLFontType, fontValidator } from '../styles/TLFontStyle'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { SetValue } from '../util-types'
|
import { SetValue } from '../util-types'
|
||||||
import { TLBaseShape, createShapeValidator, shapeIdValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator, shapeIdValidator } from './TLBaseShape'
|
||||||
|
@ -35,7 +34,6 @@ export type TLArrowShapeProps = {
|
||||||
fill: TLFillType
|
fill: TLFillType
|
||||||
dash: TLDashType
|
dash: TLDashType
|
||||||
size: TLSizeType
|
size: TLSizeType
|
||||||
opacity: TLOpacityType
|
|
||||||
arrowheadStart: TLArrowheadType
|
arrowheadStart: TLArrowheadType
|
||||||
arrowheadEnd: TLArrowheadType
|
arrowheadEnd: TLArrowheadType
|
||||||
font: TLFontType
|
font: TLFontType
|
||||||
|
@ -72,7 +70,6 @@ export const arrowShapeValidator: T.Validator<TLArrowShape> = createShapeValidat
|
||||||
fill: fillValidator,
|
fill: fillValidator,
|
||||||
dash: dashValidator,
|
dash: dashValidator,
|
||||||
size: sizeValidator,
|
size: sizeValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
arrowheadStart: arrowheadValidator,
|
arrowheadStart: arrowheadValidator,
|
||||||
arrowheadEnd: arrowheadValidator,
|
arrowheadEnd: arrowheadValidator,
|
||||||
font: fontValidator,
|
font: fontValidator,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { BaseRecord } from '@tldraw/store'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { idValidator } from '../misc/id-validator'
|
import { idValidator } from '../misc/id-validator'
|
||||||
import { TLParentId, TLShapeId } from '../records/TLShape'
|
import { TLParentId, TLShapeId } from '../records/TLShape'
|
||||||
|
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface TLBaseShape<Type extends string, Props extends object>
|
export interface TLBaseShape<Type extends string, Props extends object>
|
||||||
|
@ -13,6 +14,7 @@ export interface TLBaseShape<Type extends string, Props extends object>
|
||||||
index: string
|
index: string
|
||||||
parentId: TLParentId
|
parentId: TLParentId
|
||||||
isLocked: boolean
|
isLocked: boolean
|
||||||
|
opacity: TLOpacityType
|
||||||
props: Props
|
props: Props
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +44,7 @@ export function createShapeValidator<Type extends string, Props extends object>(
|
||||||
parentId: parentIdValidator,
|
parentId: parentIdValidator,
|
||||||
type: T.literal(type),
|
type: T.literal(type),
|
||||||
isLocked: T.boolean,
|
isLocked: T.boolean,
|
||||||
|
opacity: opacityValidator,
|
||||||
props,
|
props,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,10 @@ import { defineMigrations } from '@tldraw/store'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { assetIdValidator } from '../assets/TLBaseAsset'
|
import { assetIdValidator } from '../assets/TLBaseAsset'
|
||||||
import { TLAssetId } from '../records/TLAsset'
|
import { TLAssetId } from '../records/TLAsset'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLBookmarkShapeProps = {
|
export type TLBookmarkShapeProps = {
|
||||||
opacity: TLOpacityType
|
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
assetId: TLAssetId | null
|
assetId: TLAssetId | null
|
||||||
|
@ -21,7 +19,6 @@ export type TLBookmarkShape = TLBaseShape<'bookmark', TLBookmarkShapeProps>
|
||||||
export const bookmarkShapeValidator: T.Validator<TLBookmarkShape> = createShapeValidator(
|
export const bookmarkShapeValidator: T.Validator<TLBookmarkShape> = createShapeValidator(
|
||||||
'bookmark',
|
'bookmark',
|
||||||
T.object({
|
T.object({
|
||||||
opacity: opacityValidator,
|
|
||||||
w: T.nonZeroNumber,
|
w: T.nonZeroNumber,
|
||||||
h: T.nonZeroNumber,
|
h: T.nonZeroNumber,
|
||||||
assetId: assetIdValidator.nullable(),
|
assetId: assetIdValidator.nullable(),
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { Vec2dModel } from '../misc/geometry-types'
|
||||||
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
||||||
import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
||||||
import { TLFillType, fillValidator } from '../styles/TLFillStyle'
|
import { TLFillType, fillValidator } from '../styles/TLFillStyle'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { SetValue } from '../util-types'
|
import { SetValue } from '../util-types'
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
@ -30,7 +29,6 @@ export type TLDrawShapeProps = {
|
||||||
fill: TLFillType
|
fill: TLFillType
|
||||||
dash: TLDashType
|
dash: TLDashType
|
||||||
size: TLSizeType
|
size: TLSizeType
|
||||||
opacity: TLOpacityType
|
|
||||||
segments: TLDrawShapeSegment[]
|
segments: TLDrawShapeSegment[]
|
||||||
isComplete: boolean
|
isComplete: boolean
|
||||||
isClosed: boolean
|
isClosed: boolean
|
||||||
|
@ -48,7 +46,6 @@ export const drawShapeValidator: T.Validator<TLDrawShape> = createShapeValidator
|
||||||
fill: fillValidator,
|
fill: fillValidator,
|
||||||
dash: dashValidator,
|
dash: dashValidator,
|
||||||
size: sizeValidator,
|
size: sizeValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
segments: T.arrayOf(drawShapeSegmentValidator),
|
segments: T.arrayOf(drawShapeSegmentValidator),
|
||||||
isComplete: T.boolean,
|
isComplete: T.boolean,
|
||||||
isClosed: T.boolean,
|
isClosed: T.boolean,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { defineMigrations } from '@tldraw/store'
|
import { defineMigrations } from '@tldraw/store'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
|
||||||
// Only allow multiplayer embeds. If we add additional routes later for example '/help' this won't match
|
// Only allow multiplayer embeds. If we add additional routes later for example '/help' this won't match
|
||||||
|
@ -549,7 +548,6 @@ export type TLEmbedShapePermissions = { [K in keyof typeof embedShapePermissionD
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLEmbedShapeProps = {
|
export type TLEmbedShapeProps = {
|
||||||
opacity: TLOpacityType
|
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
url: string
|
url: string
|
||||||
|
@ -566,7 +564,6 @@ export type TLEmbedShape = TLBaseShape<'embed', TLEmbedShapeProps>
|
||||||
export const embedShapeTypeValidator: T.Validator<TLEmbedShape> = createShapeValidator(
|
export const embedShapeTypeValidator: T.Validator<TLEmbedShape> = createShapeValidator(
|
||||||
'embed',
|
'embed',
|
||||||
T.object({
|
T.object({
|
||||||
opacity: opacityValidator,
|
|
||||||
w: T.nonZeroNumber,
|
w: T.nonZeroNumber,
|
||||||
h: T.nonZeroNumber,
|
h: T.nonZeroNumber,
|
||||||
url: T.string,
|
url: T.string,
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import { defineMigrations } from '@tldraw/store'
|
import { defineMigrations } from '@tldraw/store'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { opacityValidator, TLOpacityType } from '../styles/TLOpacityStyle'
|
|
||||||
import { createShapeValidator, TLBaseShape } from './TLBaseShape'
|
import { createShapeValidator, TLBaseShape } from './TLBaseShape'
|
||||||
|
|
||||||
type TLFrameShapeProps = {
|
type TLFrameShapeProps = {
|
||||||
opacity: TLOpacityType
|
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
name: string
|
name: string
|
||||||
|
@ -17,7 +15,6 @@ export type TLFrameShape = TLBaseShape<'frame', TLFrameShapeProps>
|
||||||
export const frameShapeValidator: T.Validator<TLFrameShape> = createShapeValidator(
|
export const frameShapeValidator: T.Validator<TLFrameShape> = createShapeValidator(
|
||||||
'frame',
|
'frame',
|
||||||
T.object({
|
T.object({
|
||||||
opacity: opacityValidator,
|
|
||||||
w: T.nonZeroNumber,
|
w: T.nonZeroNumber,
|
||||||
h: T.nonZeroNumber,
|
h: T.nonZeroNumber,
|
||||||
name: T.string,
|
name: T.string,
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
||||||
import { TLFillType, fillValidator } from '../styles/TLFillStyle'
|
import { TLFillType, fillValidator } from '../styles/TLFillStyle'
|
||||||
import { TLFontType, fontValidator } from '../styles/TLFontStyle'
|
import { TLFontType, fontValidator } from '../styles/TLFontStyle'
|
||||||
import { TLGeoType, geoValidator } from '../styles/TLGeoStyle'
|
import { TLGeoType, geoValidator } from '../styles/TLGeoStyle'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { TLVerticalAlignType, verticalAlignValidator } from '../styles/TLVerticalAlignStyle'
|
import { TLVerticalAlignType, verticalAlignValidator } from '../styles/TLVerticalAlignStyle'
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
@ -19,7 +18,6 @@ export type TLGeoShapeProps = {
|
||||||
fill: TLFillType
|
fill: TLFillType
|
||||||
dash: TLDashType
|
dash: TLDashType
|
||||||
size: TLSizeType
|
size: TLSizeType
|
||||||
opacity: TLOpacityType
|
|
||||||
font: TLFontType
|
font: TLFontType
|
||||||
align: TLAlignType
|
align: TLAlignType
|
||||||
verticalAlign: TLVerticalAlignType
|
verticalAlign: TLVerticalAlignType
|
||||||
|
@ -43,7 +41,6 @@ export const geoShapeValidator: T.Validator<TLGeoShape> = createShapeValidator(
|
||||||
fill: fillValidator,
|
fill: fillValidator,
|
||||||
dash: dashValidator,
|
dash: dashValidator,
|
||||||
size: sizeValidator,
|
size: sizeValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
font: fontValidator,
|
font: fontValidator,
|
||||||
align: alignValidator,
|
align: alignValidator,
|
||||||
verticalAlign: verticalAlignValidator,
|
verticalAlign: verticalAlignValidator,
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import { defineMigrations } from '@tldraw/store'
|
import { defineMigrations } from '@tldraw/store'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { opacityValidator, TLOpacityType } from '../styles/TLOpacityStyle'
|
|
||||||
import { createShapeValidator, TLBaseShape } from './TLBaseShape'
|
import { createShapeValidator, TLBaseShape } from './TLBaseShape'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLGroupShapeProps = {
|
export type TLGroupShapeProps = { [key in never]: undefined }
|
||||||
opacity: TLOpacityType
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLGroupShape = TLBaseShape<'group', TLGroupShapeProps>
|
export type TLGroupShape = TLBaseShape<'group', TLGroupShapeProps>
|
||||||
|
@ -14,9 +11,7 @@ export type TLGroupShape = TLBaseShape<'group', TLGroupShapeProps>
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const groupShapeValidator: T.Validator<TLGroupShape> = createShapeValidator(
|
export const groupShapeValidator: T.Validator<TLGroupShape> = createShapeValidator(
|
||||||
'group',
|
'group',
|
||||||
T.object({
|
T.object({})
|
||||||
opacity: opacityValidator,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { defineMigrations } from '@tldraw/store'
|
import { defineMigrations } from '@tldraw/store'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
import { TLDrawShapeSegment, drawShapeSegmentValidator } from './TLDrawShape'
|
import { TLDrawShapeSegment, drawShapeSegmentValidator } from './TLDrawShape'
|
||||||
|
@ -10,7 +9,6 @@ import { TLDrawShapeSegment, drawShapeSegmentValidator } from './TLDrawShape'
|
||||||
export type TLHighlightShapeProps = {
|
export type TLHighlightShapeProps = {
|
||||||
color: TLColorType
|
color: TLColorType
|
||||||
size: TLSizeType
|
size: TLSizeType
|
||||||
opacity: TLOpacityType
|
|
||||||
segments: TLDrawShapeSegment[]
|
segments: TLDrawShapeSegment[]
|
||||||
isComplete: boolean
|
isComplete: boolean
|
||||||
isPen: boolean
|
isPen: boolean
|
||||||
|
@ -25,7 +23,6 @@ export const highlightShapeValidator: T.Validator<TLHighlightShape> = createShap
|
||||||
T.object({
|
T.object({
|
||||||
color: colorValidator,
|
color: colorValidator,
|
||||||
size: sizeValidator,
|
size: sizeValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
segments: T.arrayOf(drawShapeSegmentValidator),
|
segments: T.arrayOf(drawShapeSegmentValidator),
|
||||||
isComplete: T.boolean,
|
isComplete: T.boolean,
|
||||||
isPen: T.boolean,
|
isPen: T.boolean,
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { T } from '@tldraw/validate'
|
||||||
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
||||||
import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
||||||
import { TLIconType, iconValidator } from '../styles/TLIconStyle'
|
import { TLIconType, iconValidator } from '../styles/TLIconStyle'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
|
||||||
|
@ -13,7 +12,6 @@ export type TLIconShapeProps = {
|
||||||
icon: TLIconType
|
icon: TLIconType
|
||||||
dash: TLDashType
|
dash: TLDashType
|
||||||
color: TLColorType
|
color: TLColorType
|
||||||
opacity: TLOpacityType
|
|
||||||
scale: number
|
scale: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +26,6 @@ export const iconShapeValidator: T.Validator<TLIconShape> = createShapeValidator
|
||||||
icon: iconValidator,
|
icon: iconValidator,
|
||||||
dash: dashValidator,
|
dash: dashValidator,
|
||||||
color: colorValidator,
|
color: colorValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
scale: T.number,
|
scale: T.number,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { T } from '@tldraw/validate'
|
||||||
import { assetIdValidator } from '../assets/TLBaseAsset'
|
import { assetIdValidator } from '../assets/TLBaseAsset'
|
||||||
import { Vec2dModel } from '../misc/geometry-types'
|
import { Vec2dModel } from '../misc/geometry-types'
|
||||||
import { TLAssetId } from '../records/TLAsset'
|
import { TLAssetId } from '../records/TLAsset'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
|
@ -14,7 +13,6 @@ export type TLImageCrop = {
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLImageShapeProps = {
|
export type TLImageShapeProps = {
|
||||||
opacity: TLOpacityType
|
|
||||||
url: string
|
url: string
|
||||||
playing: boolean
|
playing: boolean
|
||||||
w: number
|
w: number
|
||||||
|
@ -35,7 +33,6 @@ export const cropValidator = T.object({
|
||||||
export const imageShapeValidator: T.Validator<TLImageShape> = createShapeValidator(
|
export const imageShapeValidator: T.Validator<TLImageShape> = createShapeValidator(
|
||||||
'image',
|
'image',
|
||||||
T.object({
|
T.object({
|
||||||
opacity: opacityValidator,
|
|
||||||
w: T.nonZeroNumber,
|
w: T.nonZeroNumber,
|
||||||
h: T.nonZeroNumber,
|
h: T.nonZeroNumber,
|
||||||
playing: T.boolean,
|
playing: T.boolean,
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { T } from '@tldraw/validate'
|
||||||
import { TLHandle, handleValidator } from '../misc/TLHandle'
|
import { TLHandle, handleValidator } from '../misc/TLHandle'
|
||||||
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
||||||
import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
import { TLDashType, dashValidator } from '../styles/TLDashStyle'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { TLSplineType, splineValidator } from '../styles/TLSplineStyle'
|
import { TLSplineType, splineValidator } from '../styles/TLSplineStyle'
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
@ -13,7 +12,6 @@ export type TLLineShapeProps = {
|
||||||
color: TLColorType
|
color: TLColorType
|
||||||
dash: TLDashType
|
dash: TLDashType
|
||||||
size: TLSizeType
|
size: TLSizeType
|
||||||
opacity: TLOpacityType
|
|
||||||
spline: TLSplineType
|
spline: TLSplineType
|
||||||
handles: {
|
handles: {
|
||||||
[key: string]: TLHandle
|
[key: string]: TLHandle
|
||||||
|
@ -30,7 +28,6 @@ export const lineShapeValidator: T.Validator<TLLineShape> = createShapeValidator
|
||||||
color: colorValidator,
|
color: colorValidator,
|
||||||
dash: dashValidator,
|
dash: dashValidator,
|
||||||
size: sizeValidator,
|
size: sizeValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
spline: splineValidator,
|
spline: splineValidator,
|
||||||
handles: T.dict(T.string, handleValidator),
|
handles: T.dict(T.string, handleValidator),
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { T } from '@tldraw/validate'
|
||||||
import { TLAlignType, alignValidator } from '../styles/TLAlignStyle'
|
import { TLAlignType, alignValidator } from '../styles/TLAlignStyle'
|
||||||
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
||||||
import { TLFontType, fontValidator } from '../styles/TLFontStyle'
|
import { TLFontType, fontValidator } from '../styles/TLFontStyle'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { TLVerticalAlignType, verticalAlignValidator } from '../styles/TLVerticalAlignStyle'
|
import { TLVerticalAlignType, verticalAlignValidator } from '../styles/TLVerticalAlignStyle'
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
@ -15,7 +14,6 @@ export type TLNoteShapeProps = {
|
||||||
font: TLFontType
|
font: TLFontType
|
||||||
align: TLAlignType
|
align: TLAlignType
|
||||||
verticalAlign: TLVerticalAlignType
|
verticalAlign: TLVerticalAlignType
|
||||||
opacity: TLOpacityType
|
|
||||||
growY: number
|
growY: number
|
||||||
url: string
|
url: string
|
||||||
text: string
|
text: string
|
||||||
|
@ -33,7 +31,6 @@ export const noteShapeValidator: T.Validator<TLNoteShape> = createShapeValidator
|
||||||
font: fontValidator,
|
font: fontValidator,
|
||||||
align: alignValidator,
|
align: alignValidator,
|
||||||
verticalAlign: verticalAlignValidator,
|
verticalAlign: verticalAlignValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
growY: T.positiveNumber,
|
growY: T.positiveNumber,
|
||||||
url: T.string,
|
url: T.string,
|
||||||
text: T.string,
|
text: T.string,
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { T } from '@tldraw/validate'
|
||||||
import { TLAlignType, alignValidator } from '../styles/TLAlignStyle'
|
import { TLAlignType, alignValidator } from '../styles/TLAlignStyle'
|
||||||
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
import { TLColorType, colorValidator } from '../styles/TLColorStyle'
|
||||||
import { TLFontType, fontValidator } from '../styles/TLFontStyle'
|
import { TLFontType, fontValidator } from '../styles/TLFontStyle'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
import { TLSizeType, sizeValidator } from '../styles/TLSizeStyle'
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
|
||||||
|
@ -13,7 +12,6 @@ export type TLTextShapeProps = {
|
||||||
size: TLSizeType
|
size: TLSizeType
|
||||||
font: TLFontType
|
font: TLFontType
|
||||||
align: TLAlignType
|
align: TLAlignType
|
||||||
opacity: TLOpacityType
|
|
||||||
w: number
|
w: number
|
||||||
text: string
|
text: string
|
||||||
scale: number
|
scale: number
|
||||||
|
@ -31,7 +29,6 @@ export const textShapeValidator: T.Validator<TLTextShape> = createShapeValidator
|
||||||
size: sizeValidator,
|
size: sizeValidator,
|
||||||
font: fontValidator,
|
font: fontValidator,
|
||||||
align: alignValidator,
|
align: alignValidator,
|
||||||
opacity: opacityValidator,
|
|
||||||
w: T.nonZeroNumber,
|
w: T.nonZeroNumber,
|
||||||
text: T.string,
|
text: T.string,
|
||||||
scale: T.nonZeroNumber,
|
scale: T.nonZeroNumber,
|
||||||
|
|
|
@ -2,12 +2,10 @@ import { defineMigrations } from '@tldraw/store'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { assetIdValidator } from '../assets/TLBaseAsset'
|
import { assetIdValidator } from '../assets/TLBaseAsset'
|
||||||
import { TLAssetId } from '../records/TLAsset'
|
import { TLAssetId } from '../records/TLAsset'
|
||||||
import { TLOpacityType, opacityValidator } from '../styles/TLOpacityStyle'
|
|
||||||
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
import { TLBaseShape, createShapeValidator } from './TLBaseShape'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLVideoShapeProps = {
|
export type TLVideoShapeProps = {
|
||||||
opacity: TLOpacityType
|
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
time: number
|
time: number
|
||||||
|
@ -23,7 +21,6 @@ export type TLVideoShape = TLBaseShape<'video', TLVideoShapeProps>
|
||||||
export const videoShapeValidator: T.Validator<TLVideoShape> = createShapeValidator(
|
export const videoShapeValidator: T.Validator<TLVideoShape> = createShapeValidator(
|
||||||
'video',
|
'video',
|
||||||
T.object({
|
T.object({
|
||||||
opacity: opacityValidator,
|
|
||||||
w: T.nonZeroNumber,
|
w: T.nonZeroNumber,
|
||||||
h: T.nonZeroNumber,
|
h: T.nonZeroNumber,
|
||||||
time: T.number,
|
time: T.number,
|
||||||
|
|
|
@ -7,7 +7,6 @@ export const TL_STYLE_TYPES = new Set([
|
||||||
'dash',
|
'dash',
|
||||||
'fill',
|
'fill',
|
||||||
'size',
|
'size',
|
||||||
'opacity',
|
|
||||||
'font',
|
'font',
|
||||||
'align',
|
'align',
|
||||||
'verticalAlign',
|
'verticalAlign',
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { SetValue } from '../util-types'
|
|
||||||
import { TLBaseStyle } from './TLBaseStyle'
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export const TL_OPACITY_TYPES = new Set(['0.1', '0.25', '0.5', '0.75', '1'] as const)
|
export type TLOpacityType = number
|
||||||
|
|
||||||
/** @public */
|
|
||||||
export type TLOpacityType = SetValue<typeof TL_OPACITY_TYPES>
|
|
||||||
|
|
||||||
/** @public */
|
|
||||||
export interface TLOpacityStyle extends TLBaseStyle {
|
|
||||||
id: TLOpacityType
|
|
||||||
type: 'opacity'
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const opacityValidator = T.setEnum(TL_OPACITY_TYPES)
|
export const opacityValidator = T.number.check((n) => {
|
||||||
|
if (n < 0 || n > 1) {
|
||||||
|
throw new T.ValidationError('Opacity must be between 0 and 1')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
TLDashStyle,
|
TLDashStyle,
|
||||||
TLFillStyle,
|
TLFillStyle,
|
||||||
TLFontStyle,
|
TLFontStyle,
|
||||||
TLOpacityStyle,
|
|
||||||
TLSizeStyle,
|
TLSizeStyle,
|
||||||
TLStyleType,
|
TLStyleType,
|
||||||
} from '..'
|
} from '..'
|
||||||
|
@ -20,7 +19,6 @@ export type TLStyleItem =
|
||||||
| TLFillStyle
|
| TLFillStyle
|
||||||
| TLDashStyle
|
| TLDashStyle
|
||||||
| TLSizeStyle
|
| TLSizeStyle
|
||||||
| TLOpacityStyle
|
|
||||||
| TLFontStyle
|
| TLFontStyle
|
||||||
| TLAlignStyle
|
| TLAlignStyle
|
||||||
| TLVerticalAlignStyle
|
| TLVerticalAlignStyle
|
||||||
|
@ -36,7 +34,6 @@ export interface TLStyleCollections {
|
||||||
fill: TLFillStyle[]
|
fill: TLFillStyle[]
|
||||||
dash: TLDashStyle[]
|
dash: TLDashStyle[]
|
||||||
size: TLSizeStyle[]
|
size: TLSizeStyle[]
|
||||||
opacity: TLOpacityStyle[]
|
|
||||||
font: TLFontStyle[]
|
font: TLFontStyle[]
|
||||||
align: TLAlignStyle[]
|
align: TLAlignStyle[]
|
||||||
verticalAlign: TLVerticalAlignStyle[]
|
verticalAlign: TLVerticalAlignStyle[]
|
||||||
|
@ -44,7 +41,6 @@ export interface TLStyleCollections {
|
||||||
arrowheadStart: TLArrowheadStartStyle[]
|
arrowheadStart: TLArrowheadStartStyle[]
|
||||||
arrowheadEnd: TLArrowheadEndStyle[]
|
arrowheadEnd: TLArrowheadEndStyle[]
|
||||||
spline: TLSplineStyle[]
|
spline: TLSplineStyle[]
|
||||||
// icon: TLIconStyle[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
|
|
|
@ -1,11 +1,2 @@
|
||||||
/** @public */
|
|
||||||
export type SmooshedUnionObject<T> = {
|
|
||||||
[K in T extends infer P ? keyof P : never]: T extends infer P
|
|
||||||
? K extends keyof P
|
|
||||||
? P[K]
|
|
||||||
: never
|
|
||||||
: never
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type SetValue<T extends Set<any>> = T extends Set<infer U> ? U : never
|
export type SetValue<T extends Set<any>> = T extends Set<infer U> ? U : never
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Editor, TLNullableShapeProps, TLStyleItem, useEditor } from '@tldraw/editor'
|
import { Editor, TLNullableShapeProps, TLStyleItem, useEditor } from '@tldraw/editor'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
|
|
||||||
|
import { minBy } from '@tldraw/utils'
|
||||||
import { useValue } from 'signia-react'
|
import { useValue } from 'signia-react'
|
||||||
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
import { useTranslation } from '../../hooks/useTranslation/useTranslation'
|
||||||
import { Button } from '../primitives/Button'
|
import { Button } from '../primitives/Button'
|
||||||
|
@ -18,6 +19,10 @@ export const StylePanel = function StylePanel({ isMobile }: StylePanelProps) {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
|
|
||||||
const props = useValue('props', () => editor.props, [editor])
|
const props = useValue('props', () => editor.props, [editor])
|
||||||
|
const opacity = useValue('opacity', () => editor.opacity, [editor])
|
||||||
|
const toolShapeType = useValue('toolShapeType', () => editor.root.current.value?.shapeType, [
|
||||||
|
editor,
|
||||||
|
])
|
||||||
|
|
||||||
const handlePointerOut = useCallback(() => {
|
const handlePointerOut = useCallback(() => {
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
|
@ -25,9 +30,9 @@ export const StylePanel = function StylePanel({ isMobile }: StylePanelProps) {
|
||||||
}
|
}
|
||||||
}, [editor, isMobile])
|
}, [editor, isMobile])
|
||||||
|
|
||||||
if (!props) return null
|
if (!props && !toolShapeType) return null
|
||||||
|
|
||||||
const { geo, arrowheadEnd, arrowheadStart, spline, font } = props
|
const { geo, arrowheadEnd, arrowheadStart, spline, font } = props ?? {}
|
||||||
|
|
||||||
const hideGeo = geo === undefined
|
const hideGeo = geo === undefined
|
||||||
const hideArrowHeads = arrowheadEnd === undefined && arrowheadStart === undefined
|
const hideArrowHeads = arrowheadEnd === undefined && arrowheadStart === undefined
|
||||||
|
@ -36,13 +41,13 @@ export const StylePanel = function StylePanel({ isMobile }: StylePanelProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tlui-style-panel" data-ismobile={isMobile} onPointerLeave={handlePointerOut}>
|
<div className="tlui-style-panel" data-ismobile={isMobile} onPointerLeave={handlePointerOut}>
|
||||||
<CommonStylePickerSet props={props} />
|
<CommonStylePickerSet props={props ?? {}} opacity={opacity} />
|
||||||
{!hideText && <TextStylePickerSet props={props} />}
|
{!hideText && <TextStylePickerSet props={props ?? {}} />}
|
||||||
{!(hideGeo && hideArrowHeads && hideSpline) && (
|
{!(hideGeo && hideArrowHeads && hideSpline) && (
|
||||||
<div className="tlui-style-panel__section" aria-label="style panel styles">
|
<div className="tlui-style-panel__section" aria-label="style panel styles">
|
||||||
<GeoStylePickerSet props={props} />
|
<GeoStylePickerSet props={props ?? {}} />
|
||||||
<ArrowheadStylePickerSet props={props} />
|
<ArrowheadStylePickerSet props={props ?? {}} />
|
||||||
<SplineStylePickerSet props={props} />
|
<SplineStylePickerSet props={props ?? {}} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,7 +70,15 @@ function useStyleChangeCallback() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function CommonStylePickerSet({ props }: { props: TLNullableShapeProps }) {
|
const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1] as const
|
||||||
|
|
||||||
|
function CommonStylePickerSet({
|
||||||
|
props,
|
||||||
|
opacity,
|
||||||
|
}: {
|
||||||
|
props: TLNullableShapeProps
|
||||||
|
opacity: number | null
|
||||||
|
}) {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
const msg = useTranslation()
|
const msg = useTranslation()
|
||||||
|
|
||||||
|
@ -73,14 +86,14 @@ function CommonStylePickerSet({ props }: { props: TLNullableShapeProps }) {
|
||||||
|
|
||||||
const handleOpacityValueChange = React.useCallback(
|
const handleOpacityValueChange = React.useCallback(
|
||||||
(value: number, ephemeral: boolean) => {
|
(value: number, ephemeral: boolean) => {
|
||||||
const item = styles.opacity[value]
|
const item = tldrawSupportedOpacities[value]
|
||||||
editor.setProp(item.type, item.id, ephemeral)
|
editor.setOpacity(item, ephemeral)
|
||||||
editor.isChangingStyle = true
|
editor.isChangingStyle = true
|
||||||
},
|
},
|
||||||
[editor]
|
[editor]
|
||||||
)
|
)
|
||||||
|
|
||||||
const { color, fill, dash, size, opacity } = props
|
const { color, fill, dash, size } = props
|
||||||
|
|
||||||
if (
|
if (
|
||||||
color === undefined &&
|
color === undefined &&
|
||||||
|
@ -94,7 +107,14 @@ function CommonStylePickerSet({ props }: { props: TLNullableShapeProps }) {
|
||||||
|
|
||||||
const showPickers = fill !== undefined || dash !== undefined || size !== undefined
|
const showPickers = fill !== undefined || dash !== undefined || size !== undefined
|
||||||
|
|
||||||
const opacityIndex = styles.opacity.findIndex((s) => s.id === opacity)
|
const opacityIndex =
|
||||||
|
opacity === null
|
||||||
|
? -1
|
||||||
|
: tldrawSupportedOpacities.indexOf(
|
||||||
|
minBy(tldrawSupportedOpacities, (supportedOpacity) =>
|
||||||
|
Math.abs(supportedOpacity - opacity)
|
||||||
|
)!
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -112,10 +132,10 @@ function CommonStylePickerSet({ props }: { props: TLNullableShapeProps }) {
|
||||||
{opacity === undefined ? null : (
|
{opacity === undefined ? null : (
|
||||||
<Slider
|
<Slider
|
||||||
data-testid="style.opacity"
|
data-testid="style.opacity"
|
||||||
value={opacityIndex >= 0 ? opacityIndex : styles.opacity.length - 1}
|
value={opacityIndex >= 0 ? opacityIndex : tldrawSupportedOpacities.length - 1}
|
||||||
label={opacity ? `opacity-style.${opacity}` : 'style-panel.mixed'}
|
label={opacity ? `opacity-style.${opacity}` : 'style-panel.mixed'}
|
||||||
onValueChange={handleOpacityValueChange}
|
onValueChange={handleOpacityValueChange}
|
||||||
steps={styles.opacity.length - 1}
|
steps={tldrawSupportedOpacities.length - 1}
|
||||||
title={msg('style-panel.opacity')}
|
title={msg('style-panel.opacity')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -79,6 +79,7 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
||||||
y: element.y,
|
y: element.y,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
isLocked: element.locked,
|
isLocked: element.locked,
|
||||||
|
opacity: getOpacity(element.opacity),
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
if (element.angle !== 0) {
|
if (element.angle !== 0) {
|
||||||
|
@ -121,7 +122,6 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
||||||
type: 'geo',
|
type: 'geo',
|
||||||
props: {
|
props: {
|
||||||
geo: element.type,
|
geo: element.type,
|
||||||
opacity: getOpacity(element.opacity),
|
|
||||||
url: element.link ?? '',
|
url: element.link ?? '',
|
||||||
w: element.width,
|
w: element.width,
|
||||||
h: element.height,
|
h: element.height,
|
||||||
|
@ -142,7 +142,6 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
||||||
props: {
|
props: {
|
||||||
dash: getDash(element),
|
dash: getDash(element),
|
||||||
size: strokeWidthsToSizes[element.strokeWidth],
|
size: strokeWidthsToSizes[element.strokeWidth],
|
||||||
opacity: getOpacity(element.opacity),
|
|
||||||
color: colorsToColors[element.strokeColor] ?? 'black',
|
color: colorsToColors[element.strokeColor] ?? 'black',
|
||||||
segments: [
|
segments: [
|
||||||
{
|
{
|
||||||
|
@ -169,7 +168,6 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
||||||
props: {
|
props: {
|
||||||
dash: getDash(element),
|
dash: getDash(element),
|
||||||
size: strokeWidthsToSizes[element.strokeWidth],
|
size: strokeWidthsToSizes[element.strokeWidth],
|
||||||
opacity: getOpacity(element.opacity),
|
|
||||||
color: colorsToColors[element.strokeColor] ?? 'black',
|
color: colorsToColors[element.strokeColor] ?? 'black',
|
||||||
spline: element.roundness ? 'cubic' : 'line',
|
spline: element.roundness ? 'cubic' : 'line',
|
||||||
handles: {
|
handles: {
|
||||||
|
@ -234,7 +232,6 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
||||||
text,
|
text,
|
||||||
bend: getBend(element, start, end),
|
bend: getBend(element, start, end),
|
||||||
dash: getDash(element),
|
dash: getDash(element),
|
||||||
opacity: getOpacity(element.opacity),
|
|
||||||
size: strokeWidthsToSizes[element.strokeWidth] ?? 'm',
|
size: strokeWidthsToSizes[element.strokeWidth] ?? 'm',
|
||||||
color: colorsToColors[element.strokeColor] ?? 'black',
|
color: colorsToColors[element.strokeColor] ?? 'black',
|
||||||
start: startTargetId
|
start: startTargetId
|
||||||
|
@ -277,7 +274,6 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
||||||
size,
|
size,
|
||||||
scale,
|
scale,
|
||||||
font: fontFamilyToFontType[element.fontFamily] ?? 'draw',
|
font: fontFamilyToFontType[element.fontFamily] ?? 'draw',
|
||||||
opacity: getOpacity(element.opacity),
|
|
||||||
color: colorsToColors[element.strokeColor] ?? 'black',
|
color: colorsToColors[element.strokeColor] ?? 'black',
|
||||||
text: element.text,
|
text: element.text,
|
||||||
align: textAlignToAlignTypes[element.textAlign],
|
align: textAlignToAlignTypes[element.textAlign],
|
||||||
|
@ -308,7 +304,6 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
||||||
...base,
|
...base,
|
||||||
type: 'image',
|
type: 'image',
|
||||||
props: {
|
props: {
|
||||||
opacity: getOpacity(element.opacity),
|
|
||||||
w: element.width,
|
w: element.width,
|
||||||
h: element.height,
|
h: element.height,
|
||||||
assetId,
|
assetId,
|
||||||
|
@ -370,16 +365,16 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
||||||
const getOpacity = (opacity: number): TLOpacityType => {
|
const getOpacity = (opacity: number): TLOpacityType => {
|
||||||
const t = opacity / 100
|
const t = opacity / 100
|
||||||
if (t < 0.2) {
|
if (t < 0.2) {
|
||||||
return '0.1'
|
return 0.1
|
||||||
} else if (t < 0.4) {
|
} else if (t < 0.4) {
|
||||||
return '0.25'
|
return 0.25
|
||||||
} else if (t < 0.6) {
|
} else if (t < 0.6) {
|
||||||
return '0.5'
|
return 0.5
|
||||||
} else if (t < 0.8) {
|
} else if (t < 0.8) {
|
||||||
return '0.75'
|
return 0.75
|
||||||
}
|
}
|
||||||
|
|
||||||
return '1'
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const strokeWidthsToSizes: Record<number, TLSizeType> = {
|
const strokeWidthsToSizes: Record<number, TLSizeType> = {
|
||||||
|
|
Ładowanie…
Reference in New Issue