@formicoidea/labre-core 1.0.0 → 1.1.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/architecture/code.c4 +13 -12
- package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +1 -1
- package/dist/_pkgs/affine-block-root/configs/toolbar.js +4 -2
- package/dist/_pkgs/affine-components/context-menu/menu-all.d.ts +1 -0
- package/dist/_pkgs/affine-components/context-menu/sub-menu.d.ts +3 -0
- package/dist/_pkgs/affine-components/context-menu/sub-menu.js +14 -2
- package/dist/_pkgs/affine-database-block/configs/slash-menu.d.ts +24 -0
- package/dist/_pkgs/affine-database-block/configs/slash-menu.js +38 -31
- package/dist/_pkgs/affine-database-block/creation-menu.d.ts +6 -0
- package/dist/_pkgs/affine-database-block/data-source.d.ts +6 -0
- package/dist/_pkgs/affine-database-block/data-source.js +16 -0
- package/dist/_pkgs/affine-database-block/database-block.d.ts +23 -0
- package/dist/_pkgs/affine-database-block/database-block.js +125 -3
- package/dist/_pkgs/affine-database-block/external-data-source.d.ts +7 -0
- package/dist/_pkgs/affine-database-block/external-data-source.js +30 -4
- package/dist/_pkgs/affine-database-block/index.d.ts +3 -0
- package/dist/_pkgs/affine-database-block/index.js +5 -0
- package/dist/_pkgs/affine-database-block/linked-view.d.ts +59 -0
- package/dist/_pkgs/affine-database-block/linked-view.js +131 -1
- package/dist/_pkgs/affine-database-block/translations.d.ts +18 -0
- package/dist/_pkgs/affine-database-block/translations.js +47 -0
- package/dist/_pkgs/affine-database-block/turn-into-source.d.ts +38 -0
- package/dist/_pkgs/affine-database-block/turn-into-source.js +187 -0
- package/dist/_pkgs/affine-database-block/view-deletion.d.ts +15 -0
- package/dist/_pkgs/affine-database-block/view-deletion.js +113 -0
- package/dist/_pkgs/affine-shared/services/data-source-picker-service.d.ts +99 -1
- package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +10 -21
- package/package.json +1 -1
package/architecture/code.c4
CHANGED
|
@@ -1404,7 +1404,7 @@ model {
|
|
|
1404
1404
|
link https://github.com/formicoidea/blocksuite-labre/tree/blocksuite-labre-main/packages/affine/database/block
|
|
1405
1405
|
metadata {
|
|
1406
1406
|
path 'packages/affine/database/block'
|
|
1407
|
-
files '
|
|
1407
|
+
files '65'
|
|
1408
1408
|
}
|
|
1409
1409
|
adapters = folder 'adapters/' {
|
|
1410
1410
|
link https://github.com/formicoidea/blocksuite-labre/tree/blocksuite-labre-main/packages/affine/database/block/src/adapters
|
|
@@ -1459,7 +1459,7 @@ model {
|
|
|
1459
1459
|
link https://github.com/formicoidea/blocksuite-labre/tree/blocksuite-labre-main/packages/affine/database/block/src
|
|
1460
1460
|
metadata {
|
|
1461
1461
|
path 'packages/affine/database/block/src'
|
|
1462
|
-
files '
|
|
1462
|
+
files '20'
|
|
1463
1463
|
}
|
|
1464
1464
|
}
|
|
1465
1465
|
utils = folder 'utils/' {
|
|
@@ -3722,19 +3722,19 @@ model {
|
|
|
3722
3722
|
features.components -[uses]-> framework.store '8 imports, 7 type-only'
|
|
3723
3723
|
features.components -[uses]-> framework.sync '1 import, 1 type-only'
|
|
3724
3724
|
features.components -[uses]-> model_.model_ '16 imports, 5 type-only'
|
|
3725
|
-
features.database.block -[uses]-> features.components '
|
|
3725
|
+
features.database.block -[uses]-> features.components '10 imports, 1 type-only'
|
|
3726
3726
|
features.database.block -[uses]-> features.database.engine '42 imports, 10 type-only'
|
|
3727
3727
|
features.database.block -[uses]-> features.ext-loader '2 imports'
|
|
3728
3728
|
features.database.block -[uses]-> features.inlines.preset '3 imports'
|
|
3729
3729
|
features.database.block -[uses]-> features.inlines.reference '1 import'
|
|
3730
3730
|
features.database.block -[uses]-> features.rich-text '2 imports, 2 type-only'
|
|
3731
|
-
features.database.block -[uses]-> features.shared '
|
|
3731
|
+
features.database.block -[uses]-> features.shared '43 imports, 8 type-only'
|
|
3732
3732
|
features.database.block -[uses]-> features.widgets.drag-handle '1 import'
|
|
3733
3733
|
features.database.block -[uses]-> features.widgets.slash-menu '2 imports, 1 type-only'
|
|
3734
3734
|
features.database.block -[uses]-> framework.global_ '16 imports'
|
|
3735
|
-
features.database.block -[uses]-> framework.std '
|
|
3736
|
-
features.database.block -[uses]-> framework.store '
|
|
3737
|
-
features.database.block -[uses]-> model_.model_ '
|
|
3735
|
+
features.database.block -[uses]-> framework.std '19 imports, 9 type-only'
|
|
3736
|
+
features.database.block -[uses]-> framework.store '24 imports, 16 type-only'
|
|
3737
|
+
features.database.block -[uses]-> model_.model_ '21 imports, 13 type-only'
|
|
3738
3738
|
features.database.editor -[uses]-> features.components '6 imports'
|
|
3739
3739
|
features.database.editor -[uses]-> features.database.block '9 imports, 2 type-only'
|
|
3740
3740
|
features.database.editor -[uses]-> features.database.engine '8 imports, 2 type-only'
|
|
@@ -5744,8 +5744,9 @@ model {
|
|
|
5744
5744
|
features.database.block.src -[imports]-> features.components.caption '1 import'
|
|
5745
5745
|
features.database.block.src -[imports]-> features.components.context-menu '2 imports, 1 type-only'
|
|
5746
5746
|
features.database.block.src -[imports]-> features.components.drop-indicator '1 import'
|
|
5747
|
+
features.database.block.src -[imports]-> features.components.icons_ '1 import'
|
|
5747
5748
|
features.database.block.src -[imports]-> features.components.peek '1 import'
|
|
5748
|
-
features.database.block.src -[imports]-> features.components.toast '
|
|
5749
|
+
features.database.block.src -[imports]-> features.components.toast '2 imports'
|
|
5749
5750
|
features.database.block.src -[imports]-> features.database.block.adapters '1 import'
|
|
5750
5751
|
features.database.block.src -[imports]-> features.database.block.components '4 imports'
|
|
5751
5752
|
features.database.block.src -[imports]-> features.database.block.configs '1 import'
|
|
@@ -5762,7 +5763,7 @@ model {
|
|
|
5762
5763
|
features.database.block.src -[imports]-> features.ext-loader.src '2 imports'
|
|
5763
5764
|
features.database.block.src -[imports]-> features.shared.commands '1 import'
|
|
5764
5765
|
features.database.block.src -[imports]-> features.shared.consts '1 import'
|
|
5765
|
-
features.database.block.src -[imports]-> features.shared.services '
|
|
5766
|
+
features.database.block.src -[imports]-> features.shared.services '6 imports, 2 type-only'
|
|
5766
5767
|
features.database.block.src -[imports]-> features.shared.theme '1 import'
|
|
5767
5768
|
features.database.block.src -[imports]-> features.shared.utils '4 imports, 1 type-only'
|
|
5768
5769
|
features.database.block.src -[imports]-> features.widgets.drag-handle.src '1 import'
|
|
@@ -5771,9 +5772,9 @@ model {
|
|
|
5771
5772
|
features.database.block.src -[imports]-> framework.global_.disposable '1 import'
|
|
5772
5773
|
features.database.block.src -[imports]-> framework.global_.gfx '1 import'
|
|
5773
5774
|
features.database.block.src -[imports]-> framework.std.inline '1 import'
|
|
5774
|
-
features.database.block.src -[imports]-> framework.std.src '
|
|
5775
|
-
features.database.block.src -[imports]-> framework.store.src '
|
|
5776
|
-
features.database.block.src -[imports]-> model_.model_.src '
|
|
5775
|
+
features.database.block.src -[imports]-> framework.std.src '9 imports, 5 type-only'
|
|
5776
|
+
features.database.block.src -[imports]-> framework.store.src '9 imports, 6 type-only'
|
|
5777
|
+
features.database.block.src -[imports]-> model_.model_.src '11 imports, 9 type-only'
|
|
5777
5778
|
features.database.block.utils -[imports]-> features.database.block.service '1 import, 1 type-only'
|
|
5778
5779
|
features.database.block.utils -[imports]-> features.shared.types '1 import, 1 type-only'
|
|
5779
5780
|
features.database.block.utils -[imports]-> features.shared.utils '1 import'
|
|
@@ -26,7 +26,7 @@ export declare const builtinToolbarConfig: {
|
|
|
26
26
|
readonly tooltip: "Create Table";
|
|
27
27
|
readonly tooltipWording: import("../../affine-shared/services/index.js").ChromeWording;
|
|
28
28
|
readonly icon: import("lit-html").TemplateResult<1>;
|
|
29
|
-
readonly when: ({ chain }: import("../../affine-shared/services/index.js").ToolbarContext) => boolean;
|
|
29
|
+
readonly when: ({ chain, std }: import("../../affine-shared/services/index.js").ToolbarContext) => boolean;
|
|
30
30
|
readonly run: ({ host }: import("../../affine-shared/services/index.js").ToolbarContext) => void;
|
|
31
31
|
}, {
|
|
32
32
|
readonly id: "f.convert-to-linked-doc";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { convertToDatabase, DATABASE_CONVERT_WHITE_LIST, } from '../../affine-database-block/index.js';
|
|
1
|
+
import { canConvertToDatabase, convertToDatabase, DATABASE_CONVERT_WHITE_LIST, } from '../../affine-database-block/index.js';
|
|
2
2
|
import { convertSelectedBlocksToLinkedDoc, getTitleFromSelectedModels, notifyDocCreated, promptDocTitle, } from '../../affine-block-embed/index.js';
|
|
3
3
|
import { updateBlockType } from '../../affine-block-note/index.js';
|
|
4
4
|
import { toast } from '../../affine-components/toast/index.js';
|
|
@@ -129,7 +129,9 @@ const turnIntoDatabase = {
|
|
|
129
129
|
tooltip: 'Create Table',
|
|
130
130
|
tooltipWording: ROOT_TOOLBAR_CREATE_TABLE,
|
|
131
131
|
icon: DatabaseTableViewIcon(),
|
|
132
|
-
when({ chain }) {
|
|
132
|
+
when({ chain, std }) {
|
|
133
|
+
if (!canConvertToDatabase(std))
|
|
134
|
+
return false;
|
|
133
135
|
const middleware = (count = 0) => {
|
|
134
136
|
return (ctx, next) => {
|
|
135
137
|
const { selectedBlocks } = ctx;
|
|
@@ -6,6 +6,8 @@ export type MenuSubMenuData = {
|
|
|
6
6
|
options: MenuOptions;
|
|
7
7
|
select?: () => void;
|
|
8
8
|
class?: string;
|
|
9
|
+
/** Names the entry itself; `options.testId` names the menu it opens. */
|
|
10
|
+
testId?: string;
|
|
9
11
|
};
|
|
10
12
|
export declare const subMenuOffset: {
|
|
11
13
|
name: string;
|
|
@@ -49,6 +51,7 @@ export declare const subMenuItems: {
|
|
|
49
51
|
postfix?: TemplateResult;
|
|
50
52
|
prefix?: TemplateResult;
|
|
51
53
|
class?: string;
|
|
54
|
+
testId?: string;
|
|
52
55
|
options: MenuOptions;
|
|
53
56
|
disableArrow?: boolean;
|
|
54
57
|
hide?: () => boolean;
|
|
@@ -38,6 +38,7 @@ import { autoPlacement, autoUpdate, computePosition, offset, } from '@floating-u
|
|
|
38
38
|
import { html, nothing } from 'lit';
|
|
39
39
|
import { property } from 'lit/decorators.js';
|
|
40
40
|
import { classMap } from 'lit/directives/class-map.js';
|
|
41
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
41
42
|
import { MenuFocusable } from './focusable.js';
|
|
42
43
|
import { Menu } from './menu.js';
|
|
43
44
|
import { popMenu, popupTargetFromElement } from './menu-renderer.js';
|
|
@@ -121,7 +122,12 @@ let MenuSubMenu = (() => {
|
|
|
121
122
|
'affine-menu-button': true,
|
|
122
123
|
focused: this.isFocused$.value,
|
|
123
124
|
});
|
|
124
|
-
return html ` <div
|
|
125
|
+
return html ` <div
|
|
126
|
+
class="${classString}"
|
|
127
|
+
data-testid=${ifDefined(this.data.testId)}
|
|
128
|
+
>
|
|
129
|
+
${this.data.content()}
|
|
130
|
+
</div>`;
|
|
125
131
|
}
|
|
126
132
|
#data_accessor_storage;
|
|
127
133
|
get data() { return this.#data_accessor_storage; }
|
|
@@ -182,7 +188,12 @@ let MobileSubMenu = (() => {
|
|
|
182
188
|
'mobile-menu-button': true,
|
|
183
189
|
focused: this.isFocused$.value,
|
|
184
190
|
});
|
|
185
|
-
return html ` <div
|
|
191
|
+
return html ` <div
|
|
192
|
+
class="${classString}"
|
|
193
|
+
data-testid=${ifDefined(this.data.testId)}
|
|
194
|
+
>
|
|
195
|
+
${this.data.content()}
|
|
196
|
+
</div>`;
|
|
186
197
|
}
|
|
187
198
|
#data_accessor_storage = __runInitializers(this, _data_initializers, void 0);
|
|
188
199
|
get data() { return this.#data_accessor_storage; }
|
|
@@ -219,6 +230,7 @@ export const subMenuItems = {
|
|
|
219
230
|
${config.postfix}
|
|
220
231
|
${config.disableArrow ? nothing : ArrowRightSmallIcon()} `,
|
|
221
232
|
class: config.class,
|
|
233
|
+
testId: config.testId,
|
|
222
234
|
options: config.options,
|
|
223
235
|
};
|
|
224
236
|
return renderSubMenu(data, menu);
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import type { SlashMenuConfig } from '../../affine-widget-slash-menu/index.js';
|
|
2
|
+
import type { BlockStdScope } from '../../std/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* With a data-source picker the host owns database creation (ADR 0035 §5):
|
|
5
|
+
* its `createDatabase` is the only way in, and without it no entry creates
|
|
6
|
+
* one. Without a picker, a native inline database, as in 0.48.
|
|
7
|
+
*/
|
|
8
|
+
export declare function canCreateDatabase(std: BlockStdScope): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Three ways in, by what is registered (ADR 0035 §5, amendment of
|
|
11
|
+
* 2026-10-28):
|
|
12
|
+
*
|
|
13
|
+
* - a picker and the bundle's creation menu: the database lands at once,
|
|
14
|
+
* native and with no row, and the menu opens over it to connect it to a
|
|
15
|
+
* source (`connectDatabaseToSource`). Dismissed, the native database stays
|
|
16
|
+
* and renders: nothing is lost by closing the popover;
|
|
17
|
+
* - a picker alone (a core-only host): the host's `createDatabase` first,
|
|
18
|
+
* then a view of its first source, as in 0.61;
|
|
19
|
+
* - no picker: a native inline database with 3 rows, as in 0.48.
|
|
20
|
+
*
|
|
21
|
+
* The one creation gesture: the slash entries and the mobile keyboard
|
|
22
|
+
* toolbar's "Table view" / "Kanban view" both run it (ADR 0035 amendment of
|
|
23
|
+
* 2026-10-29), so neither inserts native rows a host's picker would not see.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createDatabaseByGesture(std: BlockStdScope, viewType: string): void;
|
|
2
26
|
/**
|
|
3
27
|
* The slash entries that CREATE a database, registered by the gated
|
|
4
28
|
* `DatabaseViewExtension` only. No Calendar entry after 0.48 (ADR 0035 §6):
|
|
@@ -12,7 +12,7 @@ import { KanbanViewTooltip, TableViewTooltip } from './tooltips.js';
|
|
|
12
12
|
* its `createDatabase` is the only way in, and without it no entry creates
|
|
13
13
|
* one. Without a picker, a native inline database, as in 0.48.
|
|
14
14
|
*/
|
|
15
|
-
function canCreateDatabase(std) {
|
|
15
|
+
export function canCreateDatabase(std) {
|
|
16
16
|
const picker = std.getOptional(DataSourcePickerProvider);
|
|
17
17
|
return !picker || !!picker.createDatabase;
|
|
18
18
|
}
|
|
@@ -55,38 +55,45 @@ function insertNativeDatabase(std, viewType, initialRows) {
|
|
|
55
55
|
* - a picker alone (a core-only host): the host's `createDatabase` first,
|
|
56
56
|
* then a view of its first source, as in 0.61;
|
|
57
57
|
* - no picker: a native inline database with 3 rows, as in 0.48.
|
|
58
|
+
*
|
|
59
|
+
* The one creation gesture: the slash entries and the mobile keyboard
|
|
60
|
+
* toolbar's "Table view" / "Kanban view" both run it (ADR 0035 amendment of
|
|
61
|
+
* 2026-10-29), so neither inserts native rows a host's picker would not see.
|
|
58
62
|
*/
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
createHostedDatabaseView(std, picker, {
|
|
73
|
-
after,
|
|
74
|
-
viewType,
|
|
75
|
-
removeEmptyLine: true,
|
|
76
|
-
}).catch(console.error);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const blockId = insertNativeDatabase(std, viewType, 0);
|
|
80
|
-
if (!blockId)
|
|
63
|
+
export function createDatabaseByGesture(std, viewType) {
|
|
64
|
+
if (!canCreateDatabase(std))
|
|
65
|
+
return;
|
|
66
|
+
const picker = std.getOptional(DataSourcePickerProvider);
|
|
67
|
+
if (!picker) {
|
|
68
|
+
insertNativeDatabase(std, viewType);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const menu = std.getOptional(DatabaseCreationMenuIdentifier);
|
|
72
|
+
if (!menu) {
|
|
73
|
+
const [, { selectedModels }] = std.command.exec(getSelectedModelsCommand);
|
|
74
|
+
const after = selectedModels?.at(-1);
|
|
75
|
+
if (!after)
|
|
81
76
|
return;
|
|
82
|
-
std
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
77
|
+
createHostedDatabaseView(std, picker, {
|
|
78
|
+
after,
|
|
79
|
+
viewType,
|
|
80
|
+
removeEmptyLine: true,
|
|
81
|
+
}).catch(console.error);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const blockId = insertNativeDatabase(std, viewType, 0);
|
|
85
|
+
if (!blockId)
|
|
86
|
+
return;
|
|
87
|
+
std.host.updateComplete
|
|
88
|
+
.then(() => {
|
|
89
|
+
const anchor = std.view.getBlock(blockId);
|
|
90
|
+
if (anchor)
|
|
91
|
+
menu.open({ std, blockId, anchor });
|
|
92
|
+
})
|
|
93
|
+
.catch(console.error);
|
|
94
|
+
}
|
|
95
|
+
function createDatabaseAction(viewType) {
|
|
96
|
+
return ({ std }) => createDatabaseByGesture(std, viewType);
|
|
90
97
|
}
|
|
91
98
|
/**
|
|
92
99
|
* The slash entries that CREATE a database, registered by the gated
|
|
@@ -7,6 +7,12 @@ export interface DatabaseCreationRequest {
|
|
|
7
7
|
blockId: string;
|
|
8
8
|
/** The block's element, to hang the menu from. */
|
|
9
9
|
anchor: HTMLElement;
|
|
10
|
+
/**
|
|
11
|
+
* The view to connect, when the block already has a source the host no
|
|
12
|
+
* longer resolves ("Connect a source" of its empty view, ADR 0035
|
|
13
|
+
* amendment of 2026-10-29). Absent: the block's first view.
|
|
14
|
+
*/
|
|
15
|
+
viewId?: string;
|
|
10
16
|
}
|
|
11
17
|
/**
|
|
12
18
|
* What a database creation entry opens over the block it just inserted
|
|
@@ -86,5 +86,11 @@ export declare class DatabaseBlockDataSource extends HostedDataSourceBase {
|
|
|
86
86
|
* to be chosen (the creation popover, ADR 0035 amendment of 2026-10-28).
|
|
87
87
|
*/
|
|
88
88
|
export declare const databaseViewInitTemplate: (datasource: DatabaseBlockDataSource, viewType: string, std?: BlockStdScope, rows?: number) => void;
|
|
89
|
+
/**
|
|
90
|
+
* "Create Table": the selected blocks become a database's rows. Under a host
|
|
91
|
+
* that requires a source they become a source of the host's instead
|
|
92
|
+
* (`turnBlocksIntoSource`, ADR 0035 amendment of 2026-10-29), never a
|
|
93
|
+
* native database it would show read-only.
|
|
94
|
+
*/
|
|
89
95
|
export declare const convertToDatabase: (host: EditorHost, viewType: string) => void;
|
|
90
96
|
export {};
|
|
@@ -7,6 +7,7 @@ import { computed, signal } from '@preact/signals-core';
|
|
|
7
7
|
import { getIcon } from './block-icons.js';
|
|
8
8
|
import { HostedDataSourceBase } from './external-data-source.js';
|
|
9
9
|
import { databaseBlockProperties, databasePropertyConverts, } from './properties/index.js';
|
|
10
|
+
import { canTurnIntoSource, turnBlocksIntoSource } from './turn-into-source.js';
|
|
10
11
|
import { addProperty, copyCellsByProperty, deleteRows, getCell, getProperty, updateCell, updateCells, updateProperty, } from './utils/block-utils.js';
|
|
11
12
|
/**
|
|
12
13
|
* The inline blob source: rows are the block's children, cells/columns live in
|
|
@@ -405,6 +406,12 @@ export const databaseViewInitTemplate = (datasource, viewType, std, rows = 3) =>
|
|
|
405
406
|
});
|
|
406
407
|
datasource.viewManager.viewAdd(viewType, viewDefaultName(datasource.viewMetaGet(viewType), std));
|
|
407
408
|
};
|
|
409
|
+
/**
|
|
410
|
+
* "Create Table": the selected blocks become a database's rows. Under a host
|
|
411
|
+
* that requires a source they become a source of the host's instead
|
|
412
|
+
* (`turnBlocksIntoSource`, ADR 0035 amendment of 2026-10-29), never a
|
|
413
|
+
* native database it would show read-only.
|
|
414
|
+
*/
|
|
408
415
|
export const convertToDatabase = (host, viewType) => {
|
|
409
416
|
const [_, ctx] = host.std.command.exec(getSelectedModelsCommand, {
|
|
410
417
|
types: ['block', 'text'],
|
|
@@ -413,6 +420,15 @@ export const convertToDatabase = (host, viewType) => {
|
|
|
413
420
|
const firstModel = selectedModels?.[0];
|
|
414
421
|
if (!firstModel)
|
|
415
422
|
return;
|
|
423
|
+
if (canTurnIntoSource(host.std)) {
|
|
424
|
+
turnBlocksIntoSource(host.std, selectedModels, viewType)
|
|
425
|
+
.then(written => {
|
|
426
|
+
if (written)
|
|
427
|
+
host.selection.clear();
|
|
428
|
+
})
|
|
429
|
+
.catch(console.error);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
416
432
|
host.store.captureSync();
|
|
417
433
|
const parentModel = host.store.getParent(firstModel);
|
|
418
434
|
if (!parentModel) {
|
|
@@ -60,6 +60,29 @@ export declare class DatabaseBlockComponent extends CaptionedBlockComponent<Data
|
|
|
60
60
|
get isCommentHighlighted(): boolean;
|
|
61
61
|
get topContenteditableElement(): BlockComponent<import("../store/index.js").BlockModel<object>, import("../std/index.js").BlockService, string> | null;
|
|
62
62
|
private readonly renderSourceMissing;
|
|
63
|
+
/**
|
|
64
|
+
* A database a host that requires a source must connect first
|
|
65
|
+
* (`sourceRequirement` is `'connect'`, ADR 0035 amendment of 2026-10-29):
|
|
66
|
+
* no source and no row, or a hosted source the host no longer resolves.
|
|
67
|
+
* Drawn in place of the tools and the body: no row, no "New", no cell to
|
|
68
|
+
* type or paste into, so nothing is written. A hosted block keeps its view
|
|
69
|
+
* bar, since another of its views may still read a source. The button
|
|
70
|
+
* reopens the creation popover over the block, for the current view; a
|
|
71
|
+
* read-only document, or a host without the bundle's creation menu, gets
|
|
72
|
+
* the notice alone. Takes precedence over "Source not found" (§7).
|
|
73
|
+
*/
|
|
74
|
+
private readonly renderSourceRequired;
|
|
75
|
+
/** "Move into a database" is waiting for the host: one call at a time. */
|
|
76
|
+
private readonly moving$;
|
|
77
|
+
private readonly moveIntoSource;
|
|
78
|
+
/**
|
|
79
|
+
* Native rows under a host that requires a source (`sourceRequirement` is
|
|
80
|
+
* `'read-only'`): the rows render, locked (`locked$`), with this notice and,
|
|
81
|
+
* where the host turns content into a source (`createDatabaseFrom`) and
|
|
82
|
+
* the document is writable, "Move into a database"; a spinner while the
|
|
83
|
+
* host answers.
|
|
84
|
+
*/
|
|
85
|
+
private renderReadOnlyNotice;
|
|
63
86
|
private renderDatabaseOps;
|
|
64
87
|
connectedCallback(): void;
|
|
65
88
|
disconnectedCallback(): void;
|
|
@@ -2,10 +2,11 @@ import { CaptionedBlockComponent } from '../affine-components/caption/index.js';
|
|
|
2
2
|
import { menu, popMenu, popupTargetFromElement, } from '../affine-components/context-menu/index.js';
|
|
3
3
|
import { DropIndicator } from '../affine-components/drop-indicator/index.js';
|
|
4
4
|
import { PeekViewProvider } from '../affine-components/peek/index.js';
|
|
5
|
+
import { LoadingIcon } from '../affine-components/icons/index.js';
|
|
5
6
|
import { toast } from '../affine-components/toast/index.js';
|
|
6
7
|
import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '../affine-shared/consts/index.js';
|
|
7
8
|
import { getScrollContainer } from '../affine-shared/utils/index.js';
|
|
8
|
-
import { BlockCommentManager, CommentProviderIdentifier, DocModeProvider, NotificationProvider, PropertiesPanelProvider, TelemetryProvider, TOAST_COPIED_TO_CLIPBOARD, translateKey, } from '../affine-shared/services/index.js';
|
|
9
|
+
import { BlockCommentManager, CommentProviderIdentifier, DataSourcePickerProvider, DocModeProvider, NotificationProvider, PropertiesPanelProvider, TelemetryProvider, TOAST_COPIED_TO_CLIPBOARD, translateKey, } from '../affine-shared/services/index.js';
|
|
9
10
|
import { getDropResult } from '../affine-widget-drag-handle/index.js';
|
|
10
11
|
import { createRecordDetail, createUniComponentFromWebComponent, DataViewRootUILogic, DataViewEventTraceKey, defineUniComponent, ExternalGroupByConfigProvider, NewRowMenuOpenerKey, PageLayoutKey, PropertiesPanelOpenerKey, renderUniLit, traceViewEvent, uniMap, ViewCreationOffKey, } from '../affine-database-engine/index.js';
|
|
11
12
|
import { CalendarExternalSourceProvider } from '../affine-database-engine/view-presets/index.js';
|
|
@@ -21,16 +22,19 @@ import { html, nothing } from 'lit';
|
|
|
21
22
|
import { keyed } from 'lit/directives/keyed.js';
|
|
22
23
|
import { popSideDetail } from './components/layout.js';
|
|
23
24
|
import { DatabaseConfigExtension } from './config.js';
|
|
25
|
+
import { DatabaseCreationMenuIdentifier } from './creation-menu.js';
|
|
24
26
|
import { EditorHostKey } from './context/host-context.js';
|
|
25
27
|
import { DatabaseBlockDataSource } from './data-source.js';
|
|
26
28
|
import { HostedDataSourceBase } from './external-data-source.js';
|
|
27
29
|
import { databaseBlockStyles, databaseContentStyles, databaseHeaderBarStyles, databaseHeaderContainerStyles, databaseOpsStyles, databaseSourceMissingHintStyles, databaseSourceMissingStyles, databaseTitleRowStyles, databaseTitleStyles, databaseToolbarRowStyles, databaseViewBarContainerStyles, } from './database-block-styles.js';
|
|
28
30
|
import { BlockRenderer } from './detail-panel/block-renderer.js';
|
|
29
31
|
import { NoteRenderer } from './detail-panel/note-renderer.js';
|
|
32
|
+
import { sourceRequirement } from './linked-view.js';
|
|
30
33
|
import { DatabaseSelection } from './selection.js';
|
|
31
34
|
import { isStandaloneDatabase } from './standalone-layout.js';
|
|
32
35
|
import { databaseOpenRowRequest, DatabaseDataSourceProvider, DatabaseInitialViewProvider, DatabaseNewRowMenuProvider, DatabaseOpenRowProvider, openDatabasePropertiesPanel, rowOpenedParams, } from './service/index.js';
|
|
33
|
-
import { DATABASE_NEW_RECORD_MORE, DATABASE_SOURCE_MISSING, DATABASE_SOURCE_MISSING_HINT, } from './translations.js';
|
|
36
|
+
import { DATABASE_CREATION_TITLE, DATABASE_MOVE_INTO_SOURCE, DATABASE_NEW_RECORD_MORE, DATABASE_SOURCE_MISSING, DATABASE_SOURCE_MISSING_HINT, DATABASE_SOURCE_REQUIRED, DATABASE_SOURCE_REQUIRED_HINT, DATABASE_SOURCE_REQUIRED_READ_ONLY, } from './translations.js';
|
|
37
|
+
import { canTurnIntoSource, moveDatabaseRowsIntoSource, } from './turn-into-source.js';
|
|
34
38
|
import { currentViewStorage, initialViewId } from './utils/current-view.js';
|
|
35
39
|
import { getSingleDocIdFromText } from './utils/title-doc.js';
|
|
36
40
|
import { DatabaseViewCreationIdentifier } from './view-creation.js';
|
|
@@ -146,6 +150,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
146
150
|
${renderUniLit(this.toolsWidget, props)}
|
|
147
151
|
</div>
|
|
148
152
|
${renderUniLit(widgetPresets.quickSettingBar, props)}
|
|
153
|
+
${this.renderReadOnlyNotice()}
|
|
149
154
|
<affine-database-values-banner
|
|
150
155
|
.dataSource="${this.dataSource}"
|
|
151
156
|
.std="${this.std}"
|
|
@@ -277,6 +282,77 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
277
282
|
</div>
|
|
278
283
|
</div>`;
|
|
279
284
|
};
|
|
285
|
+
/**
|
|
286
|
+
* A database a host that requires a source must connect first
|
|
287
|
+
* (`sourceRequirement` is `'connect'`, ADR 0035 amendment of 2026-10-29):
|
|
288
|
+
* no source and no row, or a hosted source the host no longer resolves.
|
|
289
|
+
* Drawn in place of the tools and the body: no row, no "New", no cell to
|
|
290
|
+
* type or paste into, so nothing is written. A hosted block keeps its view
|
|
291
|
+
* bar, since another of its views may still read a source. The button
|
|
292
|
+
* reopens the creation popover over the block, for the current view; a
|
|
293
|
+
* read-only document, or a host without the bundle's creation menu, gets
|
|
294
|
+
* the notice alone. Takes precedence over "Source not found" (§7).
|
|
295
|
+
*/
|
|
296
|
+
this.renderSourceRequired = (dataViewLogic) => {
|
|
297
|
+
const dataSource = this.dataSource;
|
|
298
|
+
const sourceMissing = dataSource instanceof HostedDataSourceBase &&
|
|
299
|
+
dataSource.sourceMissing$.value;
|
|
300
|
+
if (sourceRequirement(this.std, this.model, sourceMissing) !== 'connect') {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
const hosted = this.model.props.externalSourceId !== undefined;
|
|
304
|
+
const creationMenu = this.std.getOptional(DatabaseCreationMenuIdentifier);
|
|
305
|
+
const connect = creationMenu && !this.store.readonly$.value
|
|
306
|
+
? () => creationMenu.open({
|
|
307
|
+
std: this.std,
|
|
308
|
+
blockId: this.blockId,
|
|
309
|
+
anchor: this,
|
|
310
|
+
viewId: dataViewLogic.view.id,
|
|
311
|
+
})
|
|
312
|
+
: undefined;
|
|
313
|
+
return html `<div class="${databaseHeaderContainerStyles}">
|
|
314
|
+
<div class="${databaseTitleRowStyles}">
|
|
315
|
+
${this.renderTitle(dataViewLogic)}
|
|
316
|
+
</div>
|
|
317
|
+
${hosted
|
|
318
|
+
? html `<div
|
|
319
|
+
class="${databaseToolbarRowStyles} ${databaseHeaderBarStyles}"
|
|
320
|
+
>
|
|
321
|
+
${this.renderViewBar({ dataViewLogic })}
|
|
322
|
+
</div>`
|
|
323
|
+
: nothing}
|
|
324
|
+
</div>
|
|
325
|
+
<div
|
|
326
|
+
class="${databaseSourceMissingStyles}"
|
|
327
|
+
data-testid="database-source-required"
|
|
328
|
+
role="status"
|
|
329
|
+
>
|
|
330
|
+
<div>${translateKey(this.std, ...DATABASE_SOURCE_REQUIRED)}</div>
|
|
331
|
+
<div class="${databaseSourceMissingHintStyles}">
|
|
332
|
+
${translateKey(this.std, ...DATABASE_SOURCE_REQUIRED_HINT)}
|
|
333
|
+
</div>
|
|
334
|
+
${connect
|
|
335
|
+
? html `<data-view-component-button
|
|
336
|
+
data-testid="database-source-required-connect"
|
|
337
|
+
hoverType="border"
|
|
338
|
+
.onClick="${connect}"
|
|
339
|
+
.text="${translateKey(this.std, ...DATABASE_CREATION_TITLE)}"
|
|
340
|
+
></data-view-component-button>`
|
|
341
|
+
: nothing}
|
|
342
|
+
</div>`;
|
|
343
|
+
};
|
|
344
|
+
/** "Move into a database" is waiting for the host: one call at a time. */
|
|
345
|
+
this.moving$ = signal(false);
|
|
346
|
+
this.moveIntoSource = () => {
|
|
347
|
+
if (this.moving$.peek())
|
|
348
|
+
return;
|
|
349
|
+
this.moving$.value = true;
|
|
350
|
+
moveDatabaseRowsIntoSource(this.std, this.model)
|
|
351
|
+
.catch(console.error)
|
|
352
|
+
.finally(() => {
|
|
353
|
+
this.moving$.value = false;
|
|
354
|
+
});
|
|
355
|
+
};
|
|
280
356
|
this.#useZeroWidth_accessor_storage = true;
|
|
281
357
|
}
|
|
282
358
|
_buildDataSource() {
|
|
@@ -303,6 +379,12 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
303
379
|
},
|
|
304
380
|
});
|
|
305
381
|
}
|
|
382
|
+
// A view of a host database is deleted through the host's request
|
|
383
|
+
// (`deleteView`, ADR 0035 amendment of 2026-10-29); the data source
|
|
384
|
+
// reads the picker from its own scope, below the editor's.
|
|
385
|
+
const picker = this.std.getOptional(DataSourcePickerProvider);
|
|
386
|
+
if (picker)
|
|
387
|
+
dataSource.serviceSet(DataSourcePickerProvider, picker);
|
|
306
388
|
// `{ database: false }` takes view creation away, never the views.
|
|
307
389
|
if (!this.std.getOptional(DatabaseViewCreationIdentifier)) {
|
|
308
390
|
dataSource.serviceSet(ViewCreationOffKey, true);
|
|
@@ -325,6 +407,12 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
325
407
|
const id = initialViewId(this.model.id, viewId => dataSource.viewManager.viewGet(viewId) != null, this.std.getOptional(DatabaseInitialViewProvider));
|
|
326
408
|
if (id)
|
|
327
409
|
dataSource.viewManager.setCurrentView(id);
|
|
410
|
+
// A native database under a host that requires a source is never edited
|
|
411
|
+
// natively, whatever its row count (`sourceRequirement`).
|
|
412
|
+
if (dataSource instanceof DatabaseBlockDataSource &&
|
|
413
|
+
sourceRequirement(this.std, this.model) !== 'none') {
|
|
414
|
+
dataSource.locked$.value = true;
|
|
415
|
+
}
|
|
328
416
|
return dataSource;
|
|
329
417
|
}
|
|
330
418
|
get dataSource() {
|
|
@@ -429,6 +517,40 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
429
517
|
}
|
|
430
518
|
return this.rootComponent;
|
|
431
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Native rows under a host that requires a source (`sourceRequirement` is
|
|
522
|
+
* `'read-only'`): the rows render, locked (`locked$`), with this notice and,
|
|
523
|
+
* where the host turns content into a source (`createDatabaseFrom`) and
|
|
524
|
+
* the document is writable, "Move into a database"; a spinner while the
|
|
525
|
+
* host answers.
|
|
526
|
+
*/
|
|
527
|
+
renderReadOnlyNotice() {
|
|
528
|
+
if (sourceRequirement(this.std, this.model) !== 'read-only') {
|
|
529
|
+
return nothing;
|
|
530
|
+
}
|
|
531
|
+
const canMove = canTurnIntoSource(this.std) && !this.store.readonly$.value;
|
|
532
|
+
const moving = this.moving$.value;
|
|
533
|
+
return html `<div
|
|
534
|
+
class="${databaseSourceMissingStyles}"
|
|
535
|
+
data-testid="database-source-required-read-only"
|
|
536
|
+
role="status"
|
|
537
|
+
>
|
|
538
|
+
<div>${translateKey(this.std, ...DATABASE_SOURCE_REQUIRED)}</div>
|
|
539
|
+
<div class="${databaseSourceMissingHintStyles}">
|
|
540
|
+
${translateKey(this.std, ...DATABASE_SOURCE_REQUIRED_READ_ONLY)}
|
|
541
|
+
</div>
|
|
542
|
+
${canMove
|
|
543
|
+
? html `<data-view-component-button
|
|
544
|
+
data-testid="database-source-required-move"
|
|
545
|
+
hoverType="border"
|
|
546
|
+
aria-busy="${moving}"
|
|
547
|
+
.icon="${moving ? LoadingIcon() : undefined}"
|
|
548
|
+
.onClick="${this.moveIntoSource}"
|
|
549
|
+
.text="${translateKey(this.std, ...DATABASE_MOVE_INTO_SOURCE)}"
|
|
550
|
+
></data-view-component-button>`
|
|
551
|
+
: nothing}
|
|
552
|
+
</div>`;
|
|
553
|
+
}
|
|
432
554
|
renderDatabaseOps() {
|
|
433
555
|
if (this.dataSource.readonly$.value) {
|
|
434
556
|
return nothing;
|
|
@@ -509,7 +631,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
|
|
|
509
631
|
setSelection: this.setSelection,
|
|
510
632
|
dataSource: this.dataSource,
|
|
511
633
|
headerWidget: this.headerWidget,
|
|
512
|
-
viewPlaceholder: this.renderSourceMissing,
|
|
634
|
+
viewPlaceholder: logic => this.renderSourceRequired(logic) ?? this.renderSourceMissing(logic),
|
|
513
635
|
onDrag: this.onDrag,
|
|
514
636
|
clipboard: this.std.clipboard,
|
|
515
637
|
dnd: this.std.dnd,
|
|
@@ -114,6 +114,13 @@ export declare abstract class HostedDataSourceBase extends DataSourceBase {
|
|
|
114
114
|
propertyDataGet(propertyId: string): Record<string, unknown>;
|
|
115
115
|
propertyDataTypeGet(propertyId: string): TypeInstance | undefined;
|
|
116
116
|
featureFlags$: ReadonlySignal<DatabaseFlags>;
|
|
117
|
+
/**
|
|
118
|
+
* Refuses every edit on a writable store: a database with no source, under
|
|
119
|
+
* a host that requires one, shows its native rows read-only (ADR 0035
|
|
120
|
+
* amendment of 2026-10-29). Every write path of the view layer reads
|
|
121
|
+
* `readonly$` (cells, paste, drag, "New", view and property menus).
|
|
122
|
+
*/
|
|
123
|
+
readonly locked$: import("@preact/signals-core").Signal<boolean>;
|
|
117
124
|
readonly$: ReadonlySignal<boolean>;
|
|
118
125
|
viewConverts: import("./index.js").ViewConvertConfig[];
|
|
119
126
|
viewMetas: ViewMeta[];
|