[Snapping 1/5] Validation & strict types for fractional indexes (#2827)
Currently, we type our fractional index keys as `string` and don't have any validation for them. I'm touching some of this code for my work on line handles and wanted to change that: - fractional indexes are now `IndexKey`s, not `string`s. `IndexKey`s have a brand property so can't be used interchangeably with strings (like our IDs) - There's a new `T.indexKey` validator which we can use in our validations to make sure we don't end up with nonsense keys. This PR is part of a series - please don't merge it until the things before it have landed! 1. #2827 (you are here) 2. #2831 3. #2793 4. #2841 5. #2845 ### Change Type - [x] `patch` — Bug fix ### Test Plan 1. Mostly relying on unit & end to end tests here - no user facing changes. - [x] Unit Tests
This commit is contained in:
parent
fb00358a53
commit
93c2ed615c
40 changed files with 989 additions and 681 deletions
|
@ -17,6 +17,7 @@ import { EMPTY_ARRAY } from '@tldraw/state';
|
|||
import { EventEmitter } from 'eventemitter3';
|
||||
import { HistoryEntry } from '@tldraw/store';
|
||||
import { HTMLProps } from 'react';
|
||||
import { IndexKey } from '@tldraw/utils';
|
||||
import { JsonObject } from '@tldraw/utils';
|
||||
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
||||
import { MemoExoticComponent } from 'react';
|
||||
|
@ -697,7 +698,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
getErasingShapes(): NonNullable<TLShape | undefined>[];
|
||||
getFocusedGroup(): TLShape | undefined;
|
||||
getFocusedGroupId(): TLPageId | TLShapeId;
|
||||
getHighestIndexForParent(parent: TLPage | TLParentId | TLShape): string;
|
||||
getHighestIndexForParent(parent: TLPage | TLParentId | TLShape): IndexKey;
|
||||
getHintingShape(): NonNullable<TLShape | undefined>[];
|
||||
getHintingShapeIds(): TLShapeId[];
|
||||
getHoveredShape(): TLShape | undefined;
|
||||
|
@ -844,7 +845,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
} : TLExternalContent) => void) | null): this;
|
||||
renamePage(page: TLPage | TLPageId, name: string, historyOptions?: TLCommandHistoryOptions): this;
|
||||
renderingBoundsMargin: number;
|
||||
reparentShapes(shapes: TLShape[] | TLShapeId[], parentId: TLParentId, insertIndex?: string): this;
|
||||
reparentShapes(shapes: TLShape[] | TLShapeId[], parentId: TLParentId, insertIndex?: IndexKey): this;
|
||||
resetZoom(point?: Vec, animation?: TLAnimationOptions): this;
|
||||
resizeShape(shape: TLShape | TLShapeId, scale: VecLike, options?: TLResizeShapeOptions): this;
|
||||
readonly root: RootState;
|
||||
|
@ -1065,27 +1066,6 @@ export function getFreshUserPreferences(): TLUserPreferences;
|
|||
// @public
|
||||
export function getIncrementedName(name: string, others: string[]): string;
|
||||
|
||||
// @public
|
||||
export function getIndexAbove(below: string): string;
|
||||
|
||||
// @public
|
||||
export function getIndexBelow(above: string): string;
|
||||
|
||||
// @public
|
||||
export function getIndexBetween(below: string, above?: string): string;
|
||||
|
||||
// @public
|
||||
export function getIndices(n: number, start?: string): string[];
|
||||
|
||||
// @public
|
||||
export function getIndicesAbove(below: string, n: number): string[];
|
||||
|
||||
// @public
|
||||
export function getIndicesBelow(above: string, n: number): string[];
|
||||
|
||||
// @public
|
||||
export function getIndicesBetween(below: string | undefined, above: string | undefined, n: number): string[];
|
||||
|
||||
// @public (undocumented)
|
||||
export function getPointerInfo(e: PointerEvent | React.PointerEvent): {
|
||||
point: {
|
||||
|
@ -1744,11 +1724,6 @@ export class SnapManager {
|
|||
readonly shapeBounds: BoundsSnaps;
|
||||
}
|
||||
|
||||
// @public
|
||||
export function sortByIndex<T extends {
|
||||
index: string;
|
||||
}>(a: T, b: T): -1 | 0 | 1;
|
||||
|
||||
// @public (undocumented)
|
||||
export class Stadium2d extends Ellipse2d {
|
||||
constructor(config: Omit<Geometry2dOptions, 'isClosed'> & {
|
||||
|
|
|
@ -10585,8 +10585,9 @@
|
|||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -15742,8 +15743,9 @@
|
|||
"text": ", insertIndex?: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -21639,417 +21641,6 @@
|
|||
],
|
||||
"name": "getIncrementedName"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!getIndexAbove:function(1)",
|
||||
"docComment": "/**\n * Get the index above a given index.\n *\n * @param below - The index below.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndexAbove(below: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/utils/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "below",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndexAbove"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!getIndexBelow:function(1)",
|
||||
"docComment": "/**\n * Get the index below a given index.\n *\n * @param above - The index above.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndexBelow(above: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/utils/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "above",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndexBelow"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!getIndexBetween:function(1)",
|
||||
"docComment": "/**\n * Get the index between two indices.\n *\n * @param below - The index below.\n *\n * @param above - The index above.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndexBetween(below: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", above?: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/utils/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "below",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "above",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
],
|
||||
"name": "getIndexBetween"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!getIndices:function(1)",
|
||||
"docComment": "/**\n * Get n number of indices, starting at an index.\n *\n * @param n - The number of indices to get.\n *\n * @param start - The index to start at.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndices(n: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", start?: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/utils/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "n",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "start",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
],
|
||||
"name": "getIndices"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!getIndicesAbove:function(1)",
|
||||
"docComment": "/**\n * Get a number of indices above an index.\n *\n * @param below - The index below.\n *\n * @param n - The number of indices to get.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndicesAbove(below: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", n: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/utils/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "below",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "n",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndicesAbove"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!getIndicesBelow:function(1)",
|
||||
"docComment": "/**\n * Get a number of indices below an index.\n *\n * @param above - The index above.\n *\n * @param n - The number of indices to get.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndicesBelow(above: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", n: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/utils/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "above",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "n",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndicesBelow"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!getIndicesBetween:function(1)",
|
||||
"docComment": "/**\n * Get a number of indices between two indices.\n *\n * @param below - The index below.\n *\n * @param above - The index above.\n *\n * @param n - The number of indices to get.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndicesBetween(below: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string | undefined"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", above: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string | undefined"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", n: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/utils/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 7,
|
||||
"endIndex": 8
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "below",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "above",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "n",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndicesBetween"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!getPointerInfo:function(1)",
|
||||
|
@ -32799,88 +32390,6 @@
|
|||
],
|
||||
"implementsTokenRanges": []
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/editor!sortByIndex:function(1)",
|
||||
"docComment": "/**\n * Sort by index.\n *\n * @param a - An object with an index property.\n *\n * @param b - An object with an index property.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function sortByIndex<T extends "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n index: string;\n}"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ">(a: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "T"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", b: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "T"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "-1 | 0 | 1"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/editor/src/lib/utils/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 7,
|
||||
"endIndex": 8
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "a",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "b",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"typeParameters": [
|
||||
{
|
||||
"typeParameterName": "T",
|
||||
"constraintTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"defaultTypeTokenRange": {
|
||||
"startIndex": 0,
|
||||
"endIndex": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"name": "sortByIndex"
|
||||
},
|
||||
{
|
||||
"kind": "Class",
|
||||
"canonicalReference": "@tldraw/editor!Stadium2d:class",
|
||||
|
|
|
@ -356,16 +356,6 @@ export { getSvgPathFromPoints } from './lib/utils/getSvgPathFromPoints'
|
|||
export { hardResetEditor } from './lib/utils/hardResetEditor'
|
||||
export { normalizeWheel } from './lib/utils/normalizeWheel'
|
||||
export { refreshPage } from './lib/utils/refreshPage'
|
||||
export {
|
||||
getIndexAbove,
|
||||
getIndexBelow,
|
||||
getIndexBetween,
|
||||
getIndices,
|
||||
getIndicesAbove,
|
||||
getIndicesBelow,
|
||||
getIndicesBetween,
|
||||
sortByIndex,
|
||||
} from './lib/utils/reordering/reordering'
|
||||
export {
|
||||
applyRotationToSnapshotShapes,
|
||||
getRotationSnapshot,
|
||||
|
|
|
@ -39,15 +39,22 @@ import {
|
|||
isShapeId,
|
||||
} from '@tldraw/tlschema'
|
||||
import {
|
||||
IndexKey,
|
||||
JsonObject,
|
||||
annotateError,
|
||||
assert,
|
||||
compact,
|
||||
dedupe,
|
||||
deepCopy,
|
||||
getIndexAbove,
|
||||
getIndexBetween,
|
||||
getIndices,
|
||||
getIndicesAbove,
|
||||
getIndicesBetween,
|
||||
getOwnProperty,
|
||||
hasOwnProperty,
|
||||
sortById,
|
||||
sortByIndex,
|
||||
structuredClone,
|
||||
} from '@tldraw/utils'
|
||||
import { EventEmitter } from 'eventemitter3'
|
||||
|
@ -89,14 +96,6 @@ import { WeakMapCache } from '../utils/WeakMapCache'
|
|||
import { dataUrlToFile } from '../utils/assets'
|
||||
import { getIncrementedName } from '../utils/getIncrementedName'
|
||||
import { getReorderingShapesChanges } from '../utils/reorderShapes'
|
||||
import {
|
||||
getIndexAbove,
|
||||
getIndexBetween,
|
||||
getIndices,
|
||||
getIndicesAbove,
|
||||
getIndicesBetween,
|
||||
sortByIndex,
|
||||
} from '../utils/reordering/reordering'
|
||||
import { applyRotationToSnapshotShapes, getRotationSnapshot } from '../utils/rotation'
|
||||
import { uniqueId } from '../utils/uniqueId'
|
||||
import { arrowBindingsIndex } from './derivations/arrowBindingsIndex'
|
||||
|
@ -324,7 +323,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
return
|
||||
}
|
||||
|
||||
let finalIndex: string
|
||||
let finalIndex: IndexKey
|
||||
|
||||
const higherSiblings = this.getSortedChildIdsForParent(highestSibling.parentId)
|
||||
.map((id) => this.getShape(id)!)
|
||||
|
@ -4775,7 +4774,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*
|
||||
* @public
|
||||
*/
|
||||
reparentShapes(shapes: TLShapeId[] | TLShape[], parentId: TLParentId, insertIndex?: string) {
|
||||
reparentShapes(shapes: TLShapeId[] | TLShape[], parentId: TLParentId, insertIndex?: IndexKey) {
|
||||
const ids =
|
||||
typeof shapes[0] === 'string' ? (shapes as TLShapeId[]) : shapes.map((s) => (s as TLShape).id)
|
||||
if (ids.length === 0) return this
|
||||
|
@ -4788,7 +4787,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
|
||||
const parentPageRotation = parentTransform.rotation()
|
||||
|
||||
let indices: string[] = []
|
||||
let indices: IndexKey[] = []
|
||||
|
||||
const sibs = compact(this.getSortedChildIdsForParent(parentId).map((id) => this.getShape(id)))
|
||||
|
||||
|
@ -4877,12 +4876,12 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
*
|
||||
* @public
|
||||
*/
|
||||
getHighestIndexForParent(parent: TLParentId | TLPage | TLShape): string {
|
||||
getHighestIndexForParent(parent: TLParentId | TLPage | TLShape): IndexKey {
|
||||
const parentId = typeof parent === 'string' ? parent : parent.id
|
||||
const children = this._parentIdsToChildIds.get()[parentId]
|
||||
|
||||
if (!children || children.length === 0) {
|
||||
return 'a1'
|
||||
return 'a1' as IndexKey
|
||||
}
|
||||
const shape = this.getShape(children[children.length - 1])!
|
||||
return getIndexAbove(shape.index)
|
||||
|
@ -6584,7 +6583,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
// Get the highest index among the parents of each of the
|
||||
// the shapes being created; we'll increment from there.
|
||||
|
||||
const parentIndices = new Map<string, string>()
|
||||
const parentIndices = new Map<TLParentId, IndexKey>()
|
||||
|
||||
const shapeRecordsToCreate: TLShape[] = []
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { computed, isUninitialized, RESET_VALUE } from '@tldraw/state'
|
||||
import { RecordsDiff } from '@tldraw/store'
|
||||
import { isShape, TLParentId, TLRecord, TLShape, TLShapeId, TLStore } from '@tldraw/tlschema'
|
||||
import { compact } from '@tldraw/utils'
|
||||
import { sortByIndex } from '../../utils/reordering/reordering'
|
||||
import { compact, sortByIndex } from '@tldraw/utils'
|
||||
|
||||
type Parents2Children = Record<TLParentId, TLShapeId[]>
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { TLParentId, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'
|
||||
import { compact } from '@tldraw/utils'
|
||||
import { IndexKey, compact, getIndicesBetween, sortByIndex } from '@tldraw/utils'
|
||||
import { Editor } from '../editor/Editor'
|
||||
import { getIndicesBetween, sortByIndex } from './reordering/reordering'
|
||||
|
||||
export function getReorderingShapesChanges(
|
||||
editor: Editor,
|
||||
|
@ -63,8 +62,8 @@ function reorderToBack(moving: Set<TLShape>, children: TLShape[], changes: TLSha
|
|||
// If all of the children are moving, there's nothing to do
|
||||
if (moving.size === len) return
|
||||
|
||||
let below: string | undefined
|
||||
let above: string | undefined
|
||||
let below: IndexKey | undefined
|
||||
let above: IndexKey | undefined
|
||||
|
||||
// Starting at the bottom of this parent's children...
|
||||
for (let i = 0; i < len; i++) {
|
||||
|
@ -112,8 +111,8 @@ function reorderToFront(moving: Set<TLShape>, children: TLShape[], changes: TLSh
|
|||
// If all of the children are moving, there's nothing to do
|
||||
if (moving.size === len) return
|
||||
|
||||
let below: string | undefined
|
||||
let above: string | undefined
|
||||
let below: IndexKey | undefined
|
||||
let above: IndexKey | undefined
|
||||
|
||||
// Starting at the top of this parent's children...
|
||||
for (let i = len - 1; i > -1; i--) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { PageRecordType } from '@tldraw/tlschema'
|
||||
import { promiseWithResolve } from '@tldraw/utils'
|
||||
import { IndexKey, promiseWithResolve } from '@tldraw/utils'
|
||||
import { createTLStore } from '../../config/createTLStore'
|
||||
import { TLLocalSyncClient } from './TLLocalSyncClient'
|
||||
import * as idb from './indexedDb'
|
||||
|
@ -111,12 +111,12 @@ test('when a client receives an announce with a newer schema version shortly aft
|
|||
test('the first db write after a client connects is a full db overwrite', async () => {
|
||||
const { client } = testClient()
|
||||
await tick()
|
||||
client.store.put([PageRecordType.create({ name: 'test', index: 'a0' })])
|
||||
client.store.put([PageRecordType.create({ name: 'test', index: 'a0' as IndexKey })])
|
||||
await tick()
|
||||
expect(idb.storeSnapshotInIndexedDb).toHaveBeenCalledTimes(1)
|
||||
expect(idb.storeChangesInIndexedDb).not.toHaveBeenCalled()
|
||||
|
||||
client.store.put([PageRecordType.create({ name: 'test2', index: 'a1' })])
|
||||
client.store.put([PageRecordType.create({ name: 'test2', index: 'a1' as IndexKey })])
|
||||
await tick()
|
||||
expect(idb.storeSnapshotInIndexedDb).toHaveBeenCalledTimes(1)
|
||||
expect(idb.storeChangesInIndexedDb).toHaveBeenCalledTimes(1)
|
||||
|
@ -125,12 +125,12 @@ test('the first db write after a client connects is a full db overwrite', async
|
|||
test('it clears the diff queue after every write', async () => {
|
||||
const { client } = testClient()
|
||||
await tick()
|
||||
client.store.put([PageRecordType.create({ name: 'test', index: 'a0' })])
|
||||
client.store.put([PageRecordType.create({ name: 'test', index: 'a0' as IndexKey })])
|
||||
await tick()
|
||||
// @ts-expect-error
|
||||
expect(client.diffQueue.length).toBe(0)
|
||||
|
||||
client.store.put([PageRecordType.create({ name: 'test2', index: 'a1' })])
|
||||
client.store.put([PageRecordType.create({ name: 'test2', index: 'a1' as IndexKey })])
|
||||
await tick()
|
||||
// @ts-expect-error
|
||||
expect(client.diffQueue.length).toBe(0)
|
||||
|
@ -142,7 +142,7 @@ test('writes that come in during a persist operation will get persisted afterwar
|
|||
|
||||
const { client } = testClient()
|
||||
await tick()
|
||||
client.store.put([PageRecordType.create({ name: 'test', index: 'a0' })])
|
||||
client.store.put([PageRecordType.create({ name: 'test', index: 'a0' as IndexKey })])
|
||||
await tick()
|
||||
|
||||
// we should have called into idb but not resolved the promise yet
|
||||
|
@ -150,7 +150,7 @@ test('writes that come in during a persist operation will get persisted afterwar
|
|||
expect(idb.storeChangesInIndexedDb).toHaveBeenCalledTimes(0)
|
||||
|
||||
// if another change comes in, loads of time can pass, but nothing else should get called
|
||||
client.store.put([PageRecordType.create({ name: 'test', index: 'a2' })])
|
||||
client.store.put([PageRecordType.create({ name: 'test', index: 'a2' as IndexKey })])
|
||||
await tick()
|
||||
expect(idb.storeSnapshotInIndexedDb).toHaveBeenCalledTimes(1)
|
||||
expect(idb.storeChangesInIndexedDb).toHaveBeenCalledTimes(0)
|
||||
|
|
|
@ -20,6 +20,7 @@ import { EmbedDefinition } from '@tldraw/editor';
|
|||
import { EnumStyleProp } from '@tldraw/editor';
|
||||
import { Geometry2d } from '@tldraw/editor';
|
||||
import { Group2d } from '@tldraw/editor';
|
||||
import { IndexKey } from '@tldraw/editor';
|
||||
import { JsonObject } from '@tldraw/editor';
|
||||
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
||||
import { LANGUAGES } from '@tldraw/editor';
|
||||
|
@ -603,7 +604,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
parentId: TLParentId;
|
||||
isLocked: boolean;
|
||||
opacity: number;
|
||||
|
@ -633,7 +634,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
parentId: TLParentId;
|
||||
isLocked: boolean;
|
||||
opacity: number;
|
||||
|
@ -650,7 +651,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
parentId: TLParentId;
|
||||
isLocked: boolean;
|
||||
opacity: number;
|
||||
|
@ -665,7 +666,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
|
|||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
parentId: TLParentId;
|
||||
isLocked: boolean;
|
||||
opacity: number;
|
||||
|
@ -999,7 +1000,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
parentId: TLParentId;
|
||||
isLocked: boolean;
|
||||
opacity: number;
|
||||
|
@ -1023,7 +1024,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
|
|||
x: number;
|
||||
y: number;
|
||||
rotation: number;
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
parentId: TLParentId;
|
||||
isLocked: boolean;
|
||||
opacity: number;
|
||||
|
@ -1166,7 +1167,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
y: number;
|
||||
type: "text";
|
||||
rotation: number;
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
parentId: TLParentId;
|
||||
isLocked: boolean;
|
||||
opacity: number;
|
||||
|
@ -1200,7 +1201,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
|
|||
};
|
||||
type: "text";
|
||||
rotation: number;
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
parentId: TLParentId;
|
||||
isLocked: boolean;
|
||||
opacity: number;
|
||||
|
|
|
@ -7342,7 +7342,16 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => {\n props: {\n growY: number;\n geo: \"arrow-down\" | \"arrow-left\" | \"arrow-right\" | \"arrow-up\" | \"check-box\" | \"cloud\" | \"diamond\" | \"ellipse\" | \"hexagon\" | \"octagon\" | \"oval\" | \"pentagon\" | \"rectangle\" | \"rhombus-2\" | \"rhombus\" | \"star\" | \"trapezoid\" | \"triangle\" | \"x-box\";\n labelColor: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n fill: \"none\" | \"pattern\" | \"semi\" | \"solid\";\n dash: \"dashed\" | \"dotted\" | \"draw\" | \"solid\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n verticalAlign: \"end\" | \"middle\" | \"start\";\n url: string;\n w: number;\n h: number;\n text: string;\n };\n type: \"geo\";\n x: number;\n y: number;\n rotation: number;\n index: string;\n parentId: import(\"@tldraw/editor\")."
|
||||
"text": ") => {\n props: {\n growY: number;\n geo: \"arrow-down\" | \"arrow-left\" | \"arrow-right\" | \"arrow-up\" | \"check-box\" | \"cloud\" | \"diamond\" | \"ellipse\" | \"hexagon\" | \"octagon\" | \"oval\" | \"pentagon\" | \"rectangle\" | \"rhombus-2\" | \"rhombus\" | \"star\" | \"trapezoid\" | \"triangle\" | \"x-box\";\n labelColor: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n fill: \"none\" | \"pattern\" | \"semi\" | \"solid\";\n dash: \"dashed\" | \"dotted\" | \"draw\" | \"solid\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n verticalAlign: \"end\" | \"middle\" | \"start\";\n url: string;\n w: number;\n h: number;\n text: string;\n };\n type: \"geo\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n parentId: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -7382,7 +7391,7 @@
|
|||
"name": "onBeforeCreate",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 10
|
||||
"endIndex": 12
|
||||
},
|
||||
"isStatic": false,
|
||||
"isProtected": false,
|
||||
|
@ -7417,7 +7426,16 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => {\n props: {\n growY: number;\n geo: \"arrow-down\" | \"arrow-left\" | \"arrow-right\" | \"arrow-up\" | \"check-box\" | \"cloud\" | \"diamond\" | \"ellipse\" | \"hexagon\" | \"octagon\" | \"oval\" | \"pentagon\" | \"rectangle\" | \"rhombus-2\" | \"rhombus\" | \"star\" | \"trapezoid\" | \"triangle\" | \"x-box\";\n labelColor: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n fill: \"none\" | \"pattern\" | \"semi\" | \"solid\";\n dash: \"dashed\" | \"dotted\" | \"draw\" | \"solid\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n verticalAlign: \"end\" | \"middle\" | \"start\";\n url: string;\n w: number;\n h: number;\n text: string;\n };\n type: \"geo\";\n x: number;\n y: number;\n rotation: number;\n index: string;\n parentId: import(\"@tldraw/editor\")."
|
||||
"text": ") => {\n props: {\n growY: number;\n geo: \"arrow-down\" | \"arrow-left\" | \"arrow-right\" | \"arrow-up\" | \"check-box\" | \"cloud\" | \"diamond\" | \"ellipse\" | \"hexagon\" | \"octagon\" | \"oval\" | \"pentagon\" | \"rectangle\" | \"rhombus-2\" | \"rhombus\" | \"star\" | \"trapezoid\" | \"triangle\" | \"x-box\";\n labelColor: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n fill: \"none\" | \"pattern\" | \"semi\" | \"solid\";\n dash: \"dashed\" | \"dotted\" | \"draw\" | \"solid\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n verticalAlign: \"end\" | \"middle\" | \"start\";\n url: string;\n w: number;\n h: number;\n text: string;\n };\n type: \"geo\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n parentId: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -7457,7 +7475,7 @@
|
|||
"name": "onBeforeUpdate",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 12
|
||||
"endIndex": 14
|
||||
},
|
||||
"isStatic": false,
|
||||
"isProtected": false,
|
||||
|
@ -7483,7 +7501,16 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => {\n props: {\n geo: \"check-box\";\n };\n type: \"geo\";\n x: number;\n y: number;\n rotation: number;\n index: string;\n parentId: import(\"@tldraw/editor\")."
|
||||
"text": ") => {\n props: {\n geo: \"check-box\";\n };\n type: \"geo\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n parentId: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -7510,7 +7537,16 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n typeName: \"shape\";\n } | {\n props: {\n geo: \"rectangle\";\n };\n type: \"geo\";\n x: number;\n y: number;\n rotation: number;\n index: string;\n parentId: import(\"@tldraw/editor\")."
|
||||
"text": ";\n typeName: \"shape\";\n } | {\n props: {\n geo: \"rectangle\";\n };\n type: \"geo\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n parentId: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -7550,7 +7586,7 @@
|
|||
"name": "onDoubleClick",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 16
|
||||
"endIndex": 20
|
||||
},
|
||||
"isStatic": false,
|
||||
"isProtected": false,
|
||||
|
@ -11997,7 +12033,16 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => {\n props: {\n growY: number;\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n verticalAlign: \"end\" | \"middle\" | \"start\";\n url: string;\n text: string;\n };\n type: \"note\";\n x: number;\n y: number;\n rotation: number;\n index: string;\n parentId: import(\"@tldraw/editor\")."
|
||||
"text": ") => {\n props: {\n growY: number;\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n verticalAlign: \"end\" | \"middle\" | \"start\";\n url: string;\n text: string;\n };\n type: \"note\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n parentId: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -12037,7 +12082,7 @@
|
|||
"name": "onBeforeCreate",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 10
|
||||
"endIndex": 12
|
||||
},
|
||||
"isStatic": false,
|
||||
"isProtected": false,
|
||||
|
@ -12072,7 +12117,16 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => {\n props: {\n growY: number;\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n verticalAlign: \"end\" | \"middle\" | \"start\";\n url: string;\n text: string;\n };\n type: \"note\";\n x: number;\n y: number;\n rotation: number;\n index: string;\n parentId: import(\"@tldraw/editor\")."
|
||||
"text": ") => {\n props: {\n growY: number;\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n verticalAlign: \"end\" | \"middle\" | \"start\";\n url: string;\n text: string;\n };\n type: \"note\";\n x: number;\n y: number;\n rotation: number;\n index: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n parentId: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -12112,7 +12166,7 @@
|
|||
"name": "onBeforeUpdate",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 12
|
||||
"endIndex": 14
|
||||
},
|
||||
"isStatic": false,
|
||||
"isProtected": false,
|
||||
|
@ -13660,7 +13714,16 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => {\n x: number;\n y: number;\n type: \"text\";\n rotation: number;\n index: string;\n parentId: import(\"@tldraw/editor\")."
|
||||
"text": ") => {\n x: number;\n y: number;\n type: \"text\";\n rotation: number;\n index: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n parentId: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -13700,7 +13763,7 @@
|
|||
"name": "onBeforeCreate",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 10
|
||||
"endIndex": 12
|
||||
},
|
||||
"isStatic": false,
|
||||
"isProtected": false,
|
||||
|
@ -13735,7 +13798,16 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ") => {\n x: number;\n y: number;\n props: {\n w: number;\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n text: string;\n scale: number;\n autoSize: boolean;\n };\n type: \"text\";\n rotation: number;\n index: string;\n parentId: import(\"@tldraw/editor\")."
|
||||
"text": ") => {\n x: number;\n y: number;\n props: {\n w: number;\n color: \"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\";\n size: \"l\" | \"m\" | \"s\" | \"xl\";\n font: \"draw\" | \"mono\" | \"sans\" | \"serif\";\n align: \"end-legacy\" | \"end\" | \"middle-legacy\" | \"middle\" | \"start-legacy\" | \"start\";\n text: string;\n scale: number;\n autoSize: boolean;\n };\n type: \"text\";\n rotation: number;\n index: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n parentId: import(\"@tldraw/editor\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -13775,7 +13847,7 @@
|
|||
"name": "onBeforeUpdate",
|
||||
"propertyTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 12
|
||||
"endIndex": 14
|
||||
},
|
||||
"isStatic": false,
|
||||
"isProtected": false,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { TLArrowShape, Vec, createShapeId } from '@tldraw/editor'
|
||||
import { IndexKey, TLArrowShape, Vec, createShapeId } from '@tldraw/editor'
|
||||
import { TestEditor } from '../../../test/TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -406,14 +406,14 @@ describe('reparenting issue', () => {
|
|||
{ id: ids.box3, type: 'geo', x: 350, y: 350, props: { w: 90, h: 90 } }, // overlapping box2
|
||||
])
|
||||
|
||||
editor.expectShapeToMatch({ id: ids.box1, index: 'a2' })
|
||||
editor.expectShapeToMatch({ id: ids.box2, index: 'a3' })
|
||||
editor.expectShapeToMatch({ id: ids.box3, index: 'a4' })
|
||||
editor.expectShapeToMatch({ id: ids.box1, index: 'a2' as IndexKey })
|
||||
editor.expectShapeToMatch({ id: ids.box2, index: 'a3' as IndexKey })
|
||||
editor.expectShapeToMatch({ id: ids.box3, index: 'a4' as IndexKey })
|
||||
|
||||
editor.select(arrowId)
|
||||
editor.pointerDown(100, 100, {
|
||||
target: 'handle',
|
||||
handle: { id: 'end', type: 'vertex', index: 'a0', x: 100, y: 100 },
|
||||
handle: { id: 'end', type: 'vertex', index: 'a0' as IndexKey, x: 100, y: 100 },
|
||||
shape: editor.getShape(arrowId)!,
|
||||
})
|
||||
editor.expectToBeIn('select.pointing_handle')
|
||||
|
@ -422,7 +422,7 @@ describe('reparenting issue', () => {
|
|||
editor.expectToBeIn('select.dragging_handle')
|
||||
editor.expectShapeToMatch({
|
||||
id: arrowId,
|
||||
index: 'a3V',
|
||||
index: 'a3V' as IndexKey,
|
||||
props: { end: { boundShapeId: ids.box2 } },
|
||||
}) // between box 2 (a3) and 3 (a4)
|
||||
|
||||
|
@ -433,15 +433,15 @@ describe('reparenting issue', () => {
|
|||
editor.pointerMove(350, 350) // over box 3 and box 2, but box 3 is smaller
|
||||
editor.expectShapeToMatch({
|
||||
id: arrowId,
|
||||
index: 'a5',
|
||||
index: 'a5' as IndexKey,
|
||||
props: { end: { boundShapeId: ids.box3 } },
|
||||
}) // above box 3 (a4)
|
||||
|
||||
editor.pointerMove(150, 150) // over box 1
|
||||
editor.expectShapeToMatch({ id: arrowId, index: 'a2V' }) // between box 1 (a2) and box 3 (a3)
|
||||
editor.expectShapeToMatch({ id: arrowId, index: 'a2V' as IndexKey }) // between box 1 (a2) and box 3 (a3)
|
||||
|
||||
editor.pointerMove(-100, -100) // over the page
|
||||
editor.expectShapeToMatch({ id: arrowId, index: 'a2V' }) // no change needed, keep whatever we had before
|
||||
editor.expectShapeToMatch({ id: arrowId, index: 'a2V' as IndexKey }) // no change needed, keep whatever we had before
|
||||
|
||||
// todo: should the arrow go back to where it was before?
|
||||
})
|
||||
|
@ -481,7 +481,7 @@ describe('reparenting issue', () => {
|
|||
.select(arrow1Id)
|
||||
.pointerDown(100, 100, {
|
||||
target: 'handle',
|
||||
handle: { id: 'end', type: 'vertex', index: 'a0', x: 100, y: 100 },
|
||||
handle: { id: 'end', type: 'vertex', index: 'a0' as IndexKey, x: 100, y: 100 },
|
||||
shape: editor.getShape(arrow1Id)!,
|
||||
})
|
||||
.pointerMove(120, 120)
|
||||
|
@ -490,7 +490,7 @@ describe('reparenting issue', () => {
|
|||
.select(arrow2Id)
|
||||
.pointerDown(100, 100, {
|
||||
target: 'handle',
|
||||
handle: { id: 'end', type: 'vertex', index: 'a0', x: 100, y: 100 },
|
||||
handle: { id: 'end', type: 'vertex', index: 'a0' as IndexKey, x: 100, y: 100 },
|
||||
shape: editor.getShape(arrow2Id)!,
|
||||
})
|
||||
.pointerMove(150, 150)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { TLGeoShape, TLLineShape, createShapeId, deepCopy } from '@tldraw/editor'
|
||||
import { IndexKey, TLGeoShape, TLLineShape, createShapeId, deepCopy } from '@tldraw/editor'
|
||||
import { TestEditor } from '../../../test/TestEditor'
|
||||
|
||||
jest.mock('nanoid', () => {
|
||||
|
@ -85,7 +85,7 @@ it('create new handle', () => {
|
|||
handle: {
|
||||
id: 'mid-0',
|
||||
type: 'create',
|
||||
index: 'a1V',
|
||||
index: 'a1V' as IndexKey,
|
||||
x: 50,
|
||||
y: 50,
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import {
|
||||
CubicSpline2d,
|
||||
IndexKey,
|
||||
Polyline2d,
|
||||
SVGContainer,
|
||||
ShapeUtil,
|
||||
|
@ -55,7 +56,7 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
|||
type: 'vertex',
|
||||
canBind: false,
|
||||
canSnap: true,
|
||||
index: 'a1',
|
||||
index: 'a1' as IndexKey,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
|
@ -64,7 +65,7 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
|
|||
type: 'vertex',
|
||||
canBind: false,
|
||||
canSnap: true,
|
||||
index: 'a2',
|
||||
index: 'a2' as IndexKey,
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
},
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
IndexKey,
|
||||
Mat,
|
||||
StateNode,
|
||||
TLEventHandlers,
|
||||
|
@ -50,7 +51,7 @@ export class Pointing extends StateNode {
|
|||
new Vec(this.shape.x, this.shape.y)
|
||||
)
|
||||
|
||||
let nextEndHandleIndex: string, nextEndHandleId: string, nextEndHandle: TLHandle
|
||||
let nextEndHandleIndex: IndexKey, nextEndHandleId: string, nextEndHandle: TLHandle
|
||||
|
||||
const nextPoint = Vec.Sub(currentPagePoint, shapePagePoint)
|
||||
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { Editor, getIndexAbove, getIndexBelow, getIndexBetween, TLPageId } from '@tldraw/editor'
|
||||
import {
|
||||
Editor,
|
||||
getIndexAbove,
|
||||
getIndexBelow,
|
||||
getIndexBetween,
|
||||
IndexKey,
|
||||
TLPageId,
|
||||
} from '@tldraw/editor'
|
||||
|
||||
export const onMovePage = (editor: Editor, id: TLPageId, from: number, to: number) => {
|
||||
let index: string
|
||||
let index: IndexKey
|
||||
|
||||
const pages = editor.getPages()
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
TLShapeId,
|
||||
Vec,
|
||||
VecLike,
|
||||
ZERO_INDEX_KEY,
|
||||
compact,
|
||||
createShapeId,
|
||||
getIndexAbove,
|
||||
|
@ -63,7 +64,7 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
|
|||
}
|
||||
})
|
||||
|
||||
let index = 'a1'
|
||||
let index = ZERO_INDEX_KEY
|
||||
|
||||
for (const element of elements) {
|
||||
if (skipIds.has(element.id)) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createShapeId } from '@tldraw/editor'
|
||||
import { IndexKey, createShapeId } from '@tldraw/editor'
|
||||
import { TestEditor } from './TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -114,7 +114,7 @@ describe('PointingHandle', () => {
|
|||
editor.pointerDown(150, 150, {
|
||||
target: 'handle',
|
||||
shape,
|
||||
handle: { id: 'start', type: 'vertex', index: 'a1', x: 0, y: 0 },
|
||||
handle: { id: 'start', type: 'vertex', index: 'a1' as IndexKey, x: 0, y: 0 },
|
||||
})
|
||||
editor.expectToBeIn('select.pointing_handle')
|
||||
|
||||
|
@ -127,7 +127,7 @@ describe('PointingHandle', () => {
|
|||
editor.pointerDown(150, 150, {
|
||||
target: 'handle',
|
||||
shape,
|
||||
handle: { id: 'start', type: 'vertex', index: 'a1', x: 0, y: 0 },
|
||||
handle: { id: 'start', type: 'vertex', index: 'a1' as IndexKey, x: 0, y: 0 },
|
||||
})
|
||||
editor.expectToBeIn('select.pointing_handle')
|
||||
editor.cancel()
|
||||
|
@ -142,7 +142,7 @@ describe('DraggingHandle', () => {
|
|||
editor.pointerDown(150, 150, {
|
||||
target: 'handle',
|
||||
shape,
|
||||
handle: { id: 'start', type: 'vertex', index: 'a1', x: 0, y: 0 },
|
||||
handle: { id: 'start', type: 'vertex', index: 'a1' as IndexKey, x: 0, y: 0 },
|
||||
})
|
||||
editor.pointerMove(100, 100)
|
||||
editor.expectToBeIn('select.dragging_handle')
|
||||
|
@ -158,7 +158,7 @@ describe('DraggingHandle', () => {
|
|||
editor.pointerDown(150, 150, {
|
||||
target: 'handle',
|
||||
shape,
|
||||
handle: { id: 'start', type: 'vertex', index: 'a1', x: 0, y: 0 },
|
||||
handle: { id: 'start', type: 'vertex', index: 'a1' as IndexKey, x: 0, y: 0 },
|
||||
})
|
||||
editor.pointerMove(100, 100)
|
||||
editor.expectToBeIn('select.dragging_handle')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PageRecordType, TLShape, createShapeId } from '@tldraw/editor'
|
||||
import { IndexKey, PageRecordType, TLShape, createShapeId } from '@tldraw/editor'
|
||||
import { TestEditor } from '../TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -122,7 +122,7 @@ describe('Editor.moveShapesToPage', () => {
|
|||
editor.expectShapeToMatch({
|
||||
id: ids.box1,
|
||||
parentId: page2Id,
|
||||
index: 'a1',
|
||||
index: 'a1' as IndexKey,
|
||||
})
|
||||
|
||||
const page3Id = PageRecordType.createId('newPage3')
|
||||
|
@ -134,7 +134,7 @@ describe('Editor.moveShapesToPage', () => {
|
|||
editor.expectShapeToMatch({
|
||||
id: ids.box2,
|
||||
parentId: page3Id,
|
||||
index: 'a1',
|
||||
index: 'a1' as IndexKey,
|
||||
})
|
||||
|
||||
editor.setCurrentPage(page2Id)
|
||||
|
@ -147,12 +147,12 @@ describe('Editor.moveShapesToPage', () => {
|
|||
{
|
||||
id: ids.box2,
|
||||
parentId: page3Id,
|
||||
index: 'a1',
|
||||
index: 'a1' as IndexKey,
|
||||
},
|
||||
{
|
||||
id: ids.box1,
|
||||
parentId: page3Id,
|
||||
index: 'a2', // should be a2 now
|
||||
index: 'a2' as IndexKey, // should be a2 now
|
||||
}
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createShapeId } from '@tldraw/editor'
|
||||
import { IndexKey, createShapeId } from '@tldraw/editor'
|
||||
import { TestEditor, createDefaultShapes, defaultShapesIds } from '../TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -105,7 +105,7 @@ it('adds children at a given index', () => {
|
|||
expect(editor.getShape(ids.ellipse1)!.index).toBe('a1')
|
||||
|
||||
// Handles collisions (trying to move box3 to a0, but box2 is there already)
|
||||
editor.reparentShapes([ids.box3], ids.box1, 'a1')
|
||||
editor.reparentShapes([ids.box3], ids.box1, 'a1' as IndexKey)
|
||||
|
||||
// Page
|
||||
// - box1 a1
|
||||
|
@ -124,7 +124,7 @@ it('adds children at a given index', () => {
|
|||
|
||||
// Handles collisions (trying to move box5 to a0, but box2 is there already)
|
||||
// should end up between box 2 and box 3 (a0 and a1)
|
||||
editor.reparentShapes([ids.box5], ids.box1, 'a1')
|
||||
editor.reparentShapes([ids.box5], ids.box1, 'a1' as IndexKey)
|
||||
|
||||
// Page
|
||||
// - box1 a1
|
||||
|
@ -143,7 +143,7 @@ it('adds children at a given index', () => {
|
|||
|
||||
// Handles collisions (trying to move boxes 2, 3, and 5 to a0, but box1 is there already)
|
||||
// Should order them between box1 and box4
|
||||
editor.reparentShapes([ids.box2, ids.box3, ids.box5], editor.getCurrentPageId(), 'a1')
|
||||
editor.reparentShapes([ids.box2, ids.box3, ids.box5], editor.getCurrentPageId(), 'a1' as IndexKey)
|
||||
|
||||
// Page
|
||||
// - box1 a1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PageRecordType, TLPageId, createShapeId } from '@tldraw/editor'
|
||||
import { IndexKey, PageRecordType, TLPageId, createShapeId } from '@tldraw/editor'
|
||||
import { TestEditor } from '../TestEditor'
|
||||
|
||||
let editor: TestEditor
|
||||
|
@ -39,7 +39,7 @@ describe('setCurrentPage', () => {
|
|||
})
|
||||
|
||||
it("adding a page to the store by any means adds tab state for the page if it doesn't already exist", () => {
|
||||
const page = PageRecordType.create({ name: 'test', index: 'a4' })
|
||||
const page = PageRecordType.create({ name: 'test', index: 'a4' as IndexKey })
|
||||
expect(editor.getPageStates().find((p) => p.pageId === page.id)).toBeUndefined()
|
||||
editor.store.put([page])
|
||||
expect(editor.getPageStates().find((p) => p.pageId === page.id)).not.toBeUndefined()
|
||||
|
@ -47,7 +47,7 @@ describe('setCurrentPage', () => {
|
|||
|
||||
it('squashes', () => {
|
||||
const page2Id = PageRecordType.createId('page2')
|
||||
editor.createPage({ name: 'New Page 2', index: page2Id })
|
||||
editor.createPage({ name: 'New Page 2', id: page2Id })
|
||||
|
||||
editor.history.clear()
|
||||
editor.setCurrentPage(editor.getPages()[1].id)
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
TLDefaultShape,
|
||||
TLShapeId,
|
||||
TLShapePartial,
|
||||
ZERO_INDEX_KEY,
|
||||
assert,
|
||||
assertExists,
|
||||
createShapeId,
|
||||
|
@ -55,7 +56,7 @@ export function shapesFromJsx(shapes: React.JSX.Element | Array<React.JSX.Elemen
|
|||
children: React.JSX.Element | Array<React.JSX.Element>,
|
||||
parentId?: TLShapeId
|
||||
) {
|
||||
let nextIndex = 'a0'
|
||||
let nextIndex = ZERO_INDEX_KEY
|
||||
|
||||
for (const el of Array.isArray(children) ? children : [children]) {
|
||||
const shapeType = (el.type as any)[shapeTypeSymbol] as string
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { BaseRecord } from '@tldraw/store';
|
||||
import { Expand } from '@tldraw/utils';
|
||||
import { IndexKey } from '@tldraw/utils';
|
||||
import { JsonObject } from '@tldraw/utils';
|
||||
import { Migrations } from '@tldraw/store';
|
||||
import { RecordId } from '@tldraw/store';
|
||||
|
@ -820,7 +821,7 @@ export interface TLBaseAsset<Type extends string, Props> extends BaseRecord<'ass
|
|||
// @public (undocumented)
|
||||
export interface TLBaseShape<Type extends string, Props extends object> extends BaseRecord<'shape', TLShapeId> {
|
||||
// (undocumented)
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
// (undocumented)
|
||||
isLocked: boolean;
|
||||
// (undocumented)
|
||||
|
@ -968,7 +969,7 @@ export interface TLHandle {
|
|||
canSnap?: boolean;
|
||||
id: string;
|
||||
// (undocumented)
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
// (undocumented)
|
||||
type: TLHandleType;
|
||||
// (undocumented)
|
||||
|
@ -1152,7 +1153,7 @@ export type TLOpacityType = number;
|
|||
// @public
|
||||
export interface TLPage extends BaseRecord<'page', TLPageId> {
|
||||
// (undocumented)
|
||||
index: string;
|
||||
index: IndexKey;
|
||||
// (undocumented)
|
||||
meta: JsonObject;
|
||||
// (undocumented)
|
||||
|
|
|
@ -4322,8 +4322,9 @@
|
|||
"text": "index: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -6064,8 +6065,9 @@
|
|||
"text": "index: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
@ -8299,8 +8301,9 @@
|
|||
"text": "index: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
StoreSchemaOptions,
|
||||
StoreSnapshot,
|
||||
} from '@tldraw/store'
|
||||
import { annotateError, structuredClone } from '@tldraw/utils'
|
||||
import { IndexKey, annotateError, structuredClone } from '@tldraw/utils'
|
||||
import { CameraRecordType, TLCameraId } from './records/TLCamera'
|
||||
import { DocumentRecordType, TLDOCUMENT_ID } from './records/TLDocument'
|
||||
import { TLINSTANCE_ID } from './records/TLInstance'
|
||||
|
@ -81,7 +81,12 @@ export const onValidationFailure: StoreSchemaOptions<
|
|||
|
||||
function getDefaultPages() {
|
||||
return [
|
||||
PageRecordType.create({ id: 'page:page' as TLPageId, name: 'Page 1', index: 'a1', meta: {} }),
|
||||
PageRecordType.create({
|
||||
id: 'page:page' as TLPageId,
|
||||
name: 'Page 1',
|
||||
index: 'a1' as IndexKey,
|
||||
meta: {},
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { IndexKey } from '@tldraw/utils'
|
||||
import { T } from '@tldraw/validate'
|
||||
import { SetValue } from '../util-types'
|
||||
|
||||
|
@ -24,7 +25,7 @@ export interface TLHandle {
|
|||
type: TLHandleType
|
||||
canBind?: boolean
|
||||
canSnap?: boolean
|
||||
index: string
|
||||
index: IndexKey
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
@ -35,7 +36,7 @@ export const handleValidator: T.Validator<TLHandle> = T.object({
|
|||
type: T.setEnum(TL_HANDLE_TYPES),
|
||||
canBind: T.boolean.optional(),
|
||||
canSnap: T.boolean.optional(),
|
||||
index: T.string,
|
||||
index: T.indexKey,
|
||||
x: T.number,
|
||||
y: T.number,
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { BaseRecord, createRecordType, defineMigrations, RecordId } from '@tldraw/store'
|
||||
import { JsonObject } from '@tldraw/utils'
|
||||
import { IndexKey, JsonObject } from '@tldraw/utils'
|
||||
import { T } from '@tldraw/validate'
|
||||
import { idValidator } from '../misc/id-validator'
|
||||
|
||||
|
@ -10,7 +10,7 @@ import { idValidator } from '../misc/id-validator'
|
|||
*/
|
||||
export interface TLPage extends BaseRecord<'page', TLPageId> {
|
||||
name: string
|
||||
index: string
|
||||
index: IndexKey
|
||||
meta: JsonObject
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ export const pageValidator: T.Validator<TLPage> = T.model(
|
|||
typeName: T.literal('page'),
|
||||
id: pageIdValidator,
|
||||
name: T.string,
|
||||
index: T.string,
|
||||
index: T.indexKey,
|
||||
meta: T.jsonValue as T.ObjectValidator<JsonObject>,
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { BaseRecord } from '@tldraw/store'
|
||||
import { Expand, JsonObject } from '@tldraw/utils'
|
||||
import { Expand, IndexKey, JsonObject } from '@tldraw/utils'
|
||||
import { T } from '@tldraw/validate'
|
||||
import { TLOpacityType, opacityValidator } from '../misc/TLOpacity'
|
||||
import { idValidator } from '../misc/id-validator'
|
||||
|
@ -12,7 +12,7 @@ export interface TLBaseShape<Type extends string, Props extends object>
|
|||
x: number
|
||||
y: number
|
||||
rotation: number
|
||||
index: string
|
||||
index: IndexKey
|
||||
parentId: TLParentId
|
||||
isLocked: boolean
|
||||
opacity: TLOpacityType
|
||||
|
@ -47,7 +47,7 @@ export function createShapeValidator<
|
|||
x: T.number,
|
||||
y: T.number,
|
||||
rotation: T.number,
|
||||
index: T.string,
|
||||
index: T.indexKey,
|
||||
parentId: parentIdValidator,
|
||||
type: T.literal(type),
|
||||
isLocked: T.boolean,
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
} from '@tldraw/store'
|
||||
import { DocumentRecordType, PageRecordType, TLDOCUMENT_ID } from '@tldraw/tlschema'
|
||||
import {
|
||||
IndexKey,
|
||||
Result,
|
||||
assertExists,
|
||||
exhaustiveSwitchError,
|
||||
|
@ -234,7 +235,7 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
|||
lastChangedClock: 0,
|
||||
},
|
||||
{
|
||||
state: PageRecordType.create({ name: 'Page 1', index: 'a1' }),
|
||||
state: PageRecordType.create({ name: 'Page 1', index: 'a1' as IndexKey }),
|
||||
lastChangedClock: 0,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
TLShapeId,
|
||||
createTLSchema,
|
||||
} from '@tldraw/tlschema'
|
||||
import { sortById } from '@tldraw/utils'
|
||||
import { ZERO_INDEX_KEY, sortById } from '@tldraw/utils'
|
||||
import {
|
||||
MAX_TOMBSTONES,
|
||||
RoomSnapshot,
|
||||
|
@ -24,7 +24,7 @@ const compareById = (a: { id: string }, b: { id: string }) => a.id.localeCompare
|
|||
|
||||
const records = [
|
||||
DocumentRecordType.create({}),
|
||||
PageRecordType.create({ index: 'a0', name: 'page 2' }),
|
||||
PageRecordType.create({ index: ZERO_INDEX_KEY, name: 'page 2' }),
|
||||
].sort(compareById)
|
||||
|
||||
const makeSnapshot = (records: TLRecord[], others: Partial<RoomSnapshot> = {}) => ({
|
||||
|
@ -37,7 +37,7 @@ const oldArrow: TLBaseShape<'arrow', Omit<TLArrowShapeProps, 'labelColor'>> = {
|
|||
typeName: 'shape',
|
||||
type: 'arrow',
|
||||
id: 'shape:old_arrow' as TLShapeId,
|
||||
index: 'a0',
|
||||
index: ZERO_INDEX_KEY,
|
||||
isLocked: false,
|
||||
parentId: PageRecordType.createId(),
|
||||
rotation: 0,
|
||||
|
|
|
@ -77,6 +77,27 @@ export function getHashForObject(obj: any): string;
|
|||
// @public
|
||||
export function getHashForString(string: string): string;
|
||||
|
||||
// @public
|
||||
export function getIndexAbove(below: IndexKey): IndexKey;
|
||||
|
||||
// @public
|
||||
export function getIndexBelow(above: IndexKey): IndexKey;
|
||||
|
||||
// @public
|
||||
export function getIndexBetween(below: IndexKey, above?: IndexKey): IndexKey;
|
||||
|
||||
// @public
|
||||
export function getIndices(n: number, start?: IndexKey): IndexKey[];
|
||||
|
||||
// @public
|
||||
export function getIndicesAbove(below: IndexKey, n: number): IndexKey[];
|
||||
|
||||
// @public
|
||||
export function getIndicesBelow(above: IndexKey, n: number): IndexKey[];
|
||||
|
||||
// @public
|
||||
export function getIndicesBetween(below: IndexKey | undefined, above: IndexKey | undefined, n: number): IndexKey[];
|
||||
|
||||
// @internal (undocumented)
|
||||
export function getOwnProperty<K extends string, V>(obj: Partial<Record<K, V>>, key: K): undefined | V;
|
||||
|
||||
|
@ -86,6 +107,11 @@ export function getOwnProperty(obj: object, key: string): unknown;
|
|||
// @internal (undocumented)
|
||||
export function hasOwnProperty(obj: object, key: string): boolean;
|
||||
|
||||
// @public
|
||||
export type IndexKey = string & {
|
||||
__orderKey: true;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function invLerp(a: number, b: number, t: number): number;
|
||||
|
||||
|
@ -252,6 +278,11 @@ export function sortById<T extends {
|
|||
id: any;
|
||||
}>(a: T, b: T): -1 | 1;
|
||||
|
||||
// @public
|
||||
export function sortByIndex<T extends {
|
||||
index: IndexKey;
|
||||
}>(a: T, b: T): -1 | 0 | 1;
|
||||
|
||||
// @public (undocumented)
|
||||
const structuredClone_2: <T>(i: T) => T;
|
||||
export { structuredClone_2 as structuredClone }
|
||||
|
@ -262,9 +293,15 @@ export function throttle<T extends (...args: any) => any>(func: T, limit: number
|
|||
// @internal
|
||||
export function throttledRaf(fn: () => void): void;
|
||||
|
||||
// @internal (undocumented)
|
||||
export function validateIndexKey(key: string): asserts key is IndexKey;
|
||||
|
||||
// @internal (undocumented)
|
||||
export function warnDeprecatedGetter(name: string): void;
|
||||
|
||||
// @public
|
||||
export const ZERO_INDEX_KEY: IndexKey;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
```
|
||||
|
|
|
@ -790,6 +790,483 @@
|
|||
],
|
||||
"name": "getHashForString"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!getIndexAbove:function(1)",
|
||||
"docComment": "/**\n * Get the index above a given index.\n *\n * @param below - The index below.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndexAbove(below: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "below",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndexAbove"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!getIndexBelow:function(1)",
|
||||
"docComment": "/**\n * Get the index below a given index.\n *\n * @param above - The index above.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndexBelow(above: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "above",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndexBelow"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!getIndexBetween:function(1)",
|
||||
"docComment": "/**\n * Get the index between two indices.\n *\n * @param below - The index below.\n *\n * @param above - The index above.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndexBetween(below: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", above?: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "below",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "above",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
],
|
||||
"name": "getIndexBetween"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!getIndices:function(1)",
|
||||
"docComment": "/**\n * Get n number of indices, starting at an index.\n *\n * @param n - The number of indices to get.\n *\n * @param start - The index to start at.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndices(n: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", start?: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 7
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "n",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "start",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": true
|
||||
}
|
||||
],
|
||||
"name": "getIndices"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!getIndicesAbove:function(1)",
|
||||
"docComment": "/**\n * Get a number of indices above an index.\n *\n * @param below - The index below.\n *\n * @param n - The number of indices to get.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndicesAbove(below: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", n: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 7
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "below",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "n",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndicesAbove"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!getIndicesBelow:function(1)",
|
||||
"docComment": "/**\n * Get a number of indices below an index.\n *\n * @param above - The index above.\n *\n * @param n - The number of indices to get.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndicesBelow(above: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", n: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 7
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "above",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "n",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 4
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndicesBelow"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!getIndicesBetween:function(1)",
|
||||
"docComment": "/**\n * Get a number of indices between two indices.\n *\n * @param below - The index below.\n *\n * @param above - The index above.\n *\n * @param n - The number of indices to get.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function getIndicesBetween(below: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | undefined"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", above: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": " | undefined"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", n: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "number"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "[]"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 9,
|
||||
"endIndex": 11
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "below",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 3
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "above",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 4,
|
||||
"endIndex": 6
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "n",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 7,
|
||||
"endIndex": 8
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"name": "getIndicesBetween"
|
||||
},
|
||||
{
|
||||
"kind": "TypeAlias",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type",
|
||||
"docComment": "/**\n * A string made up of an integer part followed by a fraction part. The fraction point consists of zero or more digits with no trailing zeros. Based on {@link https://observablehq.com/@dgreensp/implementing-fractional-indexing}.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export type IndexKey = "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string & {\n __orderKey: true;\n}"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/IndexKey.ts",
|
||||
"releaseTag": "Public",
|
||||
"name": "IndexKey",
|
||||
"typeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!invLerp:function(1)",
|
||||
|
@ -2666,6 +3143,97 @@
|
|||
],
|
||||
"name": "sortById"
|
||||
},
|
||||
{
|
||||
"kind": "Function",
|
||||
"canonicalReference": "@tldraw/utils!sortByIndex:function(1)",
|
||||
"docComment": "/**\n * Sort by index.\n *\n * @param a - An object with an index property.\n *\n * @param b - An object with an index property.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "export declare function sortByIndex<T extends "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "{\n index: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";\n}"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ">(a: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "T"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ", b: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "T"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "-1 | 0 | 1"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 9,
|
||||
"endIndex": 10
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "a",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 5,
|
||||
"endIndex": 6
|
||||
},
|
||||
"isOptional": false
|
||||
},
|
||||
{
|
||||
"parameterName": "b",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 7,
|
||||
"endIndex": 8
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"typeParameters": [
|
||||
{
|
||||
"typeParameterName": "T",
|
||||
"constraintTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 4
|
||||
},
|
||||
"defaultTypeTokenRange": {
|
||||
"startIndex": 0,
|
||||
"endIndex": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"name": "sortByIndex"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"canonicalReference": "@tldraw/utils!structuredClone_2:var",
|
||||
|
@ -2788,6 +3356,30 @@
|
|||
}
|
||||
],
|
||||
"name": "throttle"
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"canonicalReference": "@tldraw/utils!ZERO_INDEX_KEY:var",
|
||||
"docComment": "/**\n * The index key for the first index - 'a0'.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "ZERO_INDEX_KEY: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/utils/src/lib/reordering/reordering.ts",
|
||||
"isReadonly": true,
|
||||
"releaseTag": "Public",
|
||||
"name": "ZERO_INDEX_KEY",
|
||||
"variableTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -39,6 +39,19 @@ export {
|
|||
} from './lib/object'
|
||||
export { PngHelpers } from './lib/png'
|
||||
export { rafThrottle, throttledRaf } from './lib/raf'
|
||||
export { type IndexKey } from './lib/reordering/IndexKey'
|
||||
export {
|
||||
ZERO_INDEX_KEY,
|
||||
getIndexAbove,
|
||||
getIndexBelow,
|
||||
getIndexBetween,
|
||||
getIndices,
|
||||
getIndicesAbove,
|
||||
getIndicesBelow,
|
||||
getIndicesBetween,
|
||||
sortByIndex,
|
||||
validateIndexKey,
|
||||
} from './lib/reordering/reordering'
|
||||
export { sortById } from './lib/sort'
|
||||
export type { Expand, RecursivePartial, Required } from './lib/types'
|
||||
export { isDefined, isNonNull, isNonNullish, structuredClone } from './lib/value'
|
||||
|
|
8
packages/utils/src/lib/reordering/IndexKey.ts
Normal file
8
packages/utils/src/lib/reordering/IndexKey.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* A string made up of an integer part followed by a fraction part. The fraction point consists of
|
||||
* zero or more digits with no trailing zeros. Based on
|
||||
* {@link https://observablehq.com/@dgreensp/implementing-fractional-indexing}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type IndexKey = string & { __orderKey: true }
|
|
@ -1,6 +1,8 @@
|
|||
import { IndexKey } from '../IndexKey'
|
||||
import { generateNKeysBetween } from './dgreensp'
|
||||
|
||||
const generateKeyBetween = (a?: string, b?: string) => generateNKeysBetween(a, b, 1)[0]
|
||||
const generateKeyBetween = (a?: string, b?: string) =>
|
||||
generateNKeysBetween(a as IndexKey, b as IndexKey, 1)[0]
|
||||
|
||||
describe('get order between', () => {
|
||||
it('passes tests', () => {
|
||||
|
@ -40,11 +42,11 @@ describe('get order between', () => {
|
|||
describe('generateNKeysBetween', () => {
|
||||
it('Gets the correct orders between', () => {
|
||||
expect(generateNKeysBetween(undefined, undefined, 5).join(' ')).toBe('a0 a1 a2 a3 a4')
|
||||
expect(generateNKeysBetween('a4', undefined, 10).join(' ')).toBe(
|
||||
expect(generateNKeysBetween('a4' as IndexKey, undefined, 10).join(' ')).toBe(
|
||||
'a5 a6 a7 a8 a9 aA aB aC aD aE'
|
||||
)
|
||||
expect(generateNKeysBetween(undefined, 'a0', 5).join(' ')).toBe('Zv Zw Zx Zy Zz')
|
||||
expect(generateNKeysBetween('a0', 'a2', 20).join(' ')).toBe(
|
||||
expect(generateNKeysBetween(undefined, 'a0' as IndexKey, 5).join(' ')).toBe('Zv Zw Zx Zy Zz')
|
||||
expect(generateNKeysBetween('a0' as IndexKey, 'a2' as IndexKey, 20).join(' ')).toBe(
|
||||
'a04 a08 a0G a0K a0O a0V a0Z a0d a0l a0t a1 a14 a18 a1G a1O a1V a1Z a1d a1l a1t'
|
||||
)
|
||||
})
|
|
@ -1,8 +1,10 @@
|
|||
// Adapted from https://observablehq.com/@dgreensp/implementing-fractional-indexing
|
||||
// by @dgreensp (twitter @DavidLG)
|
||||
|
||||
import { IndexKey } from '../IndexKey'
|
||||
|
||||
const DIGITS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||
const INTEGER_ZERO = 'a0'
|
||||
export const INTEGER_ZERO = 'a0' as IndexKey
|
||||
const SMALLEST_INTEGER = 'A00000000000000000000000000'
|
||||
|
||||
/**
|
||||
|
@ -161,7 +163,7 @@ function getIntegerPart(index: string): string {
|
|||
*
|
||||
* @param x - The index to validate.
|
||||
*/
|
||||
function validateOrder(index: string): asserts index is string {
|
||||
export function validateOrder(index: string): asserts index is string {
|
||||
if (index === SMALLEST_INTEGER) {
|
||||
throw new Error('invalid index: ' + index)
|
||||
}
|
||||
|
@ -175,19 +177,13 @@ function validateOrder(index: string): asserts index is string {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A string made up of an integer part followed by a fraction part. The fraction point consists of
|
||||
* zero or more digits with no trailing zeros.
|
||||
*/
|
||||
type OrderKey = string
|
||||
|
||||
/**
|
||||
* Generate an index key at the midpoint between a start and end.
|
||||
*
|
||||
* @param a - The start index key string.
|
||||
* @param b - The end index key string, greater than A.
|
||||
*/
|
||||
function generateKeyBetween(a: OrderKey | undefined, b: OrderKey | undefined): OrderKey {
|
||||
function generateKeyBetween(a: IndexKey | undefined, b: IndexKey | undefined): IndexKey {
|
||||
if (a !== undefined) validateOrder(a)
|
||||
if (b !== undefined) validateOrder(b)
|
||||
if (a !== undefined && b !== undefined && a >= b) {
|
||||
|
@ -201,31 +197,31 @@ function generateKeyBetween(a: OrderKey | undefined, b: OrderKey | undefined): O
|
|||
const ib = getIntegerPart(b)
|
||||
const fb = b.slice(ib.length)
|
||||
if (ib === SMALLEST_INTEGER) {
|
||||
return ib + midpoint('', fb)
|
||||
return (ib + midpoint('', fb)) as IndexKey
|
||||
}
|
||||
if (ib < b) {
|
||||
return ib
|
||||
return ib as IndexKey
|
||||
}
|
||||
const ibl = decrementInteger(ib)
|
||||
isNotUndefined(ibl)
|
||||
return ibl
|
||||
return ibl as IndexKey
|
||||
}
|
||||
if (b === undefined) {
|
||||
const ia = getIntegerPart(a)
|
||||
const fa = a.slice(ia.length)
|
||||
const i = incrementInteger(ia)
|
||||
return i === undefined ? ia + midpoint(fa, undefined) : i
|
||||
return (i === undefined ? ia + midpoint(fa, undefined) : i) as IndexKey
|
||||
}
|
||||
const ia = getIntegerPart(a)
|
||||
const fa = a.slice(ia.length)
|
||||
const ib = getIntegerPart(b)
|
||||
const fb = b.slice(ib.length)
|
||||
if (ia === ib) {
|
||||
return ia + midpoint(fa, fb)
|
||||
return (ia + midpoint(fa, fb)) as IndexKey
|
||||
}
|
||||
const i = incrementInteger(ia)
|
||||
isNotUndefined(i)
|
||||
return i < b ? i : ia + midpoint(fa, undefined)
|
||||
return (i < b ? i : ia + midpoint(fa, undefined)) as IndexKey
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,10 +232,10 @@ function generateKeyBetween(a: OrderKey | undefined, b: OrderKey | undefined): O
|
|||
* @param n - The number of index keys to generate.
|
||||
*/
|
||||
export function generateNKeysBetween(
|
||||
a: string | undefined,
|
||||
b: string | undefined,
|
||||
a: IndexKey | undefined,
|
||||
b: IndexKey | undefined,
|
||||
n: number
|
||||
): string[] {
|
||||
): IndexKey[] {
|
||||
if (n === 0) return []
|
||||
if (n === 1) return [generateKeyBetween(a, b)]
|
||||
if (b === undefined) {
|
|
@ -1,4 +1,16 @@
|
|||
import { generateNKeysBetween } from './dgreensp/dgreensp'
|
||||
import { IndexKey } from './IndexKey'
|
||||
import { INTEGER_ZERO, generateNKeysBetween, validateOrder } from './dgreensp/dgreensp'
|
||||
|
||||
/**
|
||||
* The index key for the first index - 'a0'.
|
||||
* @public
|
||||
*/
|
||||
export const ZERO_INDEX_KEY = INTEGER_ZERO
|
||||
|
||||
/** @internal */
|
||||
export function validateIndexKey(key: string): asserts key is IndexKey {
|
||||
validateOrder(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a number of indices between two indices.
|
||||
|
@ -7,7 +19,11 @@ import { generateNKeysBetween } from './dgreensp/dgreensp'
|
|||
* @param n - The number of indices to get.
|
||||
* @public
|
||||
*/
|
||||
export function getIndicesBetween(below: string | undefined, above: string | undefined, n: number) {
|
||||
export function getIndicesBetween(
|
||||
below: IndexKey | undefined,
|
||||
above: IndexKey | undefined,
|
||||
n: number
|
||||
) {
|
||||
return generateNKeysBetween(below, above, n)
|
||||
}
|
||||
|
||||
|
@ -17,7 +33,7 @@ export function getIndicesBetween(below: string | undefined, above: string | und
|
|||
* @param n - The number of indices to get.
|
||||
* @public
|
||||
*/
|
||||
export function getIndicesAbove(below: string, n: number) {
|
||||
export function getIndicesAbove(below: IndexKey, n: number) {
|
||||
return generateNKeysBetween(below, undefined, n)
|
||||
}
|
||||
|
||||
|
@ -27,7 +43,7 @@ export function getIndicesAbove(below: string, n: number) {
|
|||
* @param n - The number of indices to get.
|
||||
* @public
|
||||
*/
|
||||
export function getIndicesBelow(above: string, n: number) {
|
||||
export function getIndicesBelow(above: IndexKey, n: number) {
|
||||
return generateNKeysBetween(undefined, above, n)
|
||||
}
|
||||
|
||||
|
@ -37,7 +53,7 @@ export function getIndicesBelow(above: string, n: number) {
|
|||
* @param above - The index above.
|
||||
* @public
|
||||
*/
|
||||
export function getIndexBetween(below: string, above?: string) {
|
||||
export function getIndexBetween(below: IndexKey, above?: IndexKey) {
|
||||
return generateNKeysBetween(below, above, 1)[0]
|
||||
}
|
||||
|
||||
|
@ -46,7 +62,7 @@ export function getIndexBetween(below: string, above?: string) {
|
|||
* @param below - The index below.
|
||||
* @public
|
||||
*/
|
||||
export function getIndexAbove(below: string) {
|
||||
export function getIndexAbove(below: IndexKey) {
|
||||
return generateNKeysBetween(below, undefined, 1)[0]
|
||||
}
|
||||
|
||||
|
@ -55,7 +71,7 @@ export function getIndexAbove(below: string) {
|
|||
* @param above - The index above.
|
||||
* @public
|
||||
*/
|
||||
export function getIndexBelow(above: string) {
|
||||
export function getIndexBelow(above: IndexKey) {
|
||||
return generateNKeysBetween(undefined, above, 1)[0]
|
||||
}
|
||||
|
||||
|
@ -65,7 +81,7 @@ export function getIndexBelow(above: string) {
|
|||
* @param start - The index to start at.
|
||||
* @public
|
||||
*/
|
||||
export function getIndices(n: number, start = 'a1') {
|
||||
export function getIndices(n: number, start = 'a1' as IndexKey) {
|
||||
return [start, ...generateNKeysBetween(start, undefined, n)]
|
||||
}
|
||||
|
||||
|
@ -74,7 +90,7 @@ export function getIndices(n: number, start = 'a1') {
|
|||
* @param a - An object with an index property.
|
||||
* @param b - An object with an index property.
|
||||
* @public */
|
||||
export function sortByIndex<T extends { index: string }>(a: T, b: T) {
|
||||
export function sortByIndex<T extends { index: IndexKey }>(a: T, b: T) {
|
||||
if (a.index < b.index) {
|
||||
return -1
|
||||
} else if (a.index > b.index) {
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
```ts
|
||||
|
||||
import { IndexKey } from '@tldraw/utils';
|
||||
import { JsonValue } from '@tldraw/utils';
|
||||
|
||||
// @public
|
||||
|
@ -44,6 +45,9 @@ export class DictValidator<Key extends string, Value> extends Validator<Record<K
|
|||
readonly valueValidator: Validatable<Value>;
|
||||
}
|
||||
|
||||
// @public
|
||||
const indexKey: Validator<IndexKey>;
|
||||
|
||||
// @public
|
||||
const integer: Validator<number>;
|
||||
|
||||
|
@ -155,7 +159,8 @@ declare namespace T {
|
|||
unknownObject,
|
||||
jsonValue,
|
||||
linkUrl,
|
||||
srcUrl
|
||||
srcUrl,
|
||||
indexKey
|
||||
}
|
||||
}
|
||||
export { T }
|
||||
|
|
|
@ -1543,6 +1543,43 @@
|
|||
},
|
||||
"implementsTokenRanges": []
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"canonicalReference": "@tldraw/validate!T.indexKey:var",
|
||||
"docComment": "/**\n * Validates that a value is an IndexKey.\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "indexKey: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Validator",
|
||||
"canonicalReference": "@tldraw/validate!Validator:class"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<"
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "IndexKey",
|
||||
"canonicalReference": "@tldraw/utils!IndexKey:type"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ">"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/validate/src/lib/validation.ts",
|
||||
"isReadonly": true,
|
||||
"releaseTag": "Public",
|
||||
"name": "indexKey",
|
||||
"variableTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Variable",
|
||||
"canonicalReference": "@tldraw/validate!T.integer:var",
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import { JsonValue, exhaustiveSwitchError, getOwnProperty, hasOwnProperty } from '@tldraw/utils'
|
||||
import {
|
||||
IndexKey,
|
||||
JsonValue,
|
||||
exhaustiveSwitchError,
|
||||
getOwnProperty,
|
||||
hasOwnProperty,
|
||||
validateIndexKey,
|
||||
} from '@tldraw/utils'
|
||||
|
||||
/** @public */
|
||||
export type ValidatorFn<T> = (value: unknown) => T
|
||||
|
@ -668,3 +675,16 @@ export const srcUrl = string.check((value) => {
|
|||
)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Validates that a value is an IndexKey.
|
||||
* @public
|
||||
*/
|
||||
export const indexKey = string.refine<IndexKey>((key) => {
|
||||
try {
|
||||
validateIndexKey(key)
|
||||
return key
|
||||
} catch {
|
||||
throw new ValidationError(`Expected an index key, got ${JSON.stringify(key)}`)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -122,3 +122,18 @@ describe('T.refine', () => {
|
|||
describe('T.check', () => {
|
||||
it.todo('Adds a check to a validator.')
|
||||
})
|
||||
|
||||
describe('T.indexKey', () => {
|
||||
it('validates index keys', () => {
|
||||
expect(T.indexKey.validate('a0')).toBe('a0')
|
||||
expect(T.indexKey.validate('a1J')).toBe('a1J')
|
||||
})
|
||||
it('rejects invalid index keys', () => {
|
||||
expect(() => T.indexKey.validate('a')).toThrowErrorMatchingInlineSnapshot(
|
||||
`"At null: Expected an index key, got "a""`
|
||||
)
|
||||
expect(() => T.indexKey.validate('')).toThrowErrorMatchingInlineSnapshot(
|
||||
`"At null: Expected an index key, got """`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue