@formicoidea/labre-core 0.25.0 → 0.26.1

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.
@@ -24,6 +24,7 @@ import { databaseBlockStyles, databaseContentStyles, databaseHeaderBarStyles, da
24
24
  import { BlockRenderer } from './detail-panel/block-renderer.js';
25
25
  import { NoteRenderer } from './detail-panel/note-renderer.js';
26
26
  import { DatabaseSelection } from './selection.js';
27
+ import { DatabaseDataSourceProvider } from './service/index.js';
27
28
  import { currentViewStorage } from './utils/current-view.js';
28
29
  import { getSingleDocIdFromText } from './utils/title-doc.js';
29
30
  export class DatabaseBlockComponent extends CaptionedBlockComponent {
@@ -88,14 +89,18 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
88
89
  });
89
90
  };
90
91
  this.dataSource = lazy(() => {
91
- const dataSource = new DatabaseBlockDataSource(this.model, dataSource => {
92
+ const initFn = (dataSource) => {
92
93
  dataSource.serviceSet(EditorHostKey, this.host);
93
94
  this.std.provider
94
95
  .getAll(ExternalGroupByConfigProvider)
95
96
  .forEach(config => {
96
97
  dataSource.serviceSet(ExternalGroupByConfigProvider(config.name), config);
97
98
  });
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);
99
104
  const id = currentViewStorage.getCurrentView(this.model.id);
100
105
  if (id && dataSource.viewManager.viewGet(id)) {
101
106
  dataSource.viewManager.setCurrentView(id);
@@ -11,4 +11,5 @@ export * from './properties/rich-text/cell-renderer';
11
11
  export * from './selection.js';
12
12
  export * from './service';
13
13
  export * from './utils/block-utils';
14
+ export * from './views';
14
15
  export * from '../data-view/index.js';
@@ -11,4 +11,5 @@ export * from './properties/rich-text/cell-renderer';
11
11
  export * from './selection.js';
12
12
  export * from './service';
13
13
  export * from './utils/block-utils';
14
+ export * from './views';
14
15
  export * from '../data-view/index.js';
@@ -1,5 +1,16 @@
1
- import type { PropertyMetaConfig } from '../../data-view/index.js';
1
+ import type { DatabaseBlockModel } from '../../affine-model/index.js';
2
+ import type { DataSourceBase, PropertyMetaConfig } from '../../data-view/index.js';
2
3
  export interface DatabaseBlockConfigService {
3
4
  propertiesPresets: PropertyMetaConfig[];
4
5
  }
5
6
  export declare const DatabaseBlockConfigService: import("../../global/di/types.js").ServiceIdentifier<DatabaseBlockConfigService> & (<U extends DatabaseBlockConfigService = DatabaseBlockConfigService>(variant: import("../../global/di/types.js").ServiceVariant) => import("../../global/di/types.js").ServiceIdentifier<U>);
7
+ /**
8
+ * Injection seam: a host app may register this provider to back an
9
+ * `affine:database` block with its own data source (e.g. an external
10
+ * collection). Only used when the block carries an `externalSourceId`; with no
11
+ * provider registered the block falls back to the inline blob source.
12
+ */
13
+ export interface DatabaseDataSourceProvider {
14
+ createDataSource(model: DatabaseBlockModel, init?: (ds: DataSourceBase) => void): DataSourceBase;
15
+ }
16
+ export declare const DatabaseDataSourceProvider: import("../../global/di/types.js").ServiceIdentifier<DatabaseDataSourceProvider> & (<U extends DatabaseDataSourceProvider = DatabaseDataSourceProvider>(variant: import("../../global/di/types.js").ServiceVariant) => import("../../global/di/types.js").ServiceIdentifier<U>);
@@ -1,2 +1,3 @@
1
1
  import { createIdentifier } from '../../global/di/index.js';
2
2
  export const DatabaseBlockConfigService = createIdentifier('AffineDatabaseBlockConfigService');
3
+ export const DatabaseDataSourceProvider = createIdentifier('affine:database-data-source-provider');
@@ -6,8 +6,14 @@ export class EditorToolbar extends WithDisposable(LitElement) {
6
6
  static { this.styles = css `
7
7
  ${panelBaseStyle(':host')}
8
8
  :host {
9
- height: 36px;
9
+ min-height: 36px;
10
10
  box-sizing: content-box;
11
+ /* ponytail: wrap to a second row instead of overflowing on mobile. The
12
+ width is capped to availableWidth by floating-ui's size middleware;
13
+ wrapping (not scrolling) keeps the "More" dropdown clickable — it is a
14
+ descendant of this toolbar, so an overflow container would clip it. */
15
+ flex-wrap: wrap;
16
+ row-gap: 4px;
11
17
  }
12
18
 
13
19
  :host([data-without-bg]) {
@@ -18,7 +24,9 @@ export class EditorToolbar extends WithDisposable(LitElement) {
18
24
 
19
25
  ::slotted(*) {
20
26
  display: flex;
21
- height: 100%;
27
+ /* fixed row height (not 100%) so each item stays one row tall when the
28
+ toolbar wraps to multiple rows */
29
+ height: 36px;
22
30
  justify-content: center;
23
31
  align-items: center;
24
32
  gap: 8px;
@@ -91,7 +91,7 @@ let EdgelessMindmapMenu = (() => {
91
91
  :host {
92
92
  display: flex;
93
93
  z-index: -1;
94
- justify-content: flex-end;
94
+ justify-content: center;
95
95
  }
96
96
  .text-and-mindmap {
97
97
  display: flex;
@@ -77,11 +77,6 @@ let EdgelessMindmapToolButton = (() => {
77
77
  display: flex;
78
78
  justify-content: center;
79
79
  align-items: center;
80
- /* Make this button the containing block of the popup's clip wrapper so
81
- the "Others" sub-menu anchors to THIS button (not the whole toolbar)
82
- and can be right-aligned to it — mirrors the framework senior buttons.
83
- Without this the menu drifts when senior buttons are hidden. */
84
- position: relative;
85
80
  }
86
81
  .partial-clip {
87
82
  flex-shrink: 0;
@@ -196,20 +191,6 @@ let EdgelessMindmapToolButton = (() => {
196
191
  });
197
192
  },
198
193
  });
199
- // Right-align the menu's right edge to this button's right edge, like the
200
- // framework senior buttons. Stays correct however many buttons are hidden.
201
- const el = menu.element;
202
- const wrap = el.parentElement;
203
- if (wrap) {
204
- wrap.style.overflow = 'visible';
205
- wrap.style.justifyContent = 'flex-end';
206
- }
207
- Object.assign(el.style, {
208
- position: 'static',
209
- width: 'max-content',
210
- maxWidth: 'calc(100vw - 16px)',
211
- marginLeft: '0',
212
- });
213
194
  }
214
195
  initDragController() {
215
196
  if (!this.edgeless || !this.toolbarContainer)
@@ -7,6 +7,7 @@ export type DatabaseBlockProps = {
7
7
  cells: SerializedCells;
8
8
  columns: Array<ColumnDataType>;
9
9
  comments?: Record<string, boolean>;
10
+ externalSourceId?: string;
10
11
  };
11
12
  export declare class DatabaseBlockModel extends BlockModel<DatabaseBlockProps> {
12
13
  }
@@ -9,10 +9,11 @@ export const DatabaseBlockSchema = defineBlockSchema({
9
9
  cells: Object.create(null),
10
10
  columns: [],
11
11
  comments: undefined,
12
+ externalSourceId: undefined,
12
13
  }),
13
14
  metadata: {
14
15
  role: 'hub',
15
- version: 3,
16
+ version: 4,
16
17
  parent: ['affine:note'],
17
18
  children: ['affine:paragraph', 'affine:list'],
18
19
  },
@@ -65,6 +65,10 @@ const SENIOR_TOOL_NAV_SIZE = 20;
65
65
  const DIVIDER_WIDTH = 8;
66
66
  const DIVIDER_SPACE = 8;
67
67
  const SAFE_AREA_WIDTH = 64;
68
+ // Cap the whole toolbar width so the centered senior sub-menus land close to
69
+ // the buttons. The ResizeObserver reads the host width, so the senior-tool
70
+ // scroll adapts to this cap; on viewports narrower than it, it has no effect.
71
+ const TOOLBAR_MAX_WIDTH = 900;
68
72
  export const EDGELESS_TOOLBAR_WIDGET = 'edgeless-toolbar-widget';
69
73
  let EdgelessToolbarWidget = (() => {
70
74
  let _classSuper = WidgetComponent;
@@ -109,6 +113,8 @@ let EdgelessToolbarWidget = (() => {
109
113
  -webkit-user-select: none;
110
114
  user-select: none;
111
115
  width: 100%;
116
+ /* cap + center the toolbar so centered senior sub-menus stay near the buttons */
117
+ max-width: ${unsafeCSS(TOOLBAR_MAX_WIDTH)}px;
112
118
  pointer-events: none;
113
119
  }
114
120
  .edgeless-toolbar-wrapper {
@@ -46,8 +46,12 @@ export function autoUpdatePosition(signal, toolbar, referenceElement, flavour, p
46
46
  middleware: [
47
47
  offset(10 + offsetY),
48
48
  size({
49
- apply: ({ elements }) => {
49
+ padding: 10,
50
+ apply: ({ elements, availableWidth }) => {
50
51
  elements.floating.style.width = 'fit-content';
52
+ // ponytail: cap to viewport so the toolbar scrolls instead of
53
+ // overflowing off-screen on mobile; drop into "⋮" if scroll feels off
54
+ elements.floating.style.maxWidth = `${availableWidth}px`;
51
55
  },
52
56
  }),
53
57
  isInline ? inline() : undefined,
@@ -3,11 +3,18 @@ import { VElement, VLine, VText } from './inline/index.js';
3
3
  import { EditorHost } from './view/index.js';
4
4
  export function effects() {
5
5
  // editor host
6
- customElements.define('editor-host', EditorHost);
6
+ define('editor-host', EditorHost);
7
7
  // gfx
8
- customElements.define('gfx-viewport', GfxViewportElement);
8
+ define('gfx-viewport', GfxViewportElement);
9
9
  // inline
10
- customElements.define('v-element', VElement);
11
- customElements.define('v-line', VLine);
12
- customElements.define('v-text', VText);
10
+ define('v-element', VElement);
11
+ define('v-line', VLine);
12
+ define('v-text', VText);
13
+ }
14
+ // ponytail: idempotent so effects() can run more than once (e.g. across browser
15
+ // unit-test files sharing a registry) without throwing on re-registration.
16
+ function define(name, ctor) {
17
+ if (!customElements.get(name)) {
18
+ customElements.define(name, ctor);
19
+ }
13
20
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formicoidea/labre-core",
3
3
  "description": "Labre editor — the full BlockSuite-derived editor as one package.",
4
- "version": "0.25.0",
4
+ "version": "0.26.1",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "author": "lajola",