-
Notifications
You must be signed in to change notification settings - Fork 0
Selection Method Reference
SelectionState는 DOM focus가 아니라 JSON document 위의 headless address state다. Selection은 pointer, range, caret, context를 저장하고, document patch 이후에도 가능한 한 같은 논리적 대상을 따라가도록 설계되어 있다.
Selection은 createJSONDocument(..., { selection: true }) 또는 selection: SelectionOptions로 켠 경우 doc.selection에서 접근한다.
type SelectionMode = "single" | "multiple" | "extended";
type SelectionEdge = "before" | "after";
type SelectionAffinity = "forward" | "backward";
type SelectionType = "None" | "Caret" | "Range";
type SelectionSource = Pointer | ReadonlyArray<Pointer>;Selection point는 pointer 문자열이거나 offset/edge/affinity를 가진 object다.
interface SelectionPointObject {
path: Pointer;
offset?: number;
edge?: SelectionEdge;
affinity?: SelectionAffinity;
}
type SelectionPoint = Pointer | SelectionPointObject;
interface SelectionRange {
anchor: SelectionPoint;
focus: SelectionPoint;
}
type SelectionRangeInput = SelectionPoint | SelectionRange;문자열 pointer는 해당 node 전체 또는 node boundary를 뜻하는 고수준 point로 쓴다. 문자열 내부 caret이나 부분 text range가 필요하면 object point의 offset을 쓴다.
readonly selectedPointers: ReadonlyArray<Pointer>;
readonly selectionRanges: ReadonlyArray<SelectionRange>;
readonly primaryIndex: number;
readonly anchor: SelectionPoint | null;
readonly focus: SelectionPoint | null;
readonly context?: SelectionContext | undefined;
readonly rangeCount: number;
readonly selectedCount: number;
readonly hasSelection: boolean;
readonly isCollapsed: boolean;
readonly type: SelectionType;
readonly primaryRange: SelectionRange | null;
readonly anchorPointer: Pointer | null;
readonly focusPointer: Pointer | null;
readonly selectedSource: SelectionSource | null;
readonly primaryPointer: Pointer | null;
readonly caret: SelectionPoint | null;
readonly caretPointer: Pointer | null;현재 선택된 pointer 목록이다. copy, cut, delete, move의 source로 그대로 넘길 수 있다.
선택이 없으면 null, pointer 하나면 Pointer, 여러 개면 ReadonlyArray<Pointer>다.
const source = doc.selection?.selectedSource;
if (source) doc.copy(source);현재 selection의 primary range에서 대표 pointer를 반환한다. duplicate()처럼 하나의 source가 필요한 command가 기본값으로 사용한다.
Selection이 collapsed 상태일 때 현재 caret point와 pointer를 반환한다. Text editor adapter에서 주로 쓴다.
collapse(point: SelectionPoint): void;Selection을 하나의 collapsed caret으로 만든다.
doc.selection?.collapse({ path: "/title", offset: 3 });setBaseAndExtent(anchor: SelectionPoint, focus: SelectionPoint): void;Anchor와 focus를 직접 지정해 range를 만든다. DOM Selection의 base/extent 모델과 비슷하지만, 대상은 DOM node가 아니라 JSON Pointer다.
extend(point: SelectionPoint): void;현재 anchor를 유지하고 focus를 새 point로 이동한다. Shift+Arrow, drag selection, range extension에 적합하다.
addRange(pointOrRange: SelectionPoint | SelectionRange): void;기존 selection에 range를 추가한다. mode: "single"에서는 최종 selection이 하나로 정규화될 수 있다.
removeRange(pointOrRangeOrIndex: SelectionPoint | SelectionRange | number): void;Range 또는 index를 기준으로 selection에서 제거한다.
toggleRange(pointOrRange: SelectionPoint | SelectionRange): void;같은 range가 있으면 제거하고, 없으면 추가한다.
togglePointer(pointer: Pointer): void;Pointer 단위 selection을 toggle한다. List item, table row, 블록 selection UI에서 자주 쓴다.
selectRanges(
ranges: ReadonlyArray<SelectionRangeInput>,
anchor?: SelectionPoint | null,
focus?: SelectionPoint | null,
primaryIndex?: number,
): void;Selection 전체를 명시적으로 교체한다.
외부 selection snapshot, search result, review comment anchor를 selection으로 복원할 때 적합하다.
empty(): void;Selection을 비운다.
Cursor method는 document state를 순회 가능한 point 목록으로 보고 이동한다.
type SelectionCursorDirection = "first" | "previous" | "next" | "last";
interface SelectionCursorOptions {
points?: ReadonlyArray<SelectionPoint>;
query?: string;
scope?: Pointer;
includeScope?: boolean;
wrap?: boolean;
}points를 직접 주면 그 순서를 사용한다. query를 주면 JSONPath 검색 결과를 cursor scope로 쓴다. scope는 탐색 범위를 제한한다.
moveCursor(
direction: SelectionCursorDirection,
options?: SelectionCursorOptions,
): SelectionCursorResult;Cursor를 이동하고 selection을 collapsed 상태로 바꾼다.
doc.selection?.moveCursor("next", { scope: "/items" });extendCursor(
direction: SelectionCursorDirection,
options?: SelectionCursorOptions,
): SelectionCursorResult;Cursor target까지 selection range를 확장한다.
resolveCursor(
direction: SelectionCursorDirection,
options?: SelectionCursorOptions,
): SelectionCursorResult;Selection을 바꾸지 않고 이동할 target만 계산한다.
주요 실패:
invalid_pointerpath_not_foundsyntax_errorempty_scopecursor_boundary
orderPrimaryRange(options?: SelectionOrderOptions): SelectionRangeOrderResult;Primary range의 anchor/focus를 document traversal order 기준으로 정렬한다.
orderRanges(options?: SelectionOrderOptions): SelectionRangesOrderResult;모든 range를 document traversal order 기준으로 정렬한다.
중첩 range, 역방향 range, multi-range selection을 처리하기 전에 호출한다.
spansForPointer(
pointer: Pointer,
options?: SelectionSpanOptions,
): SelectionPointerSpansResult;특정 pointer 안에서 selection이 차지하는 span을 계산한다.
interface SelectionSpanOptions extends SelectionOrderOptions {
length?: number;
getLength?: (pointer: Pointer, value: unknown) => number | null | undefined;
}Text input adapter는 startOffset, endOffset, collapsed, full을 보고 실제 문자열 편집 범위를 만든다.
selectScope(options?: SelectionScopeOptions): SelectionScopeResult;Scope 안의 선택 가능한 point들을 찾아 selection으로 만든다.
doc.selection?.selectScope({ scope: "/items", query: "$[*]" });resolveScope(options?: SelectionScopeOptions): SelectionScopeTarget;Selection을 바꾸지 않고 scope target만 계산한다.
Text editing method는 selection을 patch로 낮춘다. 직접 state를 바꾸지 않고, patch 또는 edit 계획을 반환한다.
textEdits(
replacement: string,
options?: SelectionTextEditOptions,
): SelectionTextEditsResult;Selection을 문자열 replacement edit 목록으로 변환한다. 실제 document 변경은 호출자가 별도로 수행한다.
textPatch(
replacement: string,
options?: SelectionTextEditOptions,
): ReplaceSelectionTextResult;Selection을 JSON Patch와 다음 selection snapshot으로 변환한다.
const plan = doc.selection?.textPatch("hello");
if (plan?.ok) {
doc.commit(plan.patch, { selection: plan.selection });
}deleteText(options?: SelectionTextDeleteOptions): DeleteSelectionTextResult;Selection range를 삭제하거나 collapsed caret 기준으로 앞/뒤 문자를 삭제하는 patch를 만든다.
doc.selection?.deleteText({ direction: "backward", count: 1 });Text edit 주요 실패:
missing_lengthmulti_pointer_rangeoverlapping_rangescursor_boundarypath_not_foundnot_stringpoint_not_in_order
setContext(context: SelectionContext): void;
clearContext(): void;Selection에 제품별 context를 붙이거나 제거한다.
isSelected(pointer: Pointer): boolean;Pointer가 현재 selection에 포함되는지 확인한다.
snapshot(): SelectionSnap;
toJSON(): SelectionSnap;현재 selection을 serializable snapshot으로 반환한다. History metadata, persistence, test assertion에 사용한다.
restore(snapshot: SelectionSnap): void;Snapshot으로 selection을 복원한다.
subscribe(listener: (
snapshot: SelectionSnap,
previous: SelectionSnap,
) => void): () => void;Selection 변경을 구독한다. 반환값은 unsubscribe 함수다.
const unsubscribe = doc.selection?.subscribe((next, prev) => {
console.log(prev, next);
});
unsubscribe?.();Document 변경이 성공하면 selection은 applied patch를 기준으로 자동 조정된다.
예를 들어 /items/0 앞에 새 item이 삽입되면 기존 /items/0 selection은 /items/1처럼 이동할 수 있다. 삭제된 target은 selection에서 제거될 수 있다.
이 동작 때문에 selection은 DOM focus보다 document state에 가까운 모델이다.