mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-04 11:09:01 +08:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b15a2a9638 | |||
| f3218ab3bc | |||
| f79324b6a1 | |||
| adcc6b578c | |||
| 7f833f8c15 | |||
| 785951bbfa | |||
| 19e9f970f4 | |||
| c362737441 | |||
| 618042812b | |||
| 0ff7c5f897 | |||
| b8dcb85007 | |||
| 5ac15f12e6 | |||
| efe36161e8 | |||
| 126677d7ad | |||
| 007bbabce4 | |||
| 64cc99354e | |||
| 1516903c77 | |||
| 4f831732e1 | |||
| ef28e36441 | |||
| 7b1dfb7ee8 | |||
| 5fcc402280 | |||
| fa86f71853 | |||
| 4fee2a9c4b | |||
| b4097aef8e | |||
| 9f4311f654 | |||
| 13f1859cdf | |||
| 50820482df | |||
| ec67d30b27 | |||
| fd5897dbe6 | |||
| d490e767eb | |||
| adc003862b | |||
| ff0ce1a962 | |||
| 5042d9f644 | |||
| 1d339c682b | |||
| b38abcb59c | |||
| c3fc0a0d88 | |||
| e0b2b2b33c | |||
| ba91b36cc3 | |||
| a0e3f9909c | |||
| 8f6ce2c3b4 | |||
| d8435421d2 | |||
| 676ccc9094 | |||
| 091ba7bb51 | |||
| 2b11941c0e | |||
| 02f567f2c0 | |||
| 54b7515167 | |||
| e726df9a1b | |||
| 926b35c91f | |||
| b456feee63 | |||
| 487158b9ca | |||
| 5b768d9091 | |||
| 90b0982dd3 | |||
| e5a1595980 | |||
| bc34516e6c | |||
| 521ee9d374 | |||
| 61ee5531f4 | |||
| 0f770093b0 | |||
| 2d9162b3c4 | |||
| c39a93e1fd | |||
| 53cada4640 | |||
| 60a9572c88 | |||
| e5315087cb | |||
| 319d909ac8 | |||
| 751f229e30 | |||
| c0cc4224bb | |||
| b50e507fc5 | |||
| 294002101d | |||
| 320875425c | |||
| dad39d1129 | |||
| 35f7f5a01b | |||
| 29f8a627b6 | |||
| 5a7ab880c1 | |||
| b20d316d60 | |||
| dc7e7cfc75 | |||
| 2f04e3180c | |||
| faf6e2c79f | |||
| 15e9acefc2 | |||
| 176e0a1950 | |||
| 4c7eedb920 | |||
| c21f71f58c | |||
| 75678ceca8 | |||
| cd00f06b77 | |||
| cb15d48b82 | |||
| 78346be4fe | |||
| 13834dd09a | |||
| 4e546c78ef | |||
| e639f08b71 | |||
| cedee0a1b2 | |||
| 0ed8b4f46a | |||
| 88095a87a8 | |||
| 3f4b7ec51e | |||
| 892fd16f52 | |||
| 73f3226f58 | |||
| 0af921c3fc |
@@ -17,7 +17,6 @@
|
||||
"@blocksuite/blocks": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/presets": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@blocksuite/sync": "workspace:*"
|
||||
},
|
||||
@@ -37,7 +36,6 @@
|
||||
"./inline": "./src/inline/index.ts",
|
||||
"./inline/consts": "./src/inline/consts.ts",
|
||||
"./inline/types": "./src/inline/types.ts",
|
||||
"./presets": "./src/presets/index.ts",
|
||||
"./blocks": "./src/blocks/index.ts",
|
||||
"./blocks/schemas": "./src/blocks/schemas.ts",
|
||||
"./sync": "./src/sync/index.ts"
|
||||
@@ -83,9 +81,6 @@
|
||||
"inline/types": [
|
||||
"dist/inline/types.d.ts"
|
||||
],
|
||||
"presets": [
|
||||
"dist/presets/index.d.ts"
|
||||
],
|
||||
"blocks": [
|
||||
"dist/blocks/index.d.ts"
|
||||
],
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { effects as blocksEffects } from '@blocksuite/blocks/effects';
|
||||
import { effects as presetsEffects } from '@blocksuite/presets/effects';
|
||||
|
||||
export function effects() {
|
||||
blocksEffects();
|
||||
presetsEffects();
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from '@blocksuite/presets';
|
||||
@@ -11,7 +11,6 @@
|
||||
{ "path": "../../blocks" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../presets" },
|
||||
{ "path": "../../framework/store" },
|
||||
{ "path": "../../framework/sync" }
|
||||
]
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"file-type": "^20.0.0",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -38,8 +38,6 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
||||
|
||||
protected _isResizing = false;
|
||||
|
||||
protected _isSelected = false;
|
||||
|
||||
protected _whenHover: HoverController | null = new HoverController(
|
||||
this,
|
||||
({ abortController }) => {
|
||||
|
||||
@@ -35,7 +35,7 @@ export class AttachmentEdgelessBlockComponent extends toGfxBlockComponent(
|
||||
this.slots.elementResizeEnd.on(() => {
|
||||
this._isResizing = false;
|
||||
this._showOverlay =
|
||||
this._isResizing || this._isDragging || !this._isSelected;
|
||||
this._isResizing || this._isDragging || !this.selected$.peek();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import type {
|
||||
AttachmentBlockModel,
|
||||
ImageBlockProps,
|
||||
import {
|
||||
type AttachmentBlockModel,
|
||||
type ImageBlockProps,
|
||||
MAX_IMAGE_WIDTH,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { FileSizeLimitService } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
readImageSize,
|
||||
transformModel,
|
||||
withTempBlobData,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { type BlockStdScope, StdIdentifier } from '@blocksuite/block-std';
|
||||
import type { Container } from '@blocksuite/global/di';
|
||||
import { createIdentifier } from '@blocksuite/global/di';
|
||||
import { Bound } from '@blocksuite/global/utils';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { Extension } from '@blocksuite/store';
|
||||
import type { TemplateResult } from 'lit';
|
||||
import { html } from 'lit';
|
||||
|
||||
import { getAttachmentBlob } from './utils';
|
||||
|
||||
export type AttachmentEmbedConfig = {
|
||||
name: string;
|
||||
/**
|
||||
@@ -24,7 +29,10 @@ export type AttachmentEmbedConfig = {
|
||||
/**
|
||||
* The action will be executed when the 「Turn into embed view」 button is clicked.
|
||||
*/
|
||||
action?: (model: AttachmentBlockModel) => Promise<void> | void;
|
||||
action?: (
|
||||
model: AttachmentBlockModel,
|
||||
std: BlockStdScope
|
||||
) => Promise<void> | void;
|
||||
/**
|
||||
* The template will be used to render the embed view.
|
||||
*/
|
||||
@@ -89,11 +97,11 @@ export class AttachmentEmbedService extends Extension {
|
||||
// Converts to embed view.
|
||||
convertTo(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
||||
const config = this.values.find(config => config.check(model, maxFileSize));
|
||||
if (!config || !config.action) {
|
||||
if (!config?.action) {
|
||||
model.doc.updateBlock(model, { embed: true });
|
||||
return;
|
||||
}
|
||||
config.action(model)?.catch(console.error);
|
||||
config.action(model, this.std)?.catch(console.error);
|
||||
}
|
||||
|
||||
embedded(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
|
||||
@@ -123,7 +131,12 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
||||
check: model =>
|
||||
model.doc.schema.flavourSchemaMap.has('affine:image') &&
|
||||
model.type.startsWith('image/'),
|
||||
action: model => turnIntoImageBlock(model),
|
||||
async action(model, std) {
|
||||
const component = std.view.getBlock(model.id);
|
||||
if (!component) return;
|
||||
|
||||
await turnIntoImageBlock(model);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pdf',
|
||||
@@ -171,7 +184,7 @@ const embedConfig: AttachmentEmbedConfig[] = [
|
||||
/**
|
||||
* Turn the attachment block into an image block.
|
||||
*/
|
||||
export function turnIntoImageBlock(model: AttachmentBlockModel) {
|
||||
export async function turnIntoImageBlock(model: AttachmentBlockModel) {
|
||||
if (!model.doc.schema.flavourSchemaMap.has('affine:image')) {
|
||||
console.error('The image flavour is not supported!');
|
||||
return;
|
||||
@@ -183,15 +196,37 @@ export function turnIntoImageBlock(model: AttachmentBlockModel) {
|
||||
const { saveAttachmentData, getImageData } = withTempBlobData();
|
||||
saveAttachmentData(sourceId, { name: model.name });
|
||||
|
||||
const imageConvertData = model.sourceId
|
||||
? getImageData(model.sourceId)
|
||||
let imageSize = model.sourceId ? getImageData(model.sourceId) : undefined;
|
||||
|
||||
const bounds = model.xywh
|
||||
? Bound.fromXYWH(model.deserializedXYWH)
|
||||
: undefined;
|
||||
|
||||
if (bounds) {
|
||||
if (!imageSize?.width || !imageSize?.height) {
|
||||
const blob = await getAttachmentBlob(model);
|
||||
if (blob) {
|
||||
imageSize = await readImageSize(blob);
|
||||
}
|
||||
}
|
||||
|
||||
if (imageSize?.width && imageSize?.height) {
|
||||
const p = imageSize.height / imageSize.width;
|
||||
imageSize.width = Math.min(imageSize.width, MAX_IMAGE_WIDTH);
|
||||
imageSize.height = imageSize.width * p;
|
||||
bounds.w = imageSize.width;
|
||||
bounds.h = imageSize.height;
|
||||
}
|
||||
}
|
||||
|
||||
const others = bounds ? { xywh: bounds.serialize() } : undefined;
|
||||
|
||||
const imageProp: Partial<ImageBlockProps> = {
|
||||
sourceId,
|
||||
caption: model.caption,
|
||||
size: model.size,
|
||||
...imageConvertData,
|
||||
...imageSize,
|
||||
...others,
|
||||
};
|
||||
transformModel(model, 'affine:image', imageProp);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ export async function uploadAttachmentBlob(
|
||||
}
|
||||
}
|
||||
|
||||
async function getAttachmentBlob(model: AttachmentBlockModel) {
|
||||
export async function getAttachmentBlob(model: AttachmentBlockModel) {
|
||||
const sourceId = model.sourceId;
|
||||
if (!sourceId) {
|
||||
return null;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
|
||||
@@ -4,9 +4,10 @@ import {
|
||||
} from '@blocksuite/affine-components/caption';
|
||||
import type { BookmarkBlockModel } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
import { html } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { refreshBookmarkUrlData } from './utils.js';
|
||||
@@ -14,6 +15,12 @@ import { refreshBookmarkUrlData } from './utils.js';
|
||||
export const BOOKMARK_MIN_WIDTH = 450;
|
||||
|
||||
export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBlockModel> {
|
||||
selectedStyle$: ReadonlySignal<ClassInfo> | null = computed<ClassInfo>(
|
||||
() => ({
|
||||
'selected-style': this.selected$.value,
|
||||
})
|
||||
);
|
||||
|
||||
private _fetchAbortController?: AbortController;
|
||||
|
||||
blockDraggable = true;
|
||||
@@ -69,16 +76,12 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
||||
}
|
||||
|
||||
override renderBlock() {
|
||||
const selected = this.selected$.value;
|
||||
const isInEdgeless =
|
||||
this.std.get(DocModeProvider).getEditorMode() === 'edgeless';
|
||||
|
||||
return html`
|
||||
<div
|
||||
draggable="${this.blockDraggable ? 'true' : 'false'}"
|
||||
class=${classMap({
|
||||
'affine-bookmark-container': true,
|
||||
'selected-style': selected && !isInEdgeless,
|
||||
...this.selectedStyle$?.value,
|
||||
})}
|
||||
style=${this.containerStyleMap}
|
||||
>
|
||||
|
||||
@@ -10,6 +10,8 @@ import { BookmarkBlockComponent } from './bookmark-block.js';
|
||||
export class BookmarkEdgelessBlockComponent extends toGfxBlockComponent(
|
||||
BookmarkBlockComponent
|
||||
) {
|
||||
override selectedStyle$ = null;
|
||||
|
||||
override blockDraggable = false;
|
||||
|
||||
override getRenderingRect() {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"date-fns": "^4.0.0",
|
||||
"lit": "^3.2.0",
|
||||
|
||||
@@ -34,8 +34,6 @@ import {
|
||||
} from './properties/index.js';
|
||||
import {
|
||||
addProperty,
|
||||
applyCellsUpdate,
|
||||
applyPropertyUpdate,
|
||||
copyCellsByProperty,
|
||||
deleteRows,
|
||||
deleteView,
|
||||
@@ -169,7 +167,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
columnId: propertyId,
|
||||
value: newValue,
|
||||
});
|
||||
applyCellsUpdate(this._model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +196,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
insertToPosition,
|
||||
property.create(this.newPropertyName())
|
||||
);
|
||||
applyPropertyUpdate(this._model);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -283,7 +279,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
propertyDataSet(propertyId: string, data: Record<string, unknown>): void {
|
||||
this._runCapture();
|
||||
this.updateProperty(propertyId, () => ({ data }));
|
||||
applyPropertyUpdate(this._model);
|
||||
}
|
||||
|
||||
propertyDataTypeGet(propertyId: string): TypeInstance | undefined {
|
||||
@@ -337,7 +332,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
schema
|
||||
);
|
||||
copyCellsByProperty(this._model, copyId, id);
|
||||
applyPropertyUpdate(this._model);
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -366,7 +360,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
propertyNameSet(propertyId: string, name: string): void {
|
||||
this.doc.captureSync();
|
||||
this.updateProperty(propertyId, () => ({ name }));
|
||||
applyPropertyUpdate(this._model);
|
||||
}
|
||||
|
||||
override propertyReadonlyGet(propertyId: string): boolean {
|
||||
@@ -421,7 +414,6 @@ export class DatabaseBlockDataSource extends DataSourceBase {
|
||||
}
|
||||
});
|
||||
updateCells(this._model, propertyId, cells);
|
||||
applyPropertyUpdate(this._model);
|
||||
}
|
||||
|
||||
rowAdd(insertPosition: InsertToPosition | number): string {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { DatabaseBlockModel } from '@blocksuite/affine-model';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import { BlockComponent } from '@blocksuite/block-std';
|
||||
import { DatabaseListViewIcon } from '@blocksuite/icons/lit';
|
||||
import { css, html } from 'lit';
|
||||
|
||||
export class DatabaseDndPreviewBlockComponent extends BlockComponent<DatabaseBlockModel> {
|
||||
static override styles = css`
|
||||
.affine-database-preview-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
|
||||
border-radius: 8px;
|
||||
background-color: ${unsafeCSSVarV2(
|
||||
'layer/background/overlayPanel',
|
||||
'#FBFBFC'
|
||||
)};
|
||||
}
|
||||
|
||||
.database-preview-content {
|
||||
padding: 4px 16px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.database-preview-content > svg {
|
||||
color: ${unsafeCSSVarV2('icon/primary', '#77757D')};
|
||||
}
|
||||
|
||||
.database-preview-content > .text {
|
||||
color: var(--affine-text-primary-color);
|
||||
color: ${unsafeCSSVarV2('text/primary', '#121212')};
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
override renderBlock() {
|
||||
return html`<div
|
||||
class="affine-database-preview-container"
|
||||
contenteditable="false"
|
||||
>
|
||||
<div class="database-preview-content">
|
||||
${DatabaseListViewIcon({ width: '24px', height: '24px' })}
|
||||
<span class="text">Database Block</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'affine-dnd-preview-database': DatabaseDndPreviewBlockComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CenterPeek } from './components/layout';
|
||||
import { DatabaseTitle } from './components/title';
|
||||
import { DatabaseBlockComponent } from './database-block';
|
||||
import { DatabaseDndPreviewBlockComponent } from './database-dnd-preview-block';
|
||||
import { BlockRenderer } from './detail-panel/block-renderer';
|
||||
import { NoteRenderer } from './detail-panel/note-renderer';
|
||||
import { LinkCell, LinkCellEditing } from './properties/link/cell-renderer';
|
||||
@@ -35,4 +36,9 @@ export function effects() {
|
||||
customElements.define('database-datasource-block-renderer', BlockRenderer);
|
||||
customElements.define('affine-database-link-node', LinkNode);
|
||||
customElements.define('affine-database', DatabaseBlockComponent);
|
||||
|
||||
customElements.define(
|
||||
'affine-dnd-preview-database',
|
||||
DatabaseDndPreviewBlockComponent
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,24 +37,6 @@ export function addProperty(
|
||||
return id;
|
||||
}
|
||||
|
||||
export function applyCellsUpdate(model: DatabaseBlockModel) {
|
||||
model.doc.updateBlock(model, {
|
||||
cells: model.cells,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyPropertyUpdate(model: DatabaseBlockModel) {
|
||||
model.doc.updateBlock(model, {
|
||||
columns: model.columns,
|
||||
});
|
||||
}
|
||||
|
||||
export function applyViewsUpdate(model: DatabaseBlockModel) {
|
||||
model.doc.updateBlock(model, {
|
||||
views: model.views,
|
||||
});
|
||||
}
|
||||
|
||||
export function copyCellsByProperty(
|
||||
model: DatabaseBlockModel,
|
||||
fromId: Column['id'],
|
||||
@@ -156,7 +138,6 @@ export function moveViewTo(
|
||||
arr => insertPositionToIndex(position, arr)
|
||||
);
|
||||
});
|
||||
applyViewsUpdate(model);
|
||||
}
|
||||
|
||||
export function updateCell(
|
||||
@@ -255,6 +236,5 @@ export const updateView = <ViewData extends ViewBasicDataType>(
|
||||
return { ...v, ...update(v as ViewData) };
|
||||
});
|
||||
});
|
||||
applyViewsUpdate(model);
|
||||
};
|
||||
export const DATABASE_CONVERT_WHITE_LIST = ['affine:list', 'affine:paragraph'];
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "3.0.5"
|
||||
"vitest": "3.0.6"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
|
||||
@@ -12,10 +12,11 @@ import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { BlockService } from '@blocksuite/block-std';
|
||||
import type { GfxCompatibleProps } from '@blocksuite/block-std/gfx';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import { computed, type ReadonlySignal } from '@preact/signals-core';
|
||||
import type { TemplateResult } from 'lit';
|
||||
import { html } from 'lit';
|
||||
import { query } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
export class EmbedBlockComponent<
|
||||
@@ -23,6 +24,12 @@ export class EmbedBlockComponent<
|
||||
Service extends BlockService = BlockService,
|
||||
WidgetName extends string = string,
|
||||
> extends CaptionedBlockComponent<Model, Service, WidgetName> {
|
||||
selectedStyle$: ReadonlySignal<ClassInfo> | null = computed<ClassInfo>(
|
||||
() => ({
|
||||
'selected-style': this.selected$.value,
|
||||
})
|
||||
);
|
||||
|
||||
private _fetchAbortController = new AbortController();
|
||||
|
||||
_cardStyle: EmbedCardStyle = 'horizontal';
|
||||
@@ -43,10 +50,6 @@ export class EmbedBlockComponent<
|
||||
protected embedContainerStyle: StyleInfo = {};
|
||||
|
||||
renderEmbed = (content: () => TemplateResult) => {
|
||||
const selected = this.selected$.value;
|
||||
const isInEdgeless =
|
||||
this.std.get(DocModeProvider).getEditorMode() === 'edgeless';
|
||||
|
||||
if (
|
||||
this._cardStyle === 'horizontal' ||
|
||||
this._cardStyle === 'horizontalThin' ||
|
||||
@@ -54,7 +57,7 @@ export class EmbedBlockComponent<
|
||||
) {
|
||||
this.style.display = 'block';
|
||||
|
||||
if (isInEdgeless) {
|
||||
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
||||
this.style.minWidth = `${EMBED_CARD_MIN_WIDTH}px`;
|
||||
}
|
||||
}
|
||||
@@ -64,7 +67,7 @@ export class EmbedBlockComponent<
|
||||
draggable="${this.blockDraggable ? 'true' : 'false'}"
|
||||
class=${classMap({
|
||||
'embed-block-container': true,
|
||||
'selected-style': selected && !isInEdgeless,
|
||||
...this.selectedStyle$?.value,
|
||||
})}
|
||||
style=${styleMap({
|
||||
height: `${this._cardHeight}px`,
|
||||
|
||||
@@ -404,7 +404,6 @@ export function createLinkedDocFromSlice(
|
||||
snapshots: BlockSnapshot[],
|
||||
docTitle?: string
|
||||
) {
|
||||
// const modelsWithChildren = (list:BlockModel[]):BlockModel[]=>list.flatMap(model=>[model,...modelsWithChildren(model.children)])
|
||||
const linkedDoc = doc.workspace.createDoc({});
|
||||
linkedDoc.load(() => {
|
||||
const rootId = linkedDoc.addBlock('affine:page', {
|
||||
|
||||
@@ -22,6 +22,8 @@ export function toEdgelessEmbedBlock<
|
||||
B extends typeof EmbedBlockComponent<Model, Service, WidgetName>,
|
||||
>(block: B) {
|
||||
return class extends toGfxBlockComponent(block) {
|
||||
override selectedStyle$ = null;
|
||||
|
||||
_isDragging = false;
|
||||
|
||||
_isResizing = false;
|
||||
|
||||
@@ -246,7 +246,9 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
||||
}
|
||||
|
||||
get linkedDoc() {
|
||||
return this.std.workspace.getDoc(this.model.pageId);
|
||||
return this.std.workspace.getDoc(this.model.pageId, {
|
||||
id: this.model.pageId,
|
||||
});
|
||||
}
|
||||
|
||||
private _handleDoubleClick(event: MouseEvent) {
|
||||
|
||||
+1
-1
@@ -164,7 +164,7 @@ export class EmbedEdgelessSyncedDocBlockComponent extends toEdgelessEmbedBlock(
|
||||
this.cardStyleMap = {
|
||||
display: 'block',
|
||||
width: `${EMBED_CARD_WIDTH[style]}px`,
|
||||
height: `${EMBED_CARD_WIDTH[style]}px`,
|
||||
height: `${EMBED_CARD_HEIGHT[style]}px`,
|
||||
transform: `scale(${bound.w / EMBED_CARD_WIDTH[style]}, ${bound.h / EMBED_CARD_HEIGHT[style]})`,
|
||||
transformOrigin: '0 0',
|
||||
};
|
||||
|
||||
@@ -245,7 +245,10 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
||||
</span>
|
||||
</div>
|
||||
`
|
||||
: guard([editorMode, syncedDoc], renderEditor)}
|
||||
: guard(
|
||||
[editorMode, syncedDoc, appTheme, edgelessTheme],
|
||||
renderEditor
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
class=${classMap({
|
||||
|
||||
@@ -57,6 +57,7 @@ export const blockStyles = css`
|
||||
}
|
||||
|
||||
.affine-embed-synced-doc-container {
|
||||
border: 1px solid var(--affine-border-color);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -69,12 +70,6 @@ export const blockStyles = css`
|
||||
width: 100%;
|
||||
height: calc(${EMBED_CARD_HEIGHT.syncedDoc}px + 36px);
|
||||
}
|
||||
.affine-embed-synced-doc-container:hover.light {
|
||||
box-shadow: 0px 0px 0px 2px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.affine-embed-synced-doc-container:hover.dark {
|
||||
box-shadow: 0px 0px 0px 2px rgba(255, 255, 255, 0.14);
|
||||
}
|
||||
.affine-embed-synced-doc-header-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -127,9 +122,6 @@ export const blockStyles = css`
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.affine-embed-synced-doc-editor-overlay:hover {
|
||||
background: var(--affine-hover-color);
|
||||
}
|
||||
|
||||
.affine-embed-synced-doc-editor-empty {
|
||||
display: flex;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-surface": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
@@ -23,10 +24,11 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
|
||||
+13
-4
@@ -21,14 +21,13 @@ import {
|
||||
type IVec,
|
||||
type SerializedXYWH,
|
||||
} from '@blocksuite/global/utils';
|
||||
import { Text } from '@blocksuite/store';
|
||||
import { type BlockModel, Text } from '@blocksuite/store';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { areSetsEqual } from './utils/misc.js';
|
||||
import { isFrameBlock } from './utils/query.js';
|
||||
|
||||
const FRAME_PADDING = 40;
|
||||
|
||||
export type NavigatorMode = 'fill' | 'fit';
|
||||
|
||||
export class FrameOverlay extends Overlay {
|
||||
static override overlayName: string = 'frame';
|
||||
|
||||
@@ -461,3 +460,13 @@ export class EdgelessFrameManager extends GfxExtension {
|
||||
this._disposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function areSetsEqual<T>(setA: Set<T>, setB: Set<T>) {
|
||||
if (setA.size !== setB.size) return false;
|
||||
for (const a of setA) if (!setB.has(a)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isFrameBlock(element: unknown): element is FrameBlockModel {
|
||||
return !!element && (element as BlockModel).flavour === 'affine:frame';
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './frame-block.js';
|
||||
export * from './frame-manager.js';
|
||||
export * from './frame-spec.js';
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-surface" },
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"file-type": "^20.0.0",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -40,17 +40,18 @@ export const ImageDropOption = FileDropConfigExtension({
|
||||
if (isInsideEdgelessEditor(std.host)) {
|
||||
const gfx = std.get(GfxControllerIdentifier);
|
||||
point = gfx.viewport.toViewCoordFromClientCoord(point);
|
||||
addImages(std, files, { point, maxWidth: MAX_IMAGE_WIDTH }).catch(
|
||||
console.error
|
||||
);
|
||||
addImages(std, files, { point, maxWidth: MAX_IMAGE_WIDTH })
|
||||
.then(() => {
|
||||
std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
|
||||
control: 'canvas:drop',
|
||||
page: 'whiteboard editor',
|
||||
module: 'toolbar',
|
||||
segment: 'toolbar',
|
||||
type: 'image',
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
|
||||
control: 'canvas:drop',
|
||||
page: 'whiteboard editor',
|
||||
module: 'toolbar',
|
||||
segment: 'toolbar',
|
||||
type: 'image',
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,4 +33,4 @@ export const ImageBlockSpec: ExtensionType[] = [
|
||||
ImageBlockAdapterExtensions,
|
||||
].flat();
|
||||
|
||||
export const ImageStoreSpec: ExtensionType[] = [ImageProxyService].flat();
|
||||
export const ImageStoreSpec: ExtensionType[] = [ImageProxyService];
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import {
|
||||
downloadBlob,
|
||||
humanFileSize,
|
||||
readImageSize,
|
||||
transformModel,
|
||||
withTempBlobData,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
@@ -425,27 +426,6 @@ export async function turnImageIntoCardView(
|
||||
transformModel(model, 'affine:attachment', attachmentProp);
|
||||
}
|
||||
|
||||
export function readImageSize(file: File | Blob) {
|
||||
return new Promise<{ width: number; height: number }>(resolve => {
|
||||
const size = { width: 0, height: 0 };
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
size.width = img.width;
|
||||
size.height = img.height;
|
||||
URL.revokeObjectURL(img.src);
|
||||
resolve(size);
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(img.src);
|
||||
resolve(size);
|
||||
};
|
||||
|
||||
img.src = URL.createObjectURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
export async function addImages(
|
||||
std: BlockStdScope,
|
||||
files: File[],
|
||||
@@ -512,9 +492,17 @@ export async function addImages(
|
||||
// upload image data and update the image model
|
||||
const uploadPromises = imageFiles.map(async (file, index) => {
|
||||
const { point, blockId } = dropInfos[index];
|
||||
const block = std.store.getBlock(blockId);
|
||||
const imageSize = await readImageSize(file);
|
||||
|
||||
if (!imageSize.width || !imageSize.height) {
|
||||
std.store.deleteBlock(block!.model);
|
||||
|
||||
toast(std.host, 'Failed to read image size, please try another image');
|
||||
throw new Error('Failed to read image size');
|
||||
}
|
||||
|
||||
const sourceId = await std.store.blobSync.set(file);
|
||||
const imageSize = await readImageSize(file);
|
||||
|
||||
const center = Vec.toVec(point);
|
||||
// If maxWidth is provided, limit the width of the image to maxWidth
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"katex": "^0.16.11",
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "3.0.5"
|
||||
"vitest": "3.0.6"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
|
||||
@@ -115,46 +115,92 @@ export const splitListCommand: Command<{
|
||||
let newListId: string | null = null;
|
||||
|
||||
if (model.children.length > 0 && !model.collapsed) {
|
||||
/**
|
||||
* case 3: list has children (list not collapsed)
|
||||
*
|
||||
* before:
|
||||
* - aa|a <- split here
|
||||
* - bbb
|
||||
*
|
||||
* after:
|
||||
* - aa
|
||||
* - |a
|
||||
* - bbb
|
||||
*/
|
||||
const afterText = model.text.split(inlineIndex);
|
||||
newListId = doc.addBlock(
|
||||
'affine:list',
|
||||
{
|
||||
type: model.type,
|
||||
text: afterText,
|
||||
order: model.type === 'numbered' ? 1 : null,
|
||||
},
|
||||
model,
|
||||
0
|
||||
);
|
||||
|
||||
if (model.type === 'numbered') {
|
||||
const nextContinuousNumberedLists = getNextContinuousNumberedLists(
|
||||
doc,
|
||||
newListId
|
||||
if (inlineIndex === 0) {
|
||||
/**
|
||||
* case 3: list has children (list not collapsed), split the list at the start of line
|
||||
*
|
||||
* before:
|
||||
* - |aaa <- split here
|
||||
* - bbb
|
||||
*
|
||||
* after:
|
||||
* -
|
||||
* - |aaa
|
||||
* - bbb
|
||||
*/
|
||||
newListId = doc.addBlock(
|
||||
'affine:list',
|
||||
{
|
||||
type: model.type,
|
||||
text: afterText,
|
||||
order:
|
||||
model.type === 'numbered' && model.order !== null
|
||||
? model.order + 1
|
||||
: null,
|
||||
},
|
||||
parent,
|
||||
modelIndex + 1
|
||||
);
|
||||
let base = 2;
|
||||
nextContinuousNumberedLists.forEach(list => {
|
||||
doc.transact(() => {
|
||||
list.order = base;
|
||||
const newList = doc.getBlock(newListId)?.model;
|
||||
if (!newList) return;
|
||||
// move children to new list
|
||||
doc.moveBlocks(model.children, newList);
|
||||
|
||||
if (model.type === 'numbered' && model.order !== null) {
|
||||
const nextContinuousNumberedLists = getNextContinuousNumberedLists(
|
||||
doc,
|
||||
newListId
|
||||
);
|
||||
let base = model.order + 2;
|
||||
nextContinuousNumberedLists.forEach(list => {
|
||||
doc.transact(() => {
|
||||
list.order = base;
|
||||
});
|
||||
base += 1;
|
||||
});
|
||||
base += 1;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* case 4: list has children (list not collapsed), split the list not at the start of line
|
||||
*
|
||||
* before:
|
||||
* - aa|a <- split here
|
||||
* - bbb
|
||||
*
|
||||
* after:
|
||||
* - aa
|
||||
* - |a
|
||||
* - bbb
|
||||
*/
|
||||
newListId = doc.addBlock(
|
||||
'affine:list',
|
||||
{
|
||||
type: model.type,
|
||||
text: afterText,
|
||||
order: model.type === 'numbered' ? 1 : null,
|
||||
},
|
||||
model,
|
||||
0
|
||||
);
|
||||
|
||||
if (model.type === 'numbered') {
|
||||
const nextContinuousNumberedLists = getNextContinuousNumberedLists(
|
||||
doc,
|
||||
newListId
|
||||
);
|
||||
let base = 2;
|
||||
nextContinuousNumberedLists.forEach(list => {
|
||||
doc.transact(() => {
|
||||
list.order = base;
|
||||
});
|
||||
base += 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* case 4: list has children (list collapsed)
|
||||
* case 5: list has children (list collapsed)
|
||||
*
|
||||
* before:
|
||||
* - aa|a <- split here
|
||||
@@ -166,7 +212,7 @@ export const splitListCommand: Command<{
|
||||
* - |a
|
||||
*
|
||||
*
|
||||
* case 5: list does not have children
|
||||
* case 6: list does not have children
|
||||
*
|
||||
* before:
|
||||
* - aa|a <- split here
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"lit": "^3.2.0",
|
||||
|
||||
@@ -36,7 +36,6 @@ import { property } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { NoteConfigExtension } from '../config';
|
||||
import { isPageBlock } from '../utils';
|
||||
import * as styles from './edgeless-note-background.css';
|
||||
|
||||
@requiredProperties({
|
||||
@@ -162,7 +161,7 @@ export class EdgelessNoteBackground extends SignalWatcher(
|
||||
@pointerdown=${stopPropagation}
|
||||
@click=${this._handleClickAtBackground}
|
||||
>
|
||||
${isPageBlock(this.std, this.note) ? this._renderHeader() : nothing}
|
||||
${this.note.isPageBlock() ? this._renderHeader() : nothing}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export class EdgelessNoteMask extends SignalWatcher(
|
||||
bottom: `${-extra}px`,
|
||||
right: `${-extra}px`,
|
||||
zIndex: '1',
|
||||
pointerEvents: this.editing ? 'none' : 'auto',
|
||||
pointerEvents: this.editing || this.disableMask ? 'none' : 'auto',
|
||||
borderRadius: `${
|
||||
this.model.edgeless.style.borderRadius * this.zoom
|
||||
}px`,
|
||||
@@ -64,6 +64,9 @@ export class EdgelessNoteMask extends SignalWatcher(
|
||||
`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor disableMask!: boolean;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editing!: boolean;
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import { NoteConfigExtension } from '../config';
|
||||
import { isPageBlock } from '../utils';
|
||||
import * as styles from './edgeless-page-block-title.css';
|
||||
|
||||
@requiredProperties({
|
||||
@@ -22,7 +21,7 @@ export class EdgelessPageBlockTitle extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
) {
|
||||
override render() {
|
||||
if (!isPageBlock(this.std, this.note)) return;
|
||||
if (!this.note.isPageBlock()) return;
|
||||
|
||||
const title = this.std
|
||||
.getOptional(NoteConfigExtension.identifier)
|
||||
|
||||
@@ -6,4 +6,3 @@ export * from './note-block';
|
||||
export * from './note-edgeless-block';
|
||||
export * from './note-service';
|
||||
export * from './note-spec';
|
||||
export { isPageBlock } from './utils';
|
||||
|
||||
@@ -12,10 +12,10 @@ import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { MoreIndicator } from './components/more-indicator';
|
||||
import { NoteConfigExtension } from './config';
|
||||
import { NoteBlockComponent } from './note-block';
|
||||
import { ACTIVE_NOTE_EXTRA_PADDING } from './note-edgeless-block.css';
|
||||
import * as styles from './note-edgeless-block.css';
|
||||
import { isPageBlock } from './utils';
|
||||
|
||||
export const AFFINE_EDGELESS_NOTE = 'affine-edgeless-note';
|
||||
|
||||
@@ -219,6 +219,9 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
? this._noteFullHeight < height
|
||||
: !!collapsedHeight && collapsedHeight < height;
|
||||
|
||||
const hasHeader = !!this.std.getOptional(NoteConfigExtension.identifier)
|
||||
?.edgelessNoteHeader;
|
||||
|
||||
return html`
|
||||
<div
|
||||
class=${styles.edgelessNoteContainer}
|
||||
@@ -257,10 +260,11 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
.model=${this.model}
|
||||
.host=${this.host}
|
||||
.zoom=${this.gfx.viewport.zoom ?? 1}
|
||||
.disableMask=${this.hideMask}
|
||||
.editing=${this._editing}
|
||||
></edgeless-note-mask>
|
||||
|
||||
${isCollapsable && !isPageBlock(this.std, this.model)
|
||||
${isCollapsable && (!this.model.isPageBlock() || !hasHeader)
|
||||
? html`<div
|
||||
class="${classMap({
|
||||
[styles.collapseButton]: true,
|
||||
@@ -294,6 +298,9 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
@state()
|
||||
private accessor _noteFullHeight = 0;
|
||||
|
||||
@state()
|
||||
accessor hideMask = false;
|
||||
|
||||
@query(`.${styles.clipContainer} > div`)
|
||||
private accessor _noteContent: HTMLElement | null = null;
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockStdScope } from '@blocksuite/block-std';
|
||||
|
||||
/**
|
||||
* We define a note block as a page block if it is the first visible note
|
||||
*/
|
||||
export function isPageBlock(std: BlockStdScope, note: NoteBlockModel) {
|
||||
return (
|
||||
std.get(FeatureFlagService).getFlag('enable_page_block') &&
|
||||
note.parent?.children.find(
|
||||
child =>
|
||||
matchModels(child, [NoteBlockModel]) &&
|
||||
child.displayMode !== NoteDisplayMode.EdgelessOnly
|
||||
) === note
|
||||
);
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
|
||||
@@ -245,7 +245,17 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
|
||||
|
||||
return html`
|
||||
${style}
|
||||
<div class="affine-paragraph-block-container">
|
||||
<style>
|
||||
.affine-paragraph-block-container[data-has-collapsed-siblings='false']
|
||||
affine-paragraph-heading-icon
|
||||
.heading-icon {
|
||||
transform: translateX(-48px);
|
||||
}
|
||||
</style>
|
||||
<div
|
||||
class="affine-paragraph-block-container"
|
||||
data-has-collapsed-siblings="${collapsedSiblings.length > 0}"
|
||||
>
|
||||
<div
|
||||
class=${classMap({
|
||||
'affine-paragraph-rich-text-wrapper': true,
|
||||
@@ -253,12 +263,16 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<
|
||||
[TOGGLE_BUTTON_PARENT_CLASS]: true,
|
||||
})}
|
||||
>
|
||||
${this.model.type$.value.startsWith('h') &&
|
||||
collapsedSiblings.length > 0
|
||||
${this.model.type$.value.startsWith('h')
|
||||
? html`
|
||||
<affine-paragraph-heading-icon
|
||||
.model=${this.model}
|
||||
></affine-paragraph-heading-icon>
|
||||
`
|
||||
: nothing}
|
||||
${this.model.type$.value.startsWith('h') &&
|
||||
collapsedSiblings.length > 0
|
||||
? html`
|
||||
<blocksuite-toggle-button
|
||||
.collapsed=${collapsed}
|
||||
.updateCollapsed=${(value: boolean) => {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"fractional-indexing": "^3.2.0",
|
||||
"lit": "^3.2.0",
|
||||
"lodash.chunk": "^4.2.0",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"fractional-indexing": "^3.2.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lit": "^3.2.0",
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash.chunk": "^4.2.9",
|
||||
"vitest": "3.0.5"
|
||||
"vitest": "3.0.6"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@vanilla-extract/css": "^1.14.0",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"lit": "^3.2.0",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.24.1"
|
||||
|
||||
@@ -43,6 +43,10 @@ export class SelectionController implements ReactiveController {
|
||||
private get clipboard() {
|
||||
return this.host.std.clipboard;
|
||||
}
|
||||
private get scale() {
|
||||
return this.host.getScale();
|
||||
}
|
||||
|
||||
widthAdjust(dragHandle: HTMLElement, event: MouseEvent) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@@ -50,6 +54,7 @@ export class SelectionController implements ReactiveController {
|
||||
const currentWidth =
|
||||
dragHandle.closest('td')?.getBoundingClientRect().width ??
|
||||
DefaultColumnWidth;
|
||||
const adjustedWidth = currentWidth / this.scale;
|
||||
const columnId = dragHandle.dataset['widthAdjustColumnId'];
|
||||
if (!columnId) {
|
||||
return;
|
||||
@@ -60,7 +65,7 @@ export class SelectionController implements ReactiveController {
|
||||
columnId,
|
||||
width: Math.max(
|
||||
ColumnMinWidth,
|
||||
event.clientX - initialX + currentWidth
|
||||
(event.clientX - initialX) / this.scale + adjustedWidth
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -68,6 +68,7 @@ export class SelectionLayer extends SignalWatcher(
|
||||
display: 'none',
|
||||
});
|
||||
const border = '2px solid var(--affine-primary-color)';
|
||||
|
||||
return styleMap({
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
|
||||
@@ -2,8 +2,7 @@ import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const tableContainer = style({
|
||||
display: 'block',
|
||||
backgroundColor: 'var(--affine-background-primary-color)',
|
||||
padding: '10px 0 18px',
|
||||
padding: '10px 0 18px 10px',
|
||||
overflowX: 'auto',
|
||||
overflowY: 'visible',
|
||||
selectors: {
|
||||
|
||||
@@ -54,6 +54,12 @@ export class TableBlockComponent extends CaptionedBlockComponent<TableBlockModel
|
||||
|
||||
table$ = signal<HTMLTableElement>();
|
||||
|
||||
public getScale(): number {
|
||||
const table = this.table$.value;
|
||||
if (!table) return 1;
|
||||
return table.getBoundingClientRect().width / table.offsetWidth;
|
||||
}
|
||||
|
||||
private readonly getRootRect = () => {
|
||||
const table = this.table$.value;
|
||||
if (!table) return;
|
||||
@@ -65,11 +71,12 @@ export class TableBlockComponent extends CaptionedBlockComponent<TableBlockModel
|
||||
const rootRect = this.getRootRect();
|
||||
if (!row || !rootRect) return;
|
||||
const rect = row.getBoundingClientRect();
|
||||
const scale = this.getScale();
|
||||
return {
|
||||
top: rect.top - rootRect.top,
|
||||
left: rect.left - rootRect.left,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
top: (rect.top - rootRect.top) / scale,
|
||||
left: (rect.left - rootRect.left) / scale,
|
||||
width: rect.width / scale,
|
||||
height: rect.height / scale,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -80,11 +87,12 @@ export class TableBlockComponent extends CaptionedBlockComponent<TableBlockModel
|
||||
const firstRect = columns.item(0)?.getBoundingClientRect();
|
||||
const lastRect = columns.item(columns.length - 1)?.getBoundingClientRect();
|
||||
if (!firstRect || !lastRect) return;
|
||||
const scale = this.getScale();
|
||||
return {
|
||||
top: firstRect.top - rootRect.top,
|
||||
left: firstRect.left - rootRect.left,
|
||||
width: firstRect.width,
|
||||
height: lastRect.bottom - firstRect.top,
|
||||
top: (firstRect.top - rootRect.top) / scale,
|
||||
left: (firstRect.left - rootRect.left) / scale,
|
||||
width: firstRect.width / scale,
|
||||
height: (lastRect.bottom - firstRect.top) / scale,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -99,19 +107,22 @@ export class TableBlockComponent extends CaptionedBlockComponent<TableBlockModel
|
||||
const startRow = rows.item(rowStartIndex);
|
||||
const endRow = rows.item(rowEndIndex);
|
||||
if (!startRow || !endRow || !rootRect) return;
|
||||
const columns = startRow.querySelectorAll('td');
|
||||
const startColumn = columns.item(columnStartIndex);
|
||||
const endColumn = columns.item(columnEndIndex);
|
||||
if (!startColumn || !endColumn) return;
|
||||
const startRect = startRow.getBoundingClientRect();
|
||||
const endRect = endRow.getBoundingClientRect();
|
||||
const startColumnRect = startColumn.getBoundingClientRect();
|
||||
const endColumnRect = endColumn.getBoundingClientRect();
|
||||
|
||||
const startCells = startRow.querySelectorAll('td');
|
||||
const endCells = endRow.querySelectorAll('td');
|
||||
const startCell = startCells.item(columnStartIndex);
|
||||
const endCell = endCells.item(columnEndIndex);
|
||||
if (!startCell || !endCell) return;
|
||||
|
||||
const startRect = startCell.getBoundingClientRect();
|
||||
const endRect = endCell.getBoundingClientRect();
|
||||
const scale = this.getScale();
|
||||
|
||||
return {
|
||||
top: startRect.top - rootRect.top,
|
||||
left: startColumnRect.left - rootRect.left,
|
||||
width: endColumnRect.right - startColumnRect.left,
|
||||
height: endRect.bottom - startRect.top,
|
||||
top: (startRect.top - rootRect.top) / scale,
|
||||
left: (startRect.left - rootRect.left) / scale,
|
||||
width: (endRect.right - startRect.left) / scale,
|
||||
height: (endRect.bottom - startRect.top) / scale,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -124,7 +135,7 @@ export class TableBlockComponent extends CaptionedBlockComponent<TableBlockModel
|
||||
contenteditable="false"
|
||||
class=${tableContainer}
|
||||
style=${styleMap({
|
||||
marginLeft: `-${virtualPadding}px`,
|
||||
marginLeft: `-${virtualPadding + 10}px`,
|
||||
marginRight: `-${virtualPadding}px`,
|
||||
position: 'relative',
|
||||
})}
|
||||
|
||||
@@ -4,7 +4,8 @@ import { createVar, style } from '@vanilla-extract/css';
|
||||
export const cellContainerStyle = style({
|
||||
position: 'relative',
|
||||
alignItems: 'center',
|
||||
border: '1px solid var(--affine-border-color)',
|
||||
border: '1px solid',
|
||||
borderColor: cssVarV2.table.border,
|
||||
borderCollapse: 'collapse',
|
||||
isolation: 'auto',
|
||||
textAlign: 'start',
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lottiefiles/dotlottie-wc": "^0.4.0",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"collapse-white-space": "^2.1.0",
|
||||
|
||||
+4
@@ -229,6 +229,10 @@ export class AffineLatexNode extends SignalWatcher(
|
||||
latex,
|
||||
}
|
||||
);
|
||||
this.editor.setInlineRange({
|
||||
index: this.endOffset,
|
||||
length: 0,
|
||||
});
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"date-fns": "^4.0.0",
|
||||
"lit": "^3.2.0",
|
||||
"yjs": "^13.6.21",
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-fragment-frame-panel",
|
||||
"description": "Frame panel fragment for BlockSuite.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test:unit": "nx vite:test --run --passWithNoTests",
|
||||
"test:unit:coverage": "nx vite:test --run --coverage",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [],
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-frame": "workspace:*",
|
||||
"@blocksuite/affine-block-surface": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.1",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.19.0"
|
||||
}
|
||||
+18
-19
@@ -1,12 +1,14 @@
|
||||
import { type EditorHost, ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { generateKeyBetweenV2 } from '@blocksuite/block-std/gfx';
|
||||
import { EdgelessFrameManager } from '@blocksuite/affine-block-frame';
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
DocModeProvider,
|
||||
EdgelessFrameManager,
|
||||
EdgelessRootService,
|
||||
EditPropsStore,
|
||||
type FrameBlockModel,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { type EditorHost, ShadowlessElement } from '@blocksuite/block-std';
|
||||
import {
|
||||
generateKeyBetweenV2,
|
||||
GfxControllerIdentifier,
|
||||
} from '@blocksuite/block-std/gfx';
|
||||
import {
|
||||
Bound,
|
||||
DisposableGroup,
|
||||
@@ -110,7 +112,7 @@ export class FramePanelBody extends SignalWatcher(
|
||||
}
|
||||
|
||||
this._selected = [];
|
||||
this._edgelessRootService?.selection.set({
|
||||
this._gfx.selection.set({
|
||||
elements: this._selected,
|
||||
editing: false,
|
||||
});
|
||||
@@ -126,6 +128,10 @@ export class FramePanelBody extends SignalWatcher(
|
||||
|
||||
private _lastEdgelessRootId = '';
|
||||
|
||||
private get _gfx() {
|
||||
return this.editorHost.std.get(GfxControllerIdentifier);
|
||||
}
|
||||
|
||||
private readonly _selectFrame = (e: SelectEvent) => {
|
||||
const { selected, id, multiselect } = e.detail;
|
||||
|
||||
@@ -138,7 +144,7 @@ export class FramePanelBody extends SignalWatcher(
|
||||
this._selected = [id];
|
||||
}
|
||||
|
||||
this._edgelessRootService?.selection.set({
|
||||
this._gfx.selection.set({
|
||||
elements: this._selected,
|
||||
editing: false,
|
||||
});
|
||||
@@ -152,10 +158,6 @@ export class FramePanelBody extends SignalWatcher(
|
||||
}));
|
||||
};
|
||||
|
||||
get _edgelessRootService() {
|
||||
return this.editorHost.std.getOptional(EdgelessRootService);
|
||||
}
|
||||
|
||||
get frames() {
|
||||
const frames = this.editorHost.doc
|
||||
.getBlocksByFlavour('affine:frame')
|
||||
@@ -229,8 +231,9 @@ export class FramePanelBody extends SignalWatcher(
|
||||
private _fitToElement(e: FitViewEvent) {
|
||||
const { block } = e.detail;
|
||||
const bound = Bound.deserialize(block.xywh);
|
||||
const docModeProvider = this.editorHost.std.get(DocModeProvider);
|
||||
|
||||
if (!this._edgelessRootService) {
|
||||
if (docModeProvider.getEditorMode() !== 'edgeless') {
|
||||
// When click frame card in page mode
|
||||
// Should switch to edgeless mode and set viewport to the frame
|
||||
const viewport = {
|
||||
@@ -242,11 +245,7 @@ export class FramePanelBody extends SignalWatcher(
|
||||
this.editorHost.std.get(EditPropsStore).setStorage('viewport', viewport);
|
||||
this.editorHost.std.get(DocModeProvider).setEditorMode('edgeless');
|
||||
} else {
|
||||
this._edgelessRootService.viewport.setViewportByBound(
|
||||
bound,
|
||||
this.viewportPadding,
|
||||
true
|
||||
);
|
||||
this._gfx.viewport.setViewportByBound(bound, this.viewportPadding, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +397,7 @@ export class FramePanelBody extends SignalWatcher(
|
||||
this._setDocDisposables(this.editorHost.doc);
|
||||
// after switch to edgeless mode, should update the selection
|
||||
if (this.editorHost.doc.id === this._lastEdgelessRootId) {
|
||||
this._edgelessRootService?.selection.set({
|
||||
this._gfx.selection.set({
|
||||
elements: this._selected,
|
||||
editing: false,
|
||||
});
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
import type { RichText } from '@blocksuite/affine-components/rich-text';
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import type { FrameBlockModel, RichText } from '@blocksuite/blocks';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import type { FrameBlockModel } from '@blocksuite/blocks';
|
||||
import { DisposableGroup, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, type PropertyValues } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { on, once } from '@blocksuite/affine-shared/utils';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { type FrameBlockModel, on, once } from '@blocksuite/blocks';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
AFFINE_FRAME_PANEL_BODY,
|
||||
FramePanelBody,
|
||||
} from './body/frame-panel-body';
|
||||
import { AFFINE_FRAME_CARD, FrameCard } from './card/frame-card';
|
||||
import {
|
||||
AFFINE_FRAME_CARD_TITLE,
|
||||
FrameCardTitle,
|
||||
} from './card/frame-card-title';
|
||||
import {
|
||||
AFFINE_FRAME_TITLE_EDITOR,
|
||||
FrameCardTitleEditor,
|
||||
} from './card/frame-card-title-editor';
|
||||
import { AFFINE_FRAME_PANEL, FramePanel } from './frame-panel';
|
||||
import {
|
||||
AFFINE_FRAME_PANEL_HEADER,
|
||||
FramePanelHeader,
|
||||
} from './header/frame-panel-header';
|
||||
import {
|
||||
AFFINE_FRAMES_SETTING_MENU,
|
||||
FramesSettingMenu,
|
||||
} from './header/frames-setting-menu';
|
||||
|
||||
export function effects() {
|
||||
customElements.define(AFFINE_FRAME_PANEL, FramePanel);
|
||||
customElements.define(AFFINE_FRAME_TITLE_EDITOR, FrameCardTitleEditor);
|
||||
customElements.define(AFFINE_FRAME_CARD, FrameCard);
|
||||
customElements.define(AFFINE_FRAME_CARD_TITLE, FrameCardTitle);
|
||||
customElements.define(AFFINE_FRAME_PANEL_BODY, FramePanelBody);
|
||||
customElements.define(AFFINE_FRAME_PANEL_HEADER, FramePanelHeader);
|
||||
customElements.define(AFFINE_FRAMES_SETTING_MENU, FramesSettingMenu);
|
||||
}
|
||||
+15
-15
@@ -1,12 +1,12 @@
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import type { NavigatorMode } from '@blocksuite/affine-block-frame';
|
||||
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
createButtonPopper,
|
||||
DocModeProvider,
|
||||
EdgelessLegacySlotIdentifier,
|
||||
EdgelessRootService,
|
||||
EditPropsStore,
|
||||
type NavigatorMode,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { createButtonPopper } from '@blocksuite/affine-shared/utils';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { DisposableGroup, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { PresentationIcon, SettingsIcon } from '@blocksuite/icons/lit';
|
||||
import { css, html, LitElement, type PropertyValues } from 'lit';
|
||||
@@ -112,13 +112,18 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
|
||||
|
||||
private _edgelessDisposables: DisposableGroup | null = null;
|
||||
|
||||
private get _gfx() {
|
||||
return this.editorHost.std.get(GfxControllerIdentifier);
|
||||
}
|
||||
|
||||
private readonly _enterPresentationMode = () => {
|
||||
if (!this._edgelessRootService) {
|
||||
const docModeProvider = this.editorHost.std.get(DocModeProvider);
|
||||
if (docModeProvider.getEditorMode() !== 'edgeless') {
|
||||
this.editorHost.std.get(DocModeProvider).setEditorMode('edgeless');
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this._edgelessRootService?.gfx.tool.setTool({
|
||||
this._gfx.tool.setTool({
|
||||
type: 'frameNavigator',
|
||||
mode: this._navigatorMode,
|
||||
});
|
||||
@@ -132,8 +137,6 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
|
||||
private _navigatorMode: NavigatorMode = 'fit';
|
||||
|
||||
private readonly _setEdgelessDisposables = () => {
|
||||
if (!this._edgelessRootService) return;
|
||||
|
||||
const slots = this.editorHost.std.get(EdgelessLegacySlotIdentifier);
|
||||
|
||||
this._clearEdgelessDisposables();
|
||||
@@ -145,10 +148,6 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
|
||||
);
|
||||
};
|
||||
|
||||
private get _edgelessRootService() {
|
||||
return this.editorHost.std.getOptional(EdgelessRootService);
|
||||
}
|
||||
|
||||
private _tryLoadNavigatorStateLocalRecord() {
|
||||
this._navigatorMode = this.editorHost.std
|
||||
.get(EditPropsStore)
|
||||
@@ -219,7 +218,8 @@ export class FramePanelHeader extends WithDisposable(LitElement) {
|
||||
|
||||
override updated(_changedProperties: PropertyValues) {
|
||||
if (_changedProperties.has('editorHost')) {
|
||||
if (this._edgelessRootService) {
|
||||
const docModeProvider = this.editorHost.std.get(DocModeProvider);
|
||||
if (docModeProvider.getEditorMode() === 'edgeless') {
|
||||
this._setEdgelessDisposables();
|
||||
} else {
|
||||
this._clearEdgelessDisposables();
|
||||
+6
-9
@@ -1,9 +1,9 @@
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
EdgelessLegacySlotIdentifier,
|
||||
EdgelessRootService,
|
||||
DocModeProvider,
|
||||
EditPropsStore,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, LitElement, type PropertyValues } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
@@ -103,10 +103,6 @@ export class FramesSettingMenu extends WithDisposable(LitElement) {
|
||||
this._editPropsStore.setStorage('presentHideToolbar', this.hideToolbar);
|
||||
};
|
||||
|
||||
private get _edgelessRootService() {
|
||||
return this.editorHost.std.getOptional(EdgelessRootService);
|
||||
}
|
||||
|
||||
private get _editPropsStore() {
|
||||
return this.editorHost.std.get(EditPropsStore);
|
||||
}
|
||||
@@ -176,7 +172,8 @@ export class FramesSettingMenu extends WithDisposable(LitElement) {
|
||||
|
||||
override updated(_changedProperties: PropertyValues) {
|
||||
if (_changedProperties.has('editorHost')) {
|
||||
if (this._edgelessRootService) {
|
||||
const docModeProvider = this.editorHost.std.get(DocModeProvider);
|
||||
if (docModeProvider.getEditorMode() === 'edgeless') {
|
||||
this.disposables.add(
|
||||
this.slots.navigatorSettingUpdated.on(
|
||||
({ blackBackground, hideToolbar }) => {
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './frame-panel';
|
||||
export * from './tool';
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import type { NavigatorMode } from '@blocksuite/affine-block-frame';
|
||||
import { BaseTool } from '@blocksuite/block-std/gfx';
|
||||
|
||||
import type { NavigatorMode } from '../../../_common/edgeless/frame/consts.js';
|
||||
|
||||
type PresentToolOption = {
|
||||
mode?: NavigatorMode;
|
||||
};
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import { type FrameBlockModel, on, once } from '@blocksuite/blocks';
|
||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||
import { on, once } from '@blocksuite/affine-shared/utils';
|
||||
|
||||
import type { FramePanelBody } from '../body/frame-panel-body.js';
|
||||
import { FrameCard } from '../card/frame-card.js';
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-frame" },
|
||||
{ "path": "../block-surface" },
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-fragment-outline",
|
||||
"description": "Outline fragment for BlockSuite.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test:unit": "nx vite:test --run --passWithNoTests",
|
||||
"test:unit:coverage": "nx vite:test --run --coverage",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [],
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-note": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.1",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.19.0"
|
||||
}
|
||||
+17
-32
@@ -1,17 +1,10 @@
|
||||
import { effects } from '@blocksuite/affine-block-note/effects';
|
||||
import { changeNoteDisplayMode } from '@blocksuite/affine-block-note';
|
||||
import { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import { ShadowlessElement, SurfaceSelection } from '@blocksuite/block-std';
|
||||
import {
|
||||
changeNoteDisplayMode,
|
||||
matchModels,
|
||||
NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
} from '@blocksuite/blocks';
|
||||
import {
|
||||
Bound,
|
||||
noop,
|
||||
SignalWatcher,
|
||||
WithDisposable,
|
||||
} from '@blocksuite/global/utils';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { Bound, SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import { consume } from '@lit/context';
|
||||
import { effect, signal } from '@preact/signals-core';
|
||||
@@ -21,8 +14,6 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
||||
noop(effects);
|
||||
|
||||
import { type TocContext, tocContext } from '../config';
|
||||
import type {
|
||||
ClickBlockEvent,
|
||||
@@ -78,10 +69,6 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
return this.editor.doc;
|
||||
}
|
||||
|
||||
private get edgeless() {
|
||||
return this.editor.querySelector('affine-edgeless-root');
|
||||
}
|
||||
|
||||
get viewportPadding(): [number, number, number, number] {
|
||||
const fitPadding = this._context.fitPadding$.value;
|
||||
return fitPadding.length === 4
|
||||
@@ -92,9 +79,9 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
}
|
||||
|
||||
private _deSelectNoteInEdgelessMode(note: NoteBlockModel) {
|
||||
if (!this.edgeless) return;
|
||||
const gfx = this.editor.std.get(GfxControllerIdentifier);
|
||||
const selection = gfx.selection;
|
||||
|
||||
const { selection } = this.edgeless.service;
|
||||
if (!selection.has(note.id)) return;
|
||||
const selectedIds = selection.selectedIds.filter(id => id !== note.id);
|
||||
selection.set({
|
||||
@@ -115,18 +102,12 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
}
|
||||
|
||||
private _fitToElement(e: FitViewEvent) {
|
||||
const edgeless = this.edgeless;
|
||||
|
||||
if (!edgeless) return;
|
||||
const gfx = this.editor.std.get(GfxControllerIdentifier);
|
||||
|
||||
const { block } = e.detail;
|
||||
const bound = Bound.deserialize(block.xywh);
|
||||
|
||||
edgeless.service.viewport.setViewportByBound(
|
||||
bound,
|
||||
this.viewportPadding,
|
||||
true
|
||||
);
|
||||
gfx.viewport.setViewportByBound(bound, this.viewportPadding, true);
|
||||
}
|
||||
|
||||
// when display mode change to page only, we should de-select the note if it is selected in edgeless mode
|
||||
@@ -199,6 +180,8 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
|
||||
private _selectNote(e: SelectEvent) {
|
||||
const { selected, id, multiselect } = e.detail;
|
||||
const gfx = this.editor.std.get(GfxControllerIdentifier);
|
||||
const editorMode = this.editor.std.get(DocModeProvider).getEditorMode();
|
||||
const note = this.doc.getBlock(id)?.model;
|
||||
if (!note || !matchModels(note, [NoteBlockModel])) return;
|
||||
|
||||
@@ -212,8 +195,8 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
selectedNotes = [note];
|
||||
}
|
||||
|
||||
if (this.edgeless) {
|
||||
this.edgeless?.service.selection.set({
|
||||
if (editorMode === 'edgeless') {
|
||||
gfx.selection.set({
|
||||
elements: selectedNotes.map(({ id }) => id),
|
||||
editing: false,
|
||||
});
|
||||
@@ -224,7 +207,9 @@ export class OutlinePanelBody extends SignalWatcher(
|
||||
|
||||
private _watchSelectedNotes() {
|
||||
return effect(() => {
|
||||
const { std, doc, mode } = this.editor;
|
||||
const { std, doc } = this.editor;
|
||||
const docModeService = this.editor.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
if (mode !== 'edgeless') return;
|
||||
|
||||
const currSelectedNotes = std.selection
|
||||
+2
-5
@@ -1,9 +1,6 @@
|
||||
import { type NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { createButtonPopper } from '@blocksuite/affine-shared/utils';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import {
|
||||
createButtonPopper,
|
||||
type NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
} from '@blocksuite/blocks';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { ArrowDownSmallIcon, InvisibleIcon } from '@blocksuite/icons/lit';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
+11
-11
@@ -1,16 +1,16 @@
|
||||
import type {
|
||||
AttachmentBlockModel,
|
||||
BookmarkBlockModel,
|
||||
CodeBlockModel,
|
||||
DatabaseBlockModel,
|
||||
ImageBlockModel,
|
||||
ListBlockModel,
|
||||
ParagraphBlockModel,
|
||||
RootBlockModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { DocDisplayMetaProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import {
|
||||
type AttachmentBlockModel,
|
||||
type BookmarkBlockModel,
|
||||
type CodeBlockModel,
|
||||
type DatabaseBlockModel,
|
||||
DocDisplayMetaProvider,
|
||||
type ImageBlockModel,
|
||||
type ListBlockModel,
|
||||
type ParagraphBlockModel,
|
||||
type RootBlockModel,
|
||||
} from '@blocksuite/blocks';
|
||||
import { noop, SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { LinkedPageIcon } from '@blocksuite/icons/lit';
|
||||
import type { DeltaInsert } from '@blocksuite/inline';
|
||||
+4
-4
@@ -1,4 +1,5 @@
|
||||
import type { ParagraphBlockModel, Signal } from '@blocksuite/blocks';
|
||||
import type { ParagraphBlockModel } from '@blocksuite/affine-model';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import {
|
||||
AttachmentIcon,
|
||||
BlockIcon,
|
||||
@@ -20,10 +21,9 @@ import {
|
||||
TextIcon,
|
||||
} from '@blocksuite/icons/lit';
|
||||
import { createContext } from '@lit/context';
|
||||
import type { Signal } from '@preact/signals-core';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
import type { AffineEditorContainer } from '../../editors/editor-container.js';
|
||||
|
||||
const _16px = { width: '16px', height: '16px' };
|
||||
|
||||
const paragraphIconMap: Record<
|
||||
@@ -85,7 +85,7 @@ export const headingKeys = new Set(
|
||||
export const outlineSettingsKey = 'outlinePanelSettings';
|
||||
|
||||
export type TocContext = {
|
||||
editor$: Signal<AffineEditorContainer>;
|
||||
editor$: Signal<EditorHost>;
|
||||
enableSorting$: Signal<boolean>;
|
||||
showIcons$: Signal<boolean>;
|
||||
fitPadding$: Signal<number[]>;
|
||||
@@ -0,0 +1,39 @@
|
||||
import { AFFINE_OUTLINE_NOTICE, OutlineNotice } from './body/outline-notice';
|
||||
import {
|
||||
AFFINE_OUTLINE_PANEL_BODY,
|
||||
OutlinePanelBody,
|
||||
} from './body/outline-panel-body';
|
||||
import { AFFINE_OUTLINE_NOTE_CARD, OutlineNoteCard } from './card/outline-card';
|
||||
import {
|
||||
AFFINE_OUTLINE_BLOCK_PREVIEW,
|
||||
OutlineBlockPreview,
|
||||
} from './card/outline-preview';
|
||||
import {
|
||||
AFFINE_OUTLINE_PANEL_HEADER,
|
||||
OutlinePanelHeader,
|
||||
} from './header/outline-panel-header';
|
||||
import {
|
||||
AFFINE_OUTLINE_NOTE_PREVIEW_SETTING_MENU,
|
||||
OutlineNotePreviewSettingMenu,
|
||||
} from './header/outline-setting-menu';
|
||||
import {
|
||||
AFFINE_MOBILE_OUTLINE_MENU,
|
||||
MobileOutlineMenu,
|
||||
} from './mobile-outline-panel';
|
||||
import { AFFINE_OUTLINE_PANEL, OutlinePanel } from './outline-panel';
|
||||
import { AFFINE_OUTLINE_VIEWER, OutlineViewer } from './outline-viewer';
|
||||
|
||||
export function effects() {
|
||||
customElements.define(
|
||||
AFFINE_OUTLINE_NOTE_PREVIEW_SETTING_MENU,
|
||||
OutlineNotePreviewSettingMenu
|
||||
);
|
||||
customElements.define(AFFINE_OUTLINE_NOTICE, OutlineNotice);
|
||||
customElements.define(AFFINE_OUTLINE_PANEL, OutlinePanel);
|
||||
customElements.define(AFFINE_OUTLINE_PANEL_HEADER, OutlinePanelHeader);
|
||||
customElements.define(AFFINE_OUTLINE_NOTE_CARD, OutlineNoteCard);
|
||||
customElements.define(AFFINE_OUTLINE_VIEWER, OutlineViewer);
|
||||
customElements.define(AFFINE_MOBILE_OUTLINE_MENU, MobileOutlineMenu);
|
||||
customElements.define(AFFINE_OUTLINE_BLOCK_PREVIEW, OutlineBlockPreview);
|
||||
customElements.define(AFFINE_OUTLINE_PANEL_BODY, OutlinePanelBody);
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { createButtonPopper } from '@blocksuite/affine-shared/utils';
|
||||
import { ShadowlessElement } from '@blocksuite/block-std';
|
||||
import { createButtonPopper } from '@blocksuite/blocks';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { SettingsIcon, SortIcon } from '@blocksuite/icons/lit';
|
||||
import { consume } from '@lit/context';
|
||||
+13
-8
@@ -1,11 +1,16 @@
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import { PropTypes, requiredProperties } from '@blocksuite/block-std';
|
||||
import {
|
||||
matchModels,
|
||||
NoteDisplayMode,
|
||||
ParagraphBlockModel,
|
||||
RootBlockModel,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
type EditorHost,
|
||||
PropTypes,
|
||||
requiredProperties,
|
||||
} from '@blocksuite/block-std';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import { signal } from '@preact/signals-core';
|
||||
@@ -14,7 +19,6 @@ import { property } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import type { AffineEditorContainer } from '../../editors/editor-container.js';
|
||||
import { getHeadingBlocksFromDoc } from './utils/query.js';
|
||||
import {
|
||||
observeActiveHeadingDuringScroll,
|
||||
@@ -162,8 +166,9 @@ export class MobileOutlineMenu extends SignalWatcher(
|
||||
};
|
||||
|
||||
override render() {
|
||||
if (this.editor.doc.root === null || this.editor.mode === 'edgeless')
|
||||
return nothing;
|
||||
const docModeService = this.editor.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
if (this.editor.doc.root === null || mode === 'edgeless') return nothing;
|
||||
|
||||
const headingBlocks = getHeadingBlocksFromDoc(
|
||||
this.editor.doc,
|
||||
@@ -182,7 +187,7 @@ export class MobileOutlineMenu extends SignalWatcher(
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editor!: AffineEditorContainer;
|
||||
accessor editor!: EditorHost;
|
||||
}
|
||||
|
||||
declare global {
|
||||
+13
-5
@@ -1,4 +1,6 @@
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
type EditorHost,
|
||||
PropTypes,
|
||||
requiredProperties,
|
||||
ShadowlessElement,
|
||||
@@ -9,7 +11,6 @@ import { effect, signal } from '@preact/signals-core';
|
||||
import { html, type PropertyValues } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import type { AffineEditorContainer } from '../../editors/editor-container.js';
|
||||
import { outlineSettingsKey, type TocContext, tocContext } from './config.js';
|
||||
import * as styles from './outline-panel.css';
|
||||
|
||||
@@ -21,6 +22,12 @@ export const AFFINE_OUTLINE_PANEL = 'affine-outline-panel';
|
||||
export class OutlinePanel extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
) {
|
||||
private _getEditorMode(host: EditorHost) {
|
||||
const docModeService = host.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
return mode;
|
||||
}
|
||||
|
||||
private _setContext() {
|
||||
this._context = {
|
||||
editor$: signal(this.editor),
|
||||
@@ -39,7 +46,7 @@ export class OutlinePanel extends SignalWatcher(
|
||||
}
|
||||
|
||||
const editor = this._context.editor$.value;
|
||||
if (editor.mode === 'edgeless') {
|
||||
if (this._getEditorMode(editor) === 'edgeless') {
|
||||
this._context.enableSorting$.value = true;
|
||||
} else if (settings) {
|
||||
this._context.enableSorting$.value = settings.enableSorting;
|
||||
@@ -51,7 +58,8 @@ export class OutlinePanel extends SignalWatcher(
|
||||
private _watchSettingsChange() {
|
||||
this.disposables.add(
|
||||
effect(() => {
|
||||
if (this._context.editor$.value.mode === 'edgeless') return;
|
||||
if (this._getEditorMode(this._context.editor$.value) === 'edgeless')
|
||||
return;
|
||||
|
||||
const showPreviewIcon = this._context.showIcons$.value;
|
||||
const enableNotesSorting = this._context.enableSorting$.value;
|
||||
@@ -84,7 +92,7 @@ export class OutlinePanel extends SignalWatcher(
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this.editor.host) return;
|
||||
if (!this.editor) return;
|
||||
|
||||
return html`
|
||||
<affine-outline-panel-header></affine-outline-panel-header>
|
||||
@@ -97,7 +105,7 @@ export class OutlinePanel extends SignalWatcher(
|
||||
private accessor _context!: TocContext;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editor!: AffineEditorContainer;
|
||||
accessor editor!: EditorHost;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor fitPadding!: number[];
|
||||
+8
-5
@@ -1,9 +1,12 @@
|
||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { scrollbarStyle } from '@blocksuite/affine-shared/styles';
|
||||
import {
|
||||
type EditorHost,
|
||||
PropTypes,
|
||||
requiredProperties,
|
||||
ShadowlessElement,
|
||||
} from '@blocksuite/block-std';
|
||||
import { NoteDisplayMode, scrollbarStyle } from '@blocksuite/blocks';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { TocIcon } from '@blocksuite/icons/lit';
|
||||
import { provide } from '@lit/context';
|
||||
@@ -13,7 +16,6 @@ import { property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import type { AffineEditorContainer } from '../../editors/editor-container.js';
|
||||
import { type TocContext, tocContext } from './config.js';
|
||||
import { getHeadingBlocksFromDoc } from './utils/query.js';
|
||||
import {
|
||||
@@ -219,8 +221,9 @@ export class OutlineViewer extends SignalWatcher(
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this.editor.doc.root === null || this.editor.mode === 'edgeless')
|
||||
return nothing;
|
||||
const docModeService = this.editor.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
if (this.editor.doc.root === null || mode === 'edgeless') return nothing;
|
||||
|
||||
const headingBlocks = getHeadingBlocksFromDoc(
|
||||
this.editor.doc,
|
||||
@@ -308,7 +311,7 @@ export class OutlineViewer extends SignalWatcher(
|
||||
private accessor _showViewer: boolean = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor editor!: AffineEditorContainer;
|
||||
accessor editor!: EditorHost;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor toggleOutlinePanel: (() => void) | null = null;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { NoteBlockModel, NoteDisplayMode } from '@blocksuite/blocks';
|
||||
import type { NoteBlockModel, NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
|
||||
export type ReorderEvent = CustomEvent<{
|
||||
currentNumber: number;
|
||||
+4
-5
@@ -1,11 +1,10 @@
|
||||
import {
|
||||
BlocksUtils,
|
||||
matchModels,
|
||||
NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
ParagraphBlockModel,
|
||||
RootBlockModel,
|
||||
} from '@blocksuite/blocks';
|
||||
} from '@blocksuite/affine-model';
|
||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockModel, Store } from '@blocksuite/store';
|
||||
|
||||
import { headingKeys } from '../config.js';
|
||||
@@ -35,14 +34,14 @@ export function getNotesFromDoc(
|
||||
}
|
||||
|
||||
export function isRootBlock(block: BlockModel): block is RootBlockModel {
|
||||
return BlocksUtils.matchModels(block, [RootBlockModel]);
|
||||
return matchModels(block, [RootBlockModel]);
|
||||
}
|
||||
|
||||
export function isHeadingBlock(
|
||||
block: BlockModel
|
||||
): block is ParagraphBlockModel {
|
||||
return (
|
||||
BlocksUtils.matchModels(block, [ParagraphBlockModel]) &&
|
||||
matchModels(block, [ParagraphBlockModel]) &&
|
||||
headingKeys.has(block.type$.value)
|
||||
);
|
||||
}
|
||||
+21
-22
@@ -1,16 +1,18 @@
|
||||
import { getDocTitleByEditorHost } from '@blocksuite/affine-components/doc-title';
|
||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { Viewport } from '@blocksuite/affine-shared/types';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { NoteDisplayMode } from '@blocksuite/blocks';
|
||||
import { clamp, DisposableGroup } from '@blocksuite/global/utils';
|
||||
|
||||
import type { AffineEditorContainer } from '../../../editors/editor-container.js';
|
||||
import { getDocTitleByEditorHost } from '../../doc-title/index.js';
|
||||
import { getHeadingBlocksFromDoc } from './query.js';
|
||||
|
||||
export function scrollToBlock(editor: AffineEditorContainer, blockId: string) {
|
||||
const { host, mode } = editor;
|
||||
if (mode === 'edgeless' || !host) return;
|
||||
export function scrollToBlock(host: EditorHost, blockId: string) {
|
||||
const docModeService = host.std.get(DocModeProvider);
|
||||
const mode = docModeService.getEditorMode();
|
||||
if (mode === 'edgeless') return;
|
||||
|
||||
if (editor.doc.root?.id === blockId) {
|
||||
if (host.doc.root?.id === blockId) {
|
||||
const docTitle = getDocTitleByEditorHost(host);
|
||||
if (!docTitle) return;
|
||||
|
||||
@@ -50,12 +52,11 @@ export function isBlockBeforeViewportCenter(
|
||||
}
|
||||
|
||||
export const observeActiveHeadingDuringScroll = (
|
||||
getEditor: () => AffineEditorContainer, // workaround for editor changed
|
||||
getEditor: () => EditorHost, // workaround for editor changed
|
||||
update: (activeHeading: string | null) => void
|
||||
) => {
|
||||
const handler = () => {
|
||||
const { host } = getEditor();
|
||||
if (!host) return;
|
||||
const host = getEditor();
|
||||
|
||||
const headings = getHeadingBlocksFromDoc(
|
||||
host.doc,
|
||||
@@ -82,15 +83,14 @@ export const observeActiveHeadingDuringScroll = (
|
||||
let highlightMask: HTMLDivElement | null = null;
|
||||
let highlightTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function highlightBlock(editor: AffineEditorContainer, blockId: string) {
|
||||
function highlightBlock(host: EditorHost, blockId: string) {
|
||||
const emptyClear = () => {};
|
||||
|
||||
const { host } = editor;
|
||||
if (!host) return emptyClear;
|
||||
if (host.doc.root?.id === blockId) return emptyClear;
|
||||
|
||||
if (editor.doc.root?.id === blockId) return emptyClear;
|
||||
|
||||
const rootComponent = host.querySelector('affine-page-root');
|
||||
const rootComponent = host.querySelector<
|
||||
HTMLElement & { viewport: Viewport }
|
||||
>('affine-page-root');
|
||||
if (!rootComponent) return emptyClear;
|
||||
|
||||
if (!rootComponent.viewport) {
|
||||
@@ -153,11 +153,11 @@ function highlightBlock(editor: AffineEditorContainer, blockId: string) {
|
||||
// this function is useful when the scroll need smooth animation
|
||||
let highlightIntervalId: ReturnType<typeof setInterval> | null = null;
|
||||
export async function scrollToBlockWithHighlight(
|
||||
editor: AffineEditorContainer,
|
||||
host: EditorHost,
|
||||
blockId: string,
|
||||
timeout = 3000
|
||||
) {
|
||||
scrollToBlock(editor, blockId);
|
||||
scrollToBlock(host, blockId);
|
||||
|
||||
let timeCount = 0;
|
||||
|
||||
@@ -174,10 +174,9 @@ export async function scrollToBlockWithHighlight(
|
||||
return;
|
||||
}
|
||||
|
||||
const { host } = editor;
|
||||
const block = host?.view.getBlock(blockId);
|
||||
const block = host.view.getBlock(blockId);
|
||||
|
||||
if (!host || !block || timeCount > timeout) {
|
||||
if (!block || timeCount > timeout) {
|
||||
clearInterval(highlightIntervalId);
|
||||
resolve(() => {});
|
||||
return;
|
||||
@@ -195,7 +194,7 @@ export async function scrollToBlockWithHighlight(
|
||||
clearInterval(highlightIntervalId);
|
||||
|
||||
// highlight block
|
||||
resolve(highlightBlock(editor, blockId));
|
||||
resolve(highlightBlock(host, blockId));
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-note" },
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" }
|
||||
]
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@toeverything/theme": "^1.1.7",
|
||||
"@toeverything/theme": "^1.1.11",
|
||||
"fractional-indexing": "^3.2.0",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.23.8"
|
||||
|
||||
@@ -154,4 +154,17 @@ export class NoteBlockModel
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We define a note block as a page block if it is the first visible note
|
||||
*/
|
||||
isPageBlock() {
|
||||
return (
|
||||
this.parent?.children.find(
|
||||
child =>
|
||||
child instanceof NoteBlockModel &&
|
||||
child.displayMode !== NoteDisplayMode.EdgelessOnly
|
||||
) === this
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user