mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 10:31:50 +08:00
refactor(editor): unify directories naming (#11516)
**Directory Structure Changes** - Renamed multiple block-related directories by removing the "block-" prefix: - `block-attachment` → `attachment` - `block-bookmark` → `bookmark` - `block-callout` → `callout` - `block-code` → `code` - `block-data-view` → `data-view` - `block-database` → `database` - `block-divider` → `divider` - `block-edgeless-text` → `edgeless-text` - `block-embed` → `embed`
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
import {
|
||||
cleanSpecifiedTail,
|
||||
getTextContentFromInlineRange,
|
||||
} from '@blocksuite/affine-rich-text';
|
||||
import { VirtualKeyboardProvider } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
createKeydownObserver,
|
||||
getViewportElement,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
|
||||
import { MoreHorizontalIcon } from '@blocksuite/icons/lit';
|
||||
import { PropTypes, requiredProperties } from '@blocksuite/std';
|
||||
import { signal } from '@preact/signals-core';
|
||||
import { html, LitElement, nothing } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { join } from 'lit/directives/join.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import { PageRootBlockComponent } from '../../index.js';
|
||||
import type {
|
||||
LinkedDocContext,
|
||||
LinkedMenuGroup,
|
||||
LinkedMenuItem,
|
||||
} from './config.js';
|
||||
import { mobileLinkedDocMenuStyles } from './styles.js';
|
||||
import { resolveSignal } from './utils.js';
|
||||
|
||||
export const AFFINE_MOBILE_LINKED_DOC_MENU = 'affine-mobile-linked-doc-menu';
|
||||
|
||||
@requiredProperties({
|
||||
context: PropTypes.object,
|
||||
rootComponent: PropTypes.instanceOf(PageRootBlockComponent),
|
||||
})
|
||||
export class AffineMobileLinkedDocMenu extends SignalWatcher(
|
||||
WithDisposable(LitElement)
|
||||
) {
|
||||
static override styles = mobileLinkedDocMenuStyles;
|
||||
|
||||
private readonly _expand = new Set<string>();
|
||||
|
||||
private _firstActionItem: LinkedMenuItem | null = null;
|
||||
|
||||
private readonly _linkedDocGroup$ = signal<LinkedMenuGroup[]>([]);
|
||||
|
||||
private readonly _renderGroup = (group: LinkedMenuGroup) => {
|
||||
let items = resolveSignal(group.items);
|
||||
|
||||
const isOverflow = !!group.maxDisplay && items.length > group.maxDisplay;
|
||||
const expanded = this._expand.has(group.name);
|
||||
|
||||
let moreItem = null;
|
||||
if (!expanded && isOverflow) {
|
||||
items = items.slice(0, group.maxDisplay);
|
||||
|
||||
moreItem = html`<div
|
||||
class="mobile-linked-doc-menu-item"
|
||||
@click=${() => {
|
||||
this._expand.add(group.name);
|
||||
this.requestUpdate();
|
||||
}}
|
||||
>
|
||||
${MoreHorizontalIcon()}
|
||||
<div class="text">${group.overflowText || 'more'}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
${repeat(items, item => item.key, this._renderItem)} ${moreItem}
|
||||
`;
|
||||
};
|
||||
|
||||
private readonly _renderItem = ({
|
||||
key,
|
||||
name,
|
||||
icon,
|
||||
action,
|
||||
}: LinkedMenuItem) => {
|
||||
return html`<button
|
||||
class="mobile-linked-doc-menu-item"
|
||||
data-id=${key}
|
||||
@pointerup=${() => {
|
||||
action()?.catch(console.error);
|
||||
}}
|
||||
>
|
||||
${icon}
|
||||
<div class="text">${name}</div>
|
||||
</button>`;
|
||||
};
|
||||
|
||||
private readonly _scrollInputToTop = () => {
|
||||
const { inlineEditor } = this.context;
|
||||
const { scrollContainer, scrollTopOffset } = this.context.config.mobile;
|
||||
|
||||
let container = null;
|
||||
let containerScrollTop = 0;
|
||||
if (typeof scrollContainer === 'string') {
|
||||
container = document.querySelector(scrollContainer);
|
||||
containerScrollTop = container?.scrollTop ?? 0;
|
||||
} else if (scrollContainer instanceof HTMLElement) {
|
||||
container = scrollContainer;
|
||||
containerScrollTop = scrollContainer.scrollTop;
|
||||
} else if (scrollContainer === window) {
|
||||
container = window;
|
||||
containerScrollTop = scrollContainer.scrollY;
|
||||
} else {
|
||||
container = getViewportElement(this.context.std.host);
|
||||
containerScrollTop = container?.scrollTop ?? 0;
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
if (typeof scrollTopOffset === 'function') {
|
||||
offset = scrollTopOffset();
|
||||
} else {
|
||||
offset = scrollTopOffset ?? 0;
|
||||
}
|
||||
|
||||
if (!inlineEditor.rootElement || !container) return;
|
||||
container.scrollTo({
|
||||
top:
|
||||
inlineEditor.rootElement.getBoundingClientRect().top +
|
||||
containerScrollTop -
|
||||
offset,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
};
|
||||
|
||||
private readonly _updateLinkedDocGroup = async () => {
|
||||
if (this._updateLinkedDocGroupAbortController) {
|
||||
this._updateLinkedDocGroupAbortController.abort();
|
||||
}
|
||||
this._updateLinkedDocGroupAbortController = new AbortController();
|
||||
this._linkedDocGroup$.value = await this.context.config.getMenus(
|
||||
this._query ?? '',
|
||||
() => {
|
||||
this.context.close();
|
||||
cleanSpecifiedTail(
|
||||
this.context.std,
|
||||
this.context.inlineEditor,
|
||||
this.context.triggerKey + (this._query ?? '')
|
||||
);
|
||||
},
|
||||
this.context.std.host,
|
||||
this.context.inlineEditor,
|
||||
this._updateLinkedDocGroupAbortController.signal
|
||||
);
|
||||
};
|
||||
|
||||
private _updateLinkedDocGroupAbortController: AbortController | null = null;
|
||||
|
||||
private get _query() {
|
||||
return getTextContentFromInlineRange(
|
||||
this.context.inlineEditor,
|
||||
this.context.startRange
|
||||
);
|
||||
}
|
||||
|
||||
get keyboard() {
|
||||
return this.context.std.get(VirtualKeyboardProvider);
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
const { inlineEditor, close } = this.context;
|
||||
|
||||
this._updateLinkedDocGroup().catch(console.error);
|
||||
|
||||
// prevent editor blur when click menu
|
||||
this._disposables.addFromEvent(this, 'pointerdown', e => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// close menu when click outside
|
||||
this.disposables.addFromEvent(
|
||||
window,
|
||||
'pointerdown',
|
||||
e => {
|
||||
if (e.target === this) return;
|
||||
close();
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// bind some key events
|
||||
{
|
||||
const { eventSource } = inlineEditor;
|
||||
if (!eventSource) return;
|
||||
|
||||
const keydownObserverAbortController = new AbortController();
|
||||
this._disposables.add(() => keydownObserverAbortController.abort());
|
||||
|
||||
createKeydownObserver({
|
||||
target: eventSource,
|
||||
signal: keydownObserverAbortController.signal,
|
||||
onInput: isComposition => {
|
||||
if (isComposition) {
|
||||
this._updateLinkedDocGroup().catch(console.error);
|
||||
} else {
|
||||
const subscription = inlineEditor.slots.renderComplete.subscribe(
|
||||
() => {
|
||||
subscription.unsubscribe();
|
||||
this._updateLinkedDocGroup().catch(console.error);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
onDelete: () => {
|
||||
const subscription = inlineEditor.slots.renderComplete.subscribe(
|
||||
() => {
|
||||
subscription.unsubscribe();
|
||||
const curRange = inlineEditor.getInlineRange();
|
||||
|
||||
if (!this.context.startRange || !curRange) return;
|
||||
|
||||
if (curRange.index < this.context.startRange.index) {
|
||||
this.context.close();
|
||||
}
|
||||
this._updateLinkedDocGroup().catch(console.error);
|
||||
}
|
||||
);
|
||||
},
|
||||
onConfirm: () => {
|
||||
this._firstActionItem?.action()?.catch(console.error);
|
||||
},
|
||||
onAbort: () => {
|
||||
this.context.close();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
this._scrollInputToTop();
|
||||
}
|
||||
|
||||
override render() {
|
||||
const groups = this._linkedDocGroup$.value;
|
||||
if (groups.length === 0) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
this._firstActionItem = resolveSignal(groups[0].items)[0];
|
||||
|
||||
this.style.bottom = `${this.keyboard.height$.value}px`;
|
||||
|
||||
return html`
|
||||
${join(groups.map(this._renderGroup), html`<div class="divider"></div>`)}
|
||||
`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor context!: LinkedDocContext;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor rootComponent!: PageRootBlockComponent;
|
||||
}
|
||||
Reference in New Issue
Block a user