chore: merge blocksuite source code (#9213)

This commit is contained in:
Mirone
2024-12-20 15:38:06 +08:00
committed by GitHub
parent 2c9ef916f4
commit 30200ff86d
2031 changed files with 238888 additions and 229 deletions
@@ -0,0 +1,279 @@
import { assertExists } from '@blocksuite/global/utils';
import {
arrow,
type ComputePositionReturn,
flip,
offset,
type Placement,
} from '@floating-ui/dom';
import type { CSSResult } from 'lit';
import { css, html, LitElement, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
import { HoverController, type HoverOptions } from '../hover/index.js';
const styles = css`
.affine-tooltip {
box-sizing: border-box;
max-width: 280px;
min-height: 32px;
font-family: var(--affine-font-family);
font-size: var(--affine-font-sm);
border-radius: 4px;
padding: 6px 12px;
color: var(--affine-white);
background: var(--affine-tooltip);
display: flex;
justify-content: center;
align-items: center;
overflow-wrap: anywhere;
white-space: pre-wrap;
}
.arrow {
position: absolute;
width: 0;
height: 0;
}
`;
// See http://apps.eky.hk/css-triangle-generator/
const TRIANGLE_HEIGHT = 6;
const triangleMap = {
top: {
bottom: '-6px',
borderStyle: 'solid',
borderWidth: '6px 5px 0 5px',
borderColor: 'var(--affine-tooltip) transparent transparent transparent',
},
right: {
left: '-6px',
borderStyle: 'solid',
borderWidth: '5px 6px 5px 0',
borderColor: 'transparent var(--affine-tooltip) transparent transparent',
},
bottom: {
top: '-6px',
borderStyle: 'solid',
borderWidth: '0 5px 6px 5px',
borderColor: 'transparent transparent var(--affine-tooltip) transparent',
},
left: {
right: '-6px',
borderStyle: 'solid',
borderWidth: '5px 0 5px 6px',
borderColor: 'transparent transparent transparent var(--affine-tooltip)',
},
};
// Ported from https://floating-ui.com/docs/tutorial#arrow-middleware
const updateArrowStyles = ({
placement,
middlewareData,
}: ComputePositionReturn): StyleInfo => {
const arrowX = middlewareData.arrow?.x;
const arrowY = middlewareData.arrow?.y;
const triangleStyles =
triangleMap[placement.split('-')[0] as keyof typeof triangleMap];
return {
left: arrowX != null ? `${arrowX}px` : '',
top: arrowY != null ? `${arrowY}px` : '',
...triangleStyles,
};
};
/**
* @example
* ```ts
* // Simple usage
* html`
* <affine-tooltip>Content</affine-tooltip>
* `
* // With placement
* html`
* <affine-tooltip tip-position="top">
* Content
* </affine-tooltip>
* `
*
* // With custom properties
* html`
* <affine-tooltip
* .zIndex=${0}
* .offset=${4}
* .autoFlip=${true}
* .arrow=${true}
* .tooltipStyle=${css`:host { z-index: 0; --affine-tooltip: #fff; }`}
* .allowInteractive=${false}
* >
* Content
* </affine-tooltip>
* `
* ```
*/
export class Tooltip extends LitElement {
static override styles = css`
:host {
display: none;
}
`;
private _hoverController!: HoverController;
private _setUpHoverController = () => {
this._hoverController = new HoverController(
this,
() => {
// const parentElement = this.parentElement;
// if (
// parentElement &&
// 'disabled' in parentElement &&
// parentElement.disabled
// )
// return null;
if (this.hidden) return null;
let arrowStyles: StyleInfo = {};
return {
template: ({ positionSlot, updatePortal }) => {
positionSlot.on(data => {
// The tooltip placement may change,
// so we need to update the arrow position
if (this.arrow) {
arrowStyles = updateArrowStyles(data);
} else {
arrowStyles = {};
}
updatePortal();
});
const children = Array.from(this.childNodes).map(node =>
node.cloneNode(true)
);
return html`
<style>
${this._getStyles()}
</style>
<div class="affine-tooltip" role="tooltip">${children}</div>
<div class="arrow" style=${styleMap(arrowStyles)}></div>
`;
},
computePosition: portalRoot => ({
referenceElement: this.parentElement!,
placement: this.placement,
middleware: [
this.autoFlip && flip({ padding: 12 }),
offset((this.arrow ? TRIANGLE_HEIGHT : 0) + this.offset),
arrow({
element: portalRoot.shadowRoot!.querySelector('.arrow')!,
}),
],
autoUpdate: true,
}),
};
},
{
leaveDelay: 0,
// The tooltip is not interactive by default
safeBridge: false,
allowMultiple: true,
...this.hoverOptions,
}
);
const parent = this.parentElement;
assertExists(parent, 'Tooltip must have a parent element');
// Wait for render
setTimeout(() => {
this._hoverController.setReference(parent);
}, 0);
};
private _getStyles() {
return css`
${styles}
:host {
z-index: ${unsafeCSS(this.zIndex)};
opacity: 0;
${
// All the styles are applied to the portal element
unsafeCSS(this.style.cssText)
}
}
${this.allowInteractive
? css``
: css`
:host {
pointer-events: none;
}
`}
${this.tooltipStyle}
`;
}
override connectedCallback() {
super.connectedCallback();
this._setUpHoverController();
}
getPortal() {
return this._hoverController.portal;
}
/**
* Allow the tooltip to be interactive.
* eg. allow the user to select text in the tooltip.
*/
@property({ attribute: false })
accessor allowInteractive = false;
/**
* Show a triangle arrow pointing to the reference element.
*/
@property({ attribute: false })
accessor arrow = true;
/**
* changes the placement of the floating element in order to keep it in view,
* with the ability to flip to any placement.
*
* See https://floating-ui.com/docs/flip
*/
@property({ attribute: false })
accessor autoFlip = true;
@property({ attribute: false })
accessor hoverOptions: Partial<HoverOptions> = {};
/**
* Default is `4px`
*
* See https://floating-ui.com/docs/offset
*/
@property({ attribute: false })
accessor offset = 4;
@property({ attribute: 'tip-position' })
accessor placement: Placement = 'top';
@property({ attribute: false })
accessor tooltipStyle: CSSResult = css``;
@property({ attribute: false })
accessor zIndex: number | string = 'var(--affine-z-index-popover)';
}
declare global {
interface HTMLElementTagNameMap {
'affine-tooltip': Tooltip;
}
}