@formicoidea/labre-core 0.26.1 → 0.29.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.
- package/dist/_pkgs/affine-block-database/data-source.d.ts +28 -40
- package/dist/_pkgs/affine-block-database/data-source.js +116 -178
- package/dist/_pkgs/affine-block-database/database-block.d.ts +10 -3
- package/dist/_pkgs/affine-block-database/database-block.js +110 -70
- package/dist/_pkgs/affine-block-database/external-data-source.d.ts +69 -0
- package/dist/_pkgs/affine-block-database/external-data-source.js +144 -0
- package/dist/_pkgs/affine-block-database/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-database/index.js +1 -0
- package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-linked-doc-block.js +6 -7
- package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/components/embed-synced-doc-card.js +5 -5
- package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +1 -1
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.d.ts +18 -0
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.d.ts +18 -0
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +104 -2
- package/dist/_pkgs/affine-block-root/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-root/index.js +1 -0
- package/dist/_pkgs/affine-block-root/keyboard/keyboard-manager.js +3 -21
- package/dist/_pkgs/affine-block-root/keyboard/shortcuts.d.ts +8 -0
- package/dist/_pkgs/affine-block-root/keyboard/shortcuts.js +48 -0
- package/dist/_pkgs/affine-block-root/view.js +6 -1
- package/dist/_pkgs/affine-gfx-link/effects.js +2 -2
- package/dist/_pkgs/affine-gfx-link/toolbar/undo-dense-menu.d.ts +3 -0
- package/dist/_pkgs/affine-gfx-link/toolbar/undo-dense-menu.js +12 -0
- package/dist/_pkgs/affine-gfx-link/toolbar/undo-tool-button.d.ts +18 -0
- package/dist/_pkgs/affine-gfx-link/toolbar/undo-tool-button.js +41 -0
- package/dist/_pkgs/affine-gfx-link/undo-tool.d.ts +6 -0
- package/dist/_pkgs/affine-gfx-link/undo-tool.js +17 -0
- package/dist/_pkgs/affine-gfx-link/view.js +4 -2
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-tool-button.js +13 -2
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/senior-tool.d.ts +12 -2
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/senior-tool.js +62 -11
- package/dist/_pkgs/affine-gfx-mindmap/view.d.ts +13 -2
- package/dist/_pkgs/affine-gfx-mindmap/view.js +24 -5
- package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/services/index.js +2 -0
- package/dist/_pkgs/affine-shared/services/keymap-override-service.d.ts +15 -0
- package/dist/_pkgs/affine-shared/services/keymap-override-service.js +26 -0
- package/dist/_pkgs/affine-shared/services/linked-doc-content-service.d.ts +42 -0
- package/dist/_pkgs/affine-shared/services/linked-doc-content-service.js +47 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.d.ts +29 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.js +233 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/effects.js +2 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/spec.d.ts +1 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/spec.js +2 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/view.js +2 -1
- package/dist/_pkgs/data-view/core/data-source/base.d.ts +6 -0
- package/dist/_pkgs/data-view/core/data-source/base.js +6 -0
- package/dist/_pkgs/std/extension/index.d.ts +1 -0
- package/dist/_pkgs/std/extension/index.js +1 -0
- package/dist/_pkgs/std/extension/shortcut.d.ts +69 -0
- package/dist/_pkgs/std/extension/shortcut.js +106 -0
- package/dist/_pkgs/std/gfx/model/surface/element-model.d.ts +7 -0
- package/dist/_pkgs/std/gfx/model/surface/element-model.js +24 -1
- package/dist/_pkgs/std/identifier.d.ts +7 -0
- package/dist/_pkgs/std/identifier.js +6 -0
- package/dist/_pkgs/std/inline/components/v-element.js +5 -2
- package/dist/extensions/view.js +5 -2
- package/dist/flags.d.ts +1 -1
- package/dist/flags.js +4 -1
- package/dist/shortcuts.d.ts +24 -0
- package/dist/shortcuts.js +39 -0
- package/package.json +5 -1
- package/dist/_pkgs/affine-gfx-link/link-tool.d.ts +0 -1
- package/dist/_pkgs/affine-gfx-link/link-tool.js +0 -11
- package/dist/_pkgs/affine-gfx-link/toolbar/link-dense-menu.d.ts +0 -2
- package/dist/_pkgs/affine-gfx-link/toolbar/link-dense-menu.js +0 -26
- package/dist/_pkgs/affine-gfx-link/toolbar/link-tool-button.d.ts +0 -10
- package/dist/_pkgs/affine-gfx-link/toolbar/link-tool-button.js +0 -84
|
@@ -6,7 +6,7 @@ import { toast } from '../affine-components/toast/index.js';
|
|
|
6
6
|
import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '../affine-shared/consts/index.js';
|
|
7
7
|
import { BlockCommentManager, CommentProviderIdentifier, DocModeProvider, NotificationProvider, TelemetryProvider, } from '../affine-shared/services/index.js';
|
|
8
8
|
import { getDropResult } from '../affine-widget-drag-handle/index.js';
|
|
9
|
-
import { createRecordDetail, createUniComponentFromWebComponent, DataViewRootUILogic, defineUniComponent, ExternalGroupByConfigProvider,
|
|
9
|
+
import { createRecordDetail, createUniComponentFromWebComponent, DataViewRootUILogic, defineUniComponent, ExternalGroupByConfigProvider, renderUniLit, uniMap, } from '../data-view/index.js';
|
|
10
10
|
import { widgetPresets } from '../data-view/widget-presets/index.js';
|
|
11
11
|
import { Rect } from '../global/gfx/index.js';
|
|
12
12
|
import { CommentIcon, CopyIcon, DeleteIcon, MoreHorizontalIcon, } from '@blocksuite/icons/lit';
|
|
@@ -16,6 +16,7 @@ import { Slice } from '../store/index.js';
|
|
|
16
16
|
import { autoUpdate } from '@floating-ui/dom';
|
|
17
17
|
import { computed, signal } from '@preact/signals-core';
|
|
18
18
|
import { html, nothing } from 'lit';
|
|
19
|
+
import { keyed } from 'lit/directives/keyed.js';
|
|
19
20
|
import { popSideDetail } from './components/layout.js';
|
|
20
21
|
import { DatabaseConfigExtension } from './config.js';
|
|
21
22
|
import { EditorHostKey } from './context/host-context.js';
|
|
@@ -88,25 +89,10 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
88
89
|
options,
|
|
89
90
|
});
|
|
90
91
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.getAll(ExternalGroupByConfigProvider)
|
|
96
|
-
.forEach(config => {
|
|
97
|
-
dataSource.serviceSet(ExternalGroupByConfigProvider(config.name), config);
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
const provider = this.std.getOptional(DatabaseDataSourceProvider);
|
|
101
|
-
const dataSource = provider && this.model.props.externalSourceId
|
|
102
|
-
? provider.createDataSource(this.model, initFn)
|
|
103
|
-
: new DatabaseBlockDataSource(this.model, initFn);
|
|
104
|
-
const id = currentViewStorage.getCurrentView(this.model.id);
|
|
105
|
-
if (id && dataSource.viewManager.viewGet(id)) {
|
|
106
|
-
dataSource.viewManager.setCurrentView(id);
|
|
107
|
-
}
|
|
108
|
-
return dataSource;
|
|
109
|
-
});
|
|
92
|
+
// Bumps on every rebuild so `renderBlock` can `keyed()` the renderer element,
|
|
93
|
+
// forcing Lit to tear the old one down (disposing its listeners) and mount a
|
|
94
|
+
// fresh one bound to the new source.
|
|
95
|
+
this._sourceVersion = 0;
|
|
110
96
|
this.renderTitle = (dataViewLogic) => {
|
|
111
97
|
return html ` <affine-database-title
|
|
112
98
|
class="${databaseTitleStyles}"
|
|
@@ -219,7 +205,107 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
219
205
|
return databaseSelection?.viewSelection;
|
|
220
206
|
});
|
|
221
207
|
this.virtualPadding$ = signal(0);
|
|
222
|
-
this
|
|
208
|
+
this.#useZeroWidth_accessor_storage = true;
|
|
209
|
+
}
|
|
210
|
+
_buildDataSource() {
|
|
211
|
+
const initFn = (dataSource) => {
|
|
212
|
+
dataSource.serviceSet(EditorHostKey, this.host);
|
|
213
|
+
this.std.provider
|
|
214
|
+
.getAll(ExternalGroupByConfigProvider)
|
|
215
|
+
.forEach(config => {
|
|
216
|
+
dataSource.serviceSet(ExternalGroupByConfigProvider(config.name), config);
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
const provider = this.std.getOptional(DatabaseDataSourceProvider);
|
|
220
|
+
const dataSource = provider && this.model.props.externalSourceId
|
|
221
|
+
? provider.createDataSource(this.model, initFn)
|
|
222
|
+
: new DatabaseBlockDataSource(this.model, initFn);
|
|
223
|
+
const id = currentViewStorage.getCurrentView(this.model.id);
|
|
224
|
+
if (id && dataSource.viewManager.viewGet(id)) {
|
|
225
|
+
dataSource.viewManager.setCurrentView(id);
|
|
226
|
+
}
|
|
227
|
+
return dataSource;
|
|
228
|
+
}
|
|
229
|
+
get dataSource() {
|
|
230
|
+
if (!this._dataSource) {
|
|
231
|
+
this._dataSource = this._buildDataSource();
|
|
232
|
+
}
|
|
233
|
+
return this._dataSource;
|
|
234
|
+
}
|
|
235
|
+
rebuildDataSource() {
|
|
236
|
+
this._dataSource?.dispose();
|
|
237
|
+
this._dataSource = undefined;
|
|
238
|
+
this._dataViewRootLogic = undefined;
|
|
239
|
+
this._sourceVersion++;
|
|
240
|
+
this.requestUpdate();
|
|
241
|
+
}
|
|
242
|
+
get optionsConfig() {
|
|
243
|
+
return {
|
|
244
|
+
configure: (_model, options) => options,
|
|
245
|
+
...this.std.getOptional(DatabaseConfigExtension.identifier),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
get isCommentHighlighted() {
|
|
249
|
+
return (this.std
|
|
250
|
+
.getOptional(BlockCommentManager)
|
|
251
|
+
?.isBlockCommentHighlighted(this.model) ?? false);
|
|
252
|
+
}
|
|
253
|
+
get topContenteditableElement() {
|
|
254
|
+
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
|
255
|
+
return this.closest(EDGELESS_TOP_CONTENTEDITABLE_SELECTOR);
|
|
256
|
+
}
|
|
257
|
+
return this.rootComponent;
|
|
258
|
+
}
|
|
259
|
+
renderDatabaseOps() {
|
|
260
|
+
if (this.dataSource.readonly$.value) {
|
|
261
|
+
return nothing;
|
|
262
|
+
}
|
|
263
|
+
return html ` <div
|
|
264
|
+
data-testid="database-ops"
|
|
265
|
+
class="${databaseOpsStyles}"
|
|
266
|
+
@click="${this.clickDatabaseOps}"
|
|
267
|
+
>
|
|
268
|
+
${MoreHorizontalIcon()}
|
|
269
|
+
</div>`;
|
|
270
|
+
}
|
|
271
|
+
connectedCallback() {
|
|
272
|
+
super.connectedCallback();
|
|
273
|
+
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
|
|
274
|
+
this.classList.add(databaseBlockStyles);
|
|
275
|
+
this.listenFullWidthChange();
|
|
276
|
+
// Rebuild the source whenever the block is re-pointed at (or detached from)
|
|
277
|
+
// an external collection. The first (synchronous) emission is skipped: the
|
|
278
|
+
// source is still lazy at this point, so there is nothing to rebuild.
|
|
279
|
+
this.disposables.add(this.model.props.externalSourceId$.subscribe(() => {
|
|
280
|
+
if (this._dataSource) {
|
|
281
|
+
this.rebuildDataSource();
|
|
282
|
+
}
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
disconnectedCallback() {
|
|
286
|
+
this._dataSource?.dispose();
|
|
287
|
+
this._dataSource = undefined;
|
|
288
|
+
this._dataViewRootLogic = undefined;
|
|
289
|
+
super.disconnectedCallback();
|
|
290
|
+
}
|
|
291
|
+
listenFullWidthChange() {
|
|
292
|
+
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
this.disposables.add(autoUpdate(this.host, this, () => {
|
|
296
|
+
const padding = this.getBoundingClientRect().left -
|
|
297
|
+
this.host.getBoundingClientRect().left;
|
|
298
|
+
this.virtualPadding$.value = Math.max(0, padding - 72);
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
get dataViewRootLogic() {
|
|
302
|
+
if (!this._dataViewRootLogic) {
|
|
303
|
+
this._dataViewRootLogic = this._buildDataViewRootLogic();
|
|
304
|
+
}
|
|
305
|
+
return this._dataViewRootLogic;
|
|
306
|
+
}
|
|
307
|
+
_buildDataViewRootLogic() {
|
|
308
|
+
return new DataViewRootUILogic({
|
|
223
309
|
virtualPadding$: this.virtualPadding$,
|
|
224
310
|
bindHotkey: hotkeys => {
|
|
225
311
|
return {
|
|
@@ -237,7 +323,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
237
323
|
},
|
|
238
324
|
selection$: this.viewSelection$,
|
|
239
325
|
setSelection: this.setSelection,
|
|
240
|
-
dataSource: this.dataSource
|
|
326
|
+
dataSource: this.dataSource,
|
|
241
327
|
headerWidget: this.headerWidget,
|
|
242
328
|
onDrag: this.onDrag,
|
|
243
329
|
clipboard: this.std.clipboard,
|
|
@@ -297,58 +383,12 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
297
383
|
}
|
|
298
384
|
},
|
|
299
385
|
},
|
|
300
|
-
})
|
|
301
|
-
this.#useZeroWidth_accessor_storage = true;
|
|
302
|
-
}
|
|
303
|
-
get optionsConfig() {
|
|
304
|
-
return {
|
|
305
|
-
configure: (_model, options) => options,
|
|
306
|
-
...this.std.getOptional(DatabaseConfigExtension.identifier),
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
get isCommentHighlighted() {
|
|
310
|
-
return (this.std
|
|
311
|
-
.getOptional(BlockCommentManager)
|
|
312
|
-
?.isBlockCommentHighlighted(this.model) ?? false);
|
|
313
|
-
}
|
|
314
|
-
get topContenteditableElement() {
|
|
315
|
-
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
|
316
|
-
return this.closest(EDGELESS_TOP_CONTENTEDITABLE_SELECTOR);
|
|
317
|
-
}
|
|
318
|
-
return this.rootComponent;
|
|
319
|
-
}
|
|
320
|
-
renderDatabaseOps() {
|
|
321
|
-
if (this.dataSource.value.readonly$.value) {
|
|
322
|
-
return nothing;
|
|
323
|
-
}
|
|
324
|
-
return html ` <div
|
|
325
|
-
data-testid="database-ops"
|
|
326
|
-
class="${databaseOpsStyles}"
|
|
327
|
-
@click="${this.clickDatabaseOps}"
|
|
328
|
-
>
|
|
329
|
-
${MoreHorizontalIcon()}
|
|
330
|
-
</div>`;
|
|
331
|
-
}
|
|
332
|
-
connectedCallback() {
|
|
333
|
-
super.connectedCallback();
|
|
334
|
-
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
|
|
335
|
-
this.classList.add(databaseBlockStyles);
|
|
336
|
-
this.listenFullWidthChange();
|
|
337
|
-
}
|
|
338
|
-
listenFullWidthChange() {
|
|
339
|
-
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
this.disposables.add(autoUpdate(this.host, this, () => {
|
|
343
|
-
const padding = this.getBoundingClientRect().left -
|
|
344
|
-
this.host.getBoundingClientRect().left;
|
|
345
|
-
this.virtualPadding$.value = Math.max(0, padding - 72);
|
|
346
|
-
}));
|
|
386
|
+
});
|
|
347
387
|
}
|
|
348
388
|
renderBlock() {
|
|
349
389
|
return html `
|
|
350
390
|
<div contenteditable="false" class="${databaseContentStyles}">
|
|
351
|
-
${this.dataViewRootLogic.
|
|
391
|
+
${keyed(this._sourceVersion, this.dataViewRootLogic.render())}
|
|
352
392
|
</div>
|
|
353
393
|
`;
|
|
354
394
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { DatabaseBlockModel } from '../affine-model/index.js';
|
|
2
|
+
import type { InsertToPosition } from '../affine-shared/utils/index.js';
|
|
3
|
+
import { type DatabaseFlags, DataSourceBase, type DataViewDataType, type PropertyMetaConfig, type TypeInstance, type ViewManager, type ViewMeta } from '../data-view/index.js';
|
|
4
|
+
import { DisposableGroup } from '../global/disposable/index.js';
|
|
5
|
+
import { type ReadonlySignal } from '@preact/signals-core';
|
|
6
|
+
/**
|
|
7
|
+
* Intermediate base for `affine:database` data sources whose **view config**
|
|
8
|
+
* keeps living in the block model blob (`model.props.views`) while the **data
|
|
9
|
+
* layer** (rows / cells / columns) is redirected elsewhere — the inline blob
|
|
10
|
+
* source, or a host-provided external collection.
|
|
11
|
+
*
|
|
12
|
+
* It implements everything generic — the view manager / metas / converts, the
|
|
13
|
+
* view-data CRUD, feature flags, readonly, and the reactive plumbing — so a
|
|
14
|
+
* concrete source only provides a small synchronous data contract (`getRows`,
|
|
15
|
+
* `getCellValue`, …) plus the mutations, and calls {@link invalidate} from its
|
|
16
|
+
* own change observers.
|
|
17
|
+
*
|
|
18
|
+
* Reactivity: the data-layer reads ({@link rows$}, {@link properties$} and the
|
|
19
|
+
* `*Get` getters that `DataSourceBase` wraps in `computed()`) all depend on
|
|
20
|
+
* {@link revision}. A source backed by reactive blocksuite signals (the blob
|
|
21
|
+
* source) tracks those naturally and never bumps `revision` — cost is nil. A
|
|
22
|
+
* source reading raw `Y.Map`s bumps `revision` via {@link invalidate} so the
|
|
23
|
+
* computeds re-run. Defining `rows$`/`properties$` here (not as instance fields
|
|
24
|
+
* reassigned in a subclass constructor) also avoids the "reassign after super"
|
|
25
|
+
* dance.
|
|
26
|
+
*/
|
|
27
|
+
export declare abstract class ExternalDataSourceBase extends DataSourceBase {
|
|
28
|
+
protected readonly _model: DatabaseBlockModel;
|
|
29
|
+
/** Bumped by {@link invalidate} to re-run the data-layer computeds. */
|
|
30
|
+
protected readonly revision: import("@preact/signals-core").Signal<number>;
|
|
31
|
+
protected readonly disposables: DisposableGroup;
|
|
32
|
+
constructor(model: DatabaseBlockModel);
|
|
33
|
+
/** Notify the view layer that the underlying data changed. */
|
|
34
|
+
protected invalidate(): void;
|
|
35
|
+
dispose(): void;
|
|
36
|
+
get doc(): import("../store/index.js").Store;
|
|
37
|
+
get parentProvider(): import("../global/di/provider.js").ServiceProvider;
|
|
38
|
+
protected abstract getRows(): string[];
|
|
39
|
+
protected abstract getProperties(): string[];
|
|
40
|
+
protected abstract getCellValue(rowId: string, propertyId: string): unknown;
|
|
41
|
+
protected abstract getPropertyType(propertyId: string): string | undefined;
|
|
42
|
+
protected abstract getPropertyName(propertyId: string): string;
|
|
43
|
+
protected abstract getPropertyData(propertyId: string): Record<string, unknown>;
|
|
44
|
+
protected abstract getPropertyDataType(propertyId: string): TypeInstance | undefined;
|
|
45
|
+
protected abstract getPropertyMetas(): PropertyMetaConfig[];
|
|
46
|
+
rows$: ReadonlySignal<string[]>;
|
|
47
|
+
properties$: ReadonlySignal<string[]>;
|
|
48
|
+
allPropertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
|
|
49
|
+
propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
|
|
50
|
+
cellValueGet(rowId: string, propertyId: string): unknown;
|
|
51
|
+
propertyTypeGet(propertyId: string): string | undefined;
|
|
52
|
+
propertyNameGet(propertyId: string): string;
|
|
53
|
+
propertyDataGet(propertyId: string): Record<string, unknown>;
|
|
54
|
+
propertyDataTypeGet(propertyId: string): TypeInstance | undefined;
|
|
55
|
+
featureFlags$: ReadonlySignal<DatabaseFlags>;
|
|
56
|
+
readonly$: ReadonlySignal<boolean>;
|
|
57
|
+
viewConverts: import("./index.js").ViewConvertConfig[];
|
|
58
|
+
viewMetas: ViewMeta[];
|
|
59
|
+
viewManager: ViewManager;
|
|
60
|
+
viewDataList$: ReadonlySignal<DataViewDataType[]>;
|
|
61
|
+
viewDataGet(viewId: string): DataViewDataType | undefined;
|
|
62
|
+
viewDataAdd(viewData: DataViewDataType): string;
|
|
63
|
+
viewDataDelete(viewId: string): void;
|
|
64
|
+
viewDataDuplicate(id: string): string;
|
|
65
|
+
viewDataMoveTo(id: string, position: InsertToPosition): void;
|
|
66
|
+
viewDataUpdate<ViewData extends DataViewDataType>(id: string, updater: (data: ViewData) => Partial<ViewData>): void;
|
|
67
|
+
viewMetaGet(type: string): ViewMeta;
|
|
68
|
+
viewMetaGetById(viewId: string): ViewMeta | undefined;
|
|
69
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { FeatureFlagService } from '../affine-shared/services/index.js';
|
|
2
|
+
import { DataSourceBase, ViewManagerBase, } from '../data-view/index.js';
|
|
3
|
+
import { DisposableGroup } from '../global/disposable/index.js';
|
|
4
|
+
import { IS_MOBILE } from '../global/env/index.js';
|
|
5
|
+
import { BlockSuiteError, ErrorCode } from '../global/exceptions/index.js';
|
|
6
|
+
import { computed, signal, } from '@preact/signals-core';
|
|
7
|
+
import { deleteView, duplicateView, moveViewTo, updateView, } from './utils/block-utils.js';
|
|
8
|
+
import { databaseBlockViewConverts, databaseBlockViewMap, databaseBlockViews, } from './views/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* Intermediate base for `affine:database` data sources whose **view config**
|
|
11
|
+
* keeps living in the block model blob (`model.props.views`) while the **data
|
|
12
|
+
* layer** (rows / cells / columns) is redirected elsewhere — the inline blob
|
|
13
|
+
* source, or a host-provided external collection.
|
|
14
|
+
*
|
|
15
|
+
* It implements everything generic — the view manager / metas / converts, the
|
|
16
|
+
* view-data CRUD, feature flags, readonly, and the reactive plumbing — so a
|
|
17
|
+
* concrete source only provides a small synchronous data contract (`getRows`,
|
|
18
|
+
* `getCellValue`, …) plus the mutations, and calls {@link invalidate} from its
|
|
19
|
+
* own change observers.
|
|
20
|
+
*
|
|
21
|
+
* Reactivity: the data-layer reads ({@link rows$}, {@link properties$} and the
|
|
22
|
+
* `*Get` getters that `DataSourceBase` wraps in `computed()`) all depend on
|
|
23
|
+
* {@link revision}. A source backed by reactive blocksuite signals (the blob
|
|
24
|
+
* source) tracks those naturally and never bumps `revision` — cost is nil. A
|
|
25
|
+
* source reading raw `Y.Map`s bumps `revision` via {@link invalidate} so the
|
|
26
|
+
* computeds re-run. Defining `rows$`/`properties$` here (not as instance fields
|
|
27
|
+
* reassigned in a subclass constructor) also avoids the "reassign after super"
|
|
28
|
+
* dance.
|
|
29
|
+
*/
|
|
30
|
+
export class ExternalDataSourceBase extends DataSourceBase {
|
|
31
|
+
constructor(model) {
|
|
32
|
+
super();
|
|
33
|
+
/** Bumped by {@link invalidate} to re-run the data-layer computeds. */
|
|
34
|
+
this.revision = signal(0);
|
|
35
|
+
this.disposables = new DisposableGroup();
|
|
36
|
+
// --- reactive bridge over the data contract -----------------------------
|
|
37
|
+
this.rows$ = computed(() => {
|
|
38
|
+
void this.revision.value;
|
|
39
|
+
return this.getRows();
|
|
40
|
+
});
|
|
41
|
+
this.properties$ = computed(() => {
|
|
42
|
+
void this.revision.value;
|
|
43
|
+
return this.getProperties();
|
|
44
|
+
});
|
|
45
|
+
this.allPropertyMetas$ = computed(() => {
|
|
46
|
+
void this.revision.value;
|
|
47
|
+
return this.getPropertyMetas();
|
|
48
|
+
});
|
|
49
|
+
this.propertyMetas$ = computed(() => {
|
|
50
|
+
return this.allPropertyMetas$.value.filter(v => !v.config.fixed && !v.config.hide);
|
|
51
|
+
});
|
|
52
|
+
// --- view surface (config stays in the model blob) ----------------------
|
|
53
|
+
this.featureFlags$ = computed(() => {
|
|
54
|
+
const featureFlagService = this.doc.get(FeatureFlagService);
|
|
55
|
+
return {
|
|
56
|
+
enable_number_formatting: featureFlagService.getFlag('enable_database_number_formatting') ??
|
|
57
|
+
false,
|
|
58
|
+
enable_table_virtual_scroll: featureFlagService.getFlag('enable_table_virtual_scroll') ?? false,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
this.readonly$ = computed(() => {
|
|
62
|
+
return (this._model.store.readonly ||
|
|
63
|
+
// TODO(@L-Sun): use block level readonly
|
|
64
|
+
IS_MOBILE);
|
|
65
|
+
});
|
|
66
|
+
this.viewConverts = databaseBlockViewConverts;
|
|
67
|
+
this.viewMetas = databaseBlockViews;
|
|
68
|
+
this.viewManager = new ViewManagerBase(this);
|
|
69
|
+
this.viewDataList$ = computed(() => {
|
|
70
|
+
return this._model.props.views$.value;
|
|
71
|
+
});
|
|
72
|
+
this._model = model;
|
|
73
|
+
}
|
|
74
|
+
/** Notify the view layer that the underlying data changed. */
|
|
75
|
+
invalidate() {
|
|
76
|
+
this.revision.value++;
|
|
77
|
+
}
|
|
78
|
+
dispose() {
|
|
79
|
+
this.disposables.dispose();
|
|
80
|
+
}
|
|
81
|
+
get doc() {
|
|
82
|
+
return this._model.store;
|
|
83
|
+
}
|
|
84
|
+
get parentProvider() {
|
|
85
|
+
return this._model.store.provider;
|
|
86
|
+
}
|
|
87
|
+
cellValueGet(rowId, propertyId) {
|
|
88
|
+
void this.revision.value;
|
|
89
|
+
return this.getCellValue(rowId, propertyId);
|
|
90
|
+
}
|
|
91
|
+
propertyTypeGet(propertyId) {
|
|
92
|
+
void this.revision.value;
|
|
93
|
+
return this.getPropertyType(propertyId);
|
|
94
|
+
}
|
|
95
|
+
propertyNameGet(propertyId) {
|
|
96
|
+
void this.revision.value;
|
|
97
|
+
return this.getPropertyName(propertyId);
|
|
98
|
+
}
|
|
99
|
+
propertyDataGet(propertyId) {
|
|
100
|
+
void this.revision.value;
|
|
101
|
+
return this.getPropertyData(propertyId);
|
|
102
|
+
}
|
|
103
|
+
propertyDataTypeGet(propertyId) {
|
|
104
|
+
void this.revision.value;
|
|
105
|
+
return this.getPropertyDataType(propertyId);
|
|
106
|
+
}
|
|
107
|
+
viewDataGet(viewId) {
|
|
108
|
+
return this.viewDataList$.value.find(data => data.id === viewId);
|
|
109
|
+
}
|
|
110
|
+
viewDataAdd(viewData) {
|
|
111
|
+
this._model.store.captureSync();
|
|
112
|
+
this._model.store.transact(() => {
|
|
113
|
+
this._model.props.views = [...this._model.props.views, viewData];
|
|
114
|
+
});
|
|
115
|
+
return viewData.id;
|
|
116
|
+
}
|
|
117
|
+
viewDataDelete(viewId) {
|
|
118
|
+
this._model.store.captureSync();
|
|
119
|
+
deleteView(this._model, viewId);
|
|
120
|
+
}
|
|
121
|
+
viewDataDuplicate(id) {
|
|
122
|
+
return duplicateView(this._model, id);
|
|
123
|
+
}
|
|
124
|
+
viewDataMoveTo(id, position) {
|
|
125
|
+
moveViewTo(this._model, id, position);
|
|
126
|
+
}
|
|
127
|
+
viewDataUpdate(id, updater) {
|
|
128
|
+
updateView(this._model, id, updater);
|
|
129
|
+
}
|
|
130
|
+
viewMetaGet(type) {
|
|
131
|
+
const view = databaseBlockViewMap[type];
|
|
132
|
+
if (!view) {
|
|
133
|
+
throw new BlockSuiteError(ErrorCode.DatabaseBlockError, `Unknown view type: ${type}`);
|
|
134
|
+
}
|
|
135
|
+
return view;
|
|
136
|
+
}
|
|
137
|
+
viewMetaGetById(viewId) {
|
|
138
|
+
const view = this.viewDataGet(viewId);
|
|
139
|
+
if (!view) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
return this.viewMetaGet(view.mode);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -4,6 +4,7 @@ export * from './config';
|
|
|
4
4
|
export * from './context';
|
|
5
5
|
export * from './data-source';
|
|
6
6
|
export * from './database-block';
|
|
7
|
+
export * from './external-data-source';
|
|
7
8
|
export * from './detail-panel/block-renderer';
|
|
8
9
|
export * from './detail-panel/note-renderer';
|
|
9
10
|
export * from './properties';
|
|
@@ -4,6 +4,7 @@ export * from './config';
|
|
|
4
4
|
export * from './context';
|
|
5
5
|
export * from './data-source';
|
|
6
6
|
export * from './database-block';
|
|
7
|
+
export * from './external-data-source';
|
|
7
8
|
export * from './detail-panel/block-renderer';
|
|
8
9
|
export * from './detail-panel/note-renderer';
|
|
9
10
|
export * from './properties';
|
|
@@ -38,7 +38,7 @@ import { LoadingIcon } from '../../affine-components/icons/index.js';
|
|
|
38
38
|
import { isPeekable, Peekable } from '../../affine-components/peek/index.js';
|
|
39
39
|
import { RefNodeSlotsProvider } from '../../affine-inline-reference/index.js';
|
|
40
40
|
import { EMBED_CARD_HEIGHT, EMBED_CARD_WIDTH, REFERENCE_NODE, } from '../../affine-shared/consts/index.js';
|
|
41
|
-
import { CitationProvider, DocDisplayMetaProvider, DocModeProvider, OpenDocExtensionIdentifier, ThemeProvider, } from '../../affine-shared/services/index.js';
|
|
41
|
+
import { CitationProvider, DocDisplayMetaProvider, DocModeProvider, OpenDocExtensionIdentifier, ThemeProvider, whenLinkedDocContentReady, } from '../../affine-shared/services/index.js';
|
|
42
42
|
import { cloneReferenceInfo, cloneReferenceInfoWithoutAliases, isNewTabTrigger, isNewViewTrigger, matchModels, referenceToNode, } from '../../affine-shared/utils/index.js';
|
|
43
43
|
import { Bound } from '../../global/gfx/index.js';
|
|
44
44
|
import { ResetIcon } from '@blocksuite/icons/lit';
|
|
@@ -286,12 +286,11 @@ let EmbedLinkedDocBlockComponent = (() => {
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
if (!this.isError && !linkedDoc.root) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
});
|
|
289
|
+
// Bounded wait: if the host hasn't preloaded the referenced doc, ask the
|
|
290
|
+
// optional content resolver to hydrate it and wait at most `timeoutMs`.
|
|
291
|
+
// On timeout we stop loading and fall through to a degraded card (title
|
|
292
|
+
// only) instead of spinning forever.
|
|
293
|
+
await whenLinkedDocContentReady(this.std, linkedDoc, this.model.props.pageId);
|
|
295
294
|
}
|
|
296
295
|
this._loading = false;
|
|
297
296
|
// If it is a link to a block or element, the content will not be rendered.
|
package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/components/embed-synced-doc-card.js
CHANGED
|
@@ -34,7 +34,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
34
34
|
};
|
|
35
35
|
import { RENDER_CARD_THROTTLE_MS } from '../../../affine-block-embed/index.js';
|
|
36
36
|
import { LoadingIcon } from '../../../affine-components/icons/index.js';
|
|
37
|
-
import { ThemeProvider } from '../../../affine-shared/services/index.js';
|
|
37
|
+
import { ThemeProvider, whenLinkedDocContentReady, } from '../../../affine-shared/services/index.js';
|
|
38
38
|
import { WithDisposable } from '../../../global/lit/index.js';
|
|
39
39
|
import { ResetIcon } from '@blocksuite/icons/lit';
|
|
40
40
|
import { BlockSelection, isGfxBlockComponent, ShadowlessElement, } from '../../../std/index.js';
|
|
@@ -139,10 +139,10 @@ let EmbedSyncedDocCard = (() => {
|
|
|
139
139
|
renderLinkedDocInCard(this);
|
|
140
140
|
}
|
|
141
141
|
else {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
// Bounded: ask the host to hydrate the doc, wait at most timeoutMs, then
|
|
143
|
+
// render regardless (degraded if still empty) rather than waiting for a
|
|
144
|
+
// `rootAdded` that may never fire when the doc isn't preloaded.
|
|
145
|
+
void whenLinkedDocContentReady(this.std, syncedDoc, this.model.props.pageId).then(() => renderLinkedDocInCard(this));
|
|
146
146
|
}
|
|
147
147
|
this.disposables.add(syncedDoc.workspace.slots.docListUpdated.subscribe(() => {
|
|
148
148
|
renderLinkedDocInCard(this);
|
|
@@ -39,12 +39,12 @@ export declare const builtinToolbarConfig: {
|
|
|
39
39
|
readonly label?: string;
|
|
40
40
|
readonly icon?: import("lit-html").TemplateResult;
|
|
41
41
|
readonly active?: ((cx: import("../../affine-shared/services/index.js").ToolbarContext) => boolean) | boolean;
|
|
42
|
+
readonly disabled?: ((cx: import("../../affine-shared/services/index.js").ToolbarContext) => boolean) | boolean;
|
|
42
43
|
readonly placement?: ActionPlacement;
|
|
43
44
|
readonly tooltip?: string | import("lit-html").TemplateResult;
|
|
44
45
|
readonly score?: number;
|
|
45
46
|
readonly when?: ((cx: import("../../affine-shared/services/index.js").ToolbarContext) => boolean) | boolean;
|
|
46
47
|
readonly showLabel?: boolean;
|
|
47
|
-
readonly disabled?: ((cx: import("../../affine-shared/services/index.js").ToolbarContext) => boolean) | boolean;
|
|
48
48
|
readonly run?: (cx: import("../../affine-shared/services/index.js").ToolbarContext) => void;
|
|
49
49
|
readonly id: "g.comment";
|
|
50
50
|
}, {
|
|
@@ -118,6 +118,24 @@ export declare const builtinMiscToolbarConfig: {
|
|
|
118
118
|
readonly icon: import("lit-html").TemplateResult<1>;
|
|
119
119
|
readonly when: (ctx: ToolbarContext) => boolean;
|
|
120
120
|
readonly run: (ctx: ToolbarContext) => void;
|
|
121
|
+
}, {
|
|
122
|
+
readonly id: "c.link-element";
|
|
123
|
+
readonly label: "Link";
|
|
124
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
125
|
+
readonly when: (ctx: ToolbarContext) => boolean;
|
|
126
|
+
readonly run: (ctx: ToolbarContext) => void;
|
|
127
|
+
}, {
|
|
128
|
+
readonly id: "c.edit-element-link";
|
|
129
|
+
readonly label: "Edit link";
|
|
130
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
131
|
+
readonly when: (ctx: ToolbarContext) => boolean;
|
|
132
|
+
readonly run: (ctx: ToolbarContext) => void;
|
|
133
|
+
}, {
|
|
134
|
+
readonly id: "c.remove-element-link";
|
|
135
|
+
readonly label: "Remove link";
|
|
136
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
137
|
+
readonly when: (ctx: ToolbarContext) => boolean;
|
|
138
|
+
readonly run: (ctx: ToolbarContext) => void;
|
|
121
139
|
}];
|
|
122
140
|
} | {
|
|
123
141
|
placement: ActionPlacement;
|
|
@@ -69,6 +69,24 @@ export declare const moreActions: [{
|
|
|
69
69
|
readonly icon: import("lit-html").TemplateResult<1>;
|
|
70
70
|
readonly when: (ctx: ToolbarContext) => boolean;
|
|
71
71
|
readonly run: (ctx: ToolbarContext) => void;
|
|
72
|
+
}, {
|
|
73
|
+
readonly id: "c.link-element";
|
|
74
|
+
readonly label: "Link";
|
|
75
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
76
|
+
readonly when: (ctx: ToolbarContext) => boolean;
|
|
77
|
+
readonly run: (ctx: ToolbarContext) => void;
|
|
78
|
+
}, {
|
|
79
|
+
readonly id: "c.edit-element-link";
|
|
80
|
+
readonly label: "Edit link";
|
|
81
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
82
|
+
readonly when: (ctx: ToolbarContext) => boolean;
|
|
83
|
+
readonly run: (ctx: ToolbarContext) => void;
|
|
84
|
+
}, {
|
|
85
|
+
readonly id: "c.remove-element-link";
|
|
86
|
+
readonly label: "Remove link";
|
|
87
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
88
|
+
readonly when: (ctx: ToolbarContext) => boolean;
|
|
89
|
+
readonly run: (ctx: ToolbarContext) => void;
|
|
72
90
|
}];
|
|
73
91
|
}, {
|
|
74
92
|
readonly id: "e.delete";
|