@lvce-editor/api 9.38.0 → 9.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,8 @@ export interface VirtualDomViewInstance {
51
51
  readonly getCss?: () => string | Promise<string>;
52
52
  readonly getMenuEntries?: (menuId: string) => readonly MenuEntry[] | Promise<readonly MenuEntry[]>;
53
53
  readonly handleEvent?: (event: ViewEvent) => unknown;
54
+ /** Whether this document has edits that have not been saved. */
55
+ readonly isDirty?: () => boolean;
54
56
  readonly render: () => readonly VirtualDomNode[] | Promise<readonly VirtualDomNode[]>;
55
57
  readonly renderActions?: () => readonly ViewAction[] | Promise<readonly ViewAction[]>;
56
58
  readonly renderActionsDom?: () => readonly VirtualDomNode[];
@@ -59,6 +61,8 @@ export interface VirtualDomViewInstance {
59
61
  readonly renderSelections?: () => readonly ViewSelection[] | Promise<readonly ViewSelection[]>;
60
62
  readonly renderStatusBarItems?: () => readonly StatusBarItem[] | Promise<readonly StatusBarItem[]>;
61
63
  readonly renderTitle?: () => string | Promise<string>;
64
+ /** Persist committed edits. Reject on failure and retain dirty state until persistence succeeds. */
65
+ readonly save?: () => void | Promise<void>;
62
66
  readonly saveState?: () => unknown;
63
67
  }
64
68
  export type ViewCommand<State = VirtualDomViewInstance, Args extends readonly any[] = readonly any[]> = (state: State, ...args: Args) => State | Promise<State>;
@@ -87,6 +91,7 @@ export interface StatefulView<State = unknown> extends ViewBase<State> {
87
91
  readonly getCss?: (state: State) => string | Promise<string>;
88
92
  readonly getMenuEntries?: (state: State, menuId: string) => readonly MenuEntry[] | Promise<readonly MenuEntry[]>;
89
93
  readonly handleEvent?: (state: State, event: ViewEvent) => State | Promise<State>;
94
+ readonly isDirty?: (state: State) => boolean;
90
95
  readonly render: (state: State) => readonly VirtualDomNode[] | Promise<readonly VirtualDomNode[]>;
91
96
  readonly renderActions?: (state: State) => readonly ViewAction[] | Promise<readonly ViewAction[]>;
92
97
  readonly renderActionsDom?: (state: State) => readonly VirtualDomNode[];
@@ -95,6 +100,7 @@ export interface StatefulView<State = unknown> extends ViewBase<State> {
95
100
  readonly renderSelections?: (state: State) => readonly ViewSelection[] | Promise<readonly ViewSelection[]>;
96
101
  readonly renderStatusBarItems?: (state: State) => readonly StatusBarItem[] | Promise<readonly StatusBarItem[]>;
97
102
  readonly renderTitle?: (state: State) => string | Promise<string>;
103
+ readonly save?: (state: State) => State | Promise<State>;
98
104
  readonly saveState?: (state: State) => unknown;
99
105
  }
100
106
  export type View<State = unknown, ComponentState = unknown> = InstanceView<State, ComponentState> | StatefulView<State>;
@@ -116,6 +122,7 @@ export interface ViewRenderResultDom {
116
122
  readonly css?: string;
117
123
  readonly dom: readonly VirtualDomNode[];
118
124
  readonly focusSelector?: string;
125
+ readonly modified?: boolean;
119
126
  readonly scrollPosition?: ViewScrollPosition;
120
127
  readonly selections?: readonly ViewSelection[];
121
128
  readonly title?: string;
@@ -124,6 +131,7 @@ export interface ViewRenderResultDom {
124
131
  export interface ViewRenderResultPatches {
125
132
  readonly css?: string;
126
133
  readonly focusSelector?: string;
134
+ readonly modified?: boolean;
127
135
  readonly patches: readonly unknown[];
128
136
  readonly scrollPosition?: ViewScrollPosition;
129
137
  readonly selections?: readonly ViewSelection[];
@@ -236,6 +236,16 @@ const createStatefulViewInstance = (view, uid) => {
236
236
  ViewletStates.replace(uid, newState);
237
237
  };
238
238
  }
239
+ if (view.isDirty) {
240
+ instance.isDirty = () => view.isDirty(ViewletStates.get(uid));
241
+ }
242
+ if (view.save) {
243
+ instance.save = async () => {
244
+ const newState = await view.save(ViewletStates.get(uid));
245
+ assertViewState(view.id, newState);
246
+ ViewletStates.replace(uid, newState);
247
+ };
248
+ }
239
249
  if (view.renderActions) {
240
250
  instance.renderActions = () => view.renderActions(ViewletStates.get(uid));
241
251
  }
@@ -506,7 +516,14 @@ const withRenderMetadata = async (uid, viewId, result, instance, contextChange)
506
516
  const resultWithScrollPosition = await withScrollPosition(resultWithSelections, instance);
507
517
  const resultWithTitle = await withTitle(resultWithScrollPosition, instance);
508
518
  await ViewStatusBarItems.renderViewStatusBarItems(uid, viewId, instance);
509
- return resultWithTitle;
519
+ if (typeof instance.isDirty !== 'function') {
520
+ return resultWithTitle;
521
+ }
522
+ const modified = instance.isDirty();
523
+ if (typeof modified !== 'boolean') {
524
+ throw new ExtensionApiError('view isDirty result must be a boolean');
525
+ }
526
+ return { ...resultWithTitle, modified };
510
527
  };
511
528
  const maybeClearContext = async (uid, viewId) => {
512
529
  if (!contexts[uid]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/api",
3
- "version": "9.38.0",
3
+ "version": "9.39.0",
4
4
  "description": "Tree-shakeable extension API for Lvce Editor extensions.",
5
5
  "keywords": [
6
6
  "lvce-editor",