mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 14:56:59 +08:00
fix(editor): footnote popup style and position issues (#11771)
Close [BS-3049](https://linear.app/affine-design/issue/BS-3049/chat引用的样式坏了) [BS-3024](https://linear.app/affine-design/issue/BS-3024/footnote-在容器边缘时,hover-抽搐)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
|
||||
export const outlineBlockPreview = style({
|
||||
fontFamily: cssVar('fontFamily'),
|
||||
@@ -108,6 +108,11 @@ export const linkedDocPreviewUnavailable = style({
|
||||
color: cssVarV2('text/disable'),
|
||||
});
|
||||
|
||||
export const linkedDocPreviewAvailable = style({});
|
||||
globalStyle(`${linkedDocPreviewAvailable} > svg`, {
|
||||
marginBottom: '0.1em',
|
||||
});
|
||||
|
||||
export const linkedDocTextUnavailable = style({
|
||||
color: cssVarV2('text/disable'),
|
||||
textDecoration: 'line-through',
|
||||
|
||||
@@ -67,6 +67,7 @@ export class OutlineBlockPreview extends SignalWatcher(
|
||||
return html`<span
|
||||
class=${classMap({
|
||||
[styles.linkedDocPreviewUnavailable]: unavailable,
|
||||
[styles.linkedDocPreviewAvailable]: !unavailable,
|
||||
})}
|
||||
>
|
||||
${icon}
|
||||
|
||||
@@ -29,13 +29,23 @@ export const markdownFootnoteReferenceToDeltaMatcher =
|
||||
const footnoteReference = FootNoteReferenceParamsSchema.parse(
|
||||
footnoteDefinitionJson
|
||||
);
|
||||
|
||||
// If the footnote reference is an attachment, and the file type is not set,
|
||||
// Try to infer the file type from the file name.
|
||||
const { type: referenceType, fileName, fileType } = footnoteReference;
|
||||
if (referenceType === 'attachment' && fileName && !fileType) {
|
||||
const ext = fileName.split('.').pop()?.toLowerCase();
|
||||
if (ext) {
|
||||
footnoteReference.fileType = ext;
|
||||
}
|
||||
}
|
||||
|
||||
const footnote = {
|
||||
label: ast.identifier,
|
||||
reference: footnoteReference,
|
||||
};
|
||||
return [{ insert: ' ', attributes: { footnote } }];
|
||||
} catch (error) {
|
||||
console.warn('Error parsing footnote reference', error);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
ZERO_WIDTH_FOR_EMPTY_LINE,
|
||||
} from '@blocksuite/std/inline';
|
||||
import type { DeltaInsert } from '@blocksuite/store';
|
||||
import { shift } from '@floating-ui/dom';
|
||||
import { flip, offset, shift } from '@floating-ui/dom';
|
||||
import { baseTheme } from '@toeverything/theme';
|
||||
import { css, html, nothing, unsafeCSS } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
@@ -28,6 +28,8 @@ import type { FootNoteNodeConfigProvider } from './footnote-config';
|
||||
|
||||
// Virtual padding for the footnote popup overflow detection offsets.
|
||||
const POPUP_SHIFT_PADDING = 8;
|
||||
// The offset between the footnote node and the popup.
|
||||
const POPUP_OFFSET = 4;
|
||||
|
||||
export class AffineFootnoteNode extends WithDisposable(ShadowlessElement) {
|
||||
static override styles = css`
|
||||
@@ -203,12 +205,16 @@ export class AffineFootnoteNode extends WithDisposable(ShadowlessElement) {
|
||||
|
||||
return {
|
||||
template: this._FootNotePopup(footnote, abortController),
|
||||
container: this,
|
||||
container: this.std.host,
|
||||
computePosition: {
|
||||
referenceElement: this,
|
||||
placement: 'top',
|
||||
autoUpdate: true,
|
||||
middleware: [shift({ padding: POPUP_SHIFT_PADDING })],
|
||||
middleware: [
|
||||
shift({ padding: POPUP_SHIFT_PADDING }),
|
||||
flip(),
|
||||
offset(POPUP_OFFSET),
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -9,48 +9,42 @@ export class FootNotePopupChip extends LitElement {
|
||||
border-radius: 4px;
|
||||
max-width: 173px;
|
||||
height: 24px;
|
||||
padding: 2px 4px;
|
||||
padding: 4px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 8px;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
transition: width 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.prefix-icon,
|
||||
.suffix-icon {
|
||||
.prefix-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
color: ${unsafeCSSVarV2('icon/primary')};
|
||||
border-radius: 4px;
|
||||
|
||||
svg,
|
||||
object {
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
fill: ${unsafeCSSVarV2('icon/primary')};
|
||||
}
|
||||
}
|
||||
|
||||
.suffix-icon:hover {
|
||||
background-color: ${unsafeCSSVarV2('layer/background/hoverOverlay')};
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.popup-chip-label {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
color: ${unsafeCSSVarV2('text/primary')};
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-size: var(--affine-font-sm);
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -63,11 +57,6 @@ export class FootNotePopupChip extends LitElement {
|
||||
</div>`
|
||||
: nothing}
|
||||
<div class="popup-chip-label" title=${this.tooltip}>${this.label}</div>
|
||||
${this.suffixIcon
|
||||
? html`<div class="suffix-icon" @click=${this.onSuffixClick}>
|
||||
${this.suffixIcon}
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -78,9 +67,6 @@ export class FootNotePopupChip extends LitElement {
|
||||
@property({ attribute: false })
|
||||
accessor label: string = '';
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor suffixIcon: TemplateResult | undefined = undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor tooltip: string = '';
|
||||
|
||||
@@ -89,7 +75,4 @@ export class FootNotePopupChip extends LitElement {
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onPrefixClick: (() => void) | undefined = undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onSuffixClick: (() => void) | undefined = undefined;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,9 @@ import {
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
|
||||
import { DualLinkIcon, LinkIcon } from '@blocksuite/icons/lit';
|
||||
import type { BlockStdScope } from '@blocksuite/std';
|
||||
import { computed, signal } from '@preact/signals-core';
|
||||
import { css, html, LitElement, type TemplateResult } from 'lit';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import type { FootNotePopupClickHandler } from './footnote-config';
|
||||
@@ -56,36 +55,11 @@ export class FootNotePopup extends SignalWatcher(WithDisposable(LitElement)) {
|
||||
}
|
||||
|
||||
const favicon = this._linkPreview$.value?.favicon;
|
||||
if (!favicon) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const titleIconType =
|
||||
favicon.split('.').pop() === 'svg'
|
||||
? 'svg+xml'
|
||||
: favicon.split('.').pop();
|
||||
const titleIcon = html`<object
|
||||
type="image/${titleIconType}"
|
||||
data=${favicon}
|
||||
draggable="false"
|
||||
>
|
||||
${WebIcon16}
|
||||
</object>`;
|
||||
return titleIcon;
|
||||
return favicon ? html`<img src=${favicon} alt="favicon" />` : WebIcon16;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
private readonly _suffixIcon = (): TemplateResult | undefined => {
|
||||
const referenceType = this.footnote.reference.type;
|
||||
if (referenceType === 'doc') {
|
||||
return DualLinkIcon({ width: '16px', height: '16px' });
|
||||
} else if (referenceType === 'url') {
|
||||
return LinkIcon({ width: '16px', height: '16px' });
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
private readonly _popupLabel$ = computed(() => {
|
||||
const referenceType = this.footnote.reference.type;
|
||||
let label = '';
|
||||
@@ -157,7 +131,6 @@ export class FootNotePopup extends SignalWatcher(WithDisposable(LitElement)) {
|
||||
<footnote-popup-chip
|
||||
.prefixIcon=${this._prefixIcon$.value}
|
||||
.label=${this._popupLabel$.value}
|
||||
.suffixIcon=${this._suffixIcon()}
|
||||
.onClick=${this._onChipClick}
|
||||
.tooltip=${this._tooltip$.value}
|
||||
></footnote-popup-chip>
|
||||
|
||||
@@ -49,6 +49,10 @@ export class AffineReference extends WithDisposable(ShadowlessElement) {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 1px 2px 1px 0;
|
||||
|
||||
svg {
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
}
|
||||
.affine-reference:hover {
|
||||
background: var(--affine-hover-color);
|
||||
|
||||
@@ -217,7 +217,7 @@ export class DocDisplayMetaService
|
||||
function iconBuilder(
|
||||
icon: typeof PageIcon,
|
||||
size = '1.25em',
|
||||
style = 'user-select:none;flex-shrink:0;vertical-align:middle;font-size:inherit;margin-bottom:0.1em;'
|
||||
style = 'user-select:none;flex-shrink:0;vertical-align:middle;font-size:inherit;'
|
||||
) {
|
||||
return icon({ width: size, height: size, style });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user