fix(editor): edgeless can't slider with finger (#15091)
fix bug edgeless can't slider with finger <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added mobile immersive edgeless mode with dynamic chrome auto-hide and tap-gesture controls. * Added a mobile zoom ruler UI for edgeless. * **Bug Fixes** * Improved iOS rendering/zoom by applying low-zoom survival behavior, gesture-aware refresh deferral, and effective-DPR canvas scaling. * Fixed iOS webview zoom/bounce and process-termination reload behavior. * Improved placeholder styling with theme-aware colors. * **Chores** * Updated local ignore rules and iOS app build/version configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: DarkSky <darksky2048@gmail.com>
This commit is contained in:
@@ -2,12 +2,14 @@ import {
|
||||
AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET,
|
||||
AffineEdgelessZoomToolbarWidget,
|
||||
} from '.';
|
||||
import { MobileZoomRuler } from './mobile-zoom-ruler';
|
||||
import { ZoomBarToggleButton } from './zoom-bar-toggle-button';
|
||||
import { EdgelessZoomToolbar } from './zoom-toolbar';
|
||||
|
||||
export function effects() {
|
||||
customElements.define('edgeless-zoom-toolbar', EdgelessZoomToolbar);
|
||||
customElements.define('zoom-bar-toggle-button', ZoomBarToggleButton);
|
||||
customElements.define('mobile-zoom-ruler', MobileZoomRuler);
|
||||
customElements.define(
|
||||
AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET,
|
||||
AffineEdgelessZoomToolbarWidget
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import type { RootBlockModel } from '@blocksuite/affine-model';
|
||||
import { IS_MOBILE } from '@blocksuite/global/env';
|
||||
import { WidgetComponent, WidgetViewExtension } from '@blocksuite/std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
|
||||
import { effect } from '@preact/signals-core';
|
||||
@@ -14,15 +15,20 @@ export class AffineEdgelessZoomToolbarWidget extends WidgetComponent<RootBlockMo
|
||||
static override styles = css`
|
||||
:host {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
bottom: var(--affine-edgeless-zoom-toolbar-bottom, 20px);
|
||||
left: 12px;
|
||||
z-index: var(--affine-z-index-popover);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
mobile-zoom-ruler {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@container viewport (width <= 1200px) {
|
||||
edgeless-zoom-toolbar {
|
||||
display: none;
|
||||
@@ -73,10 +79,14 @@ export class AffineEdgelessZoomToolbarWidget extends WidgetComponent<RootBlockMo
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this._hide || !this.edgeless) {
|
||||
if (this._hide) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
if (IS_MOBILE) {
|
||||
return html`<mobile-zoom-ruler .std=${this.std}></mobile-zoom-ruler>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<edgeless-zoom-toolbar .std=${this.std}></edgeless-zoom-toolbar>
|
||||
<zoom-bar-toggle-button .std=${this.std}></zoom-bar-toggle-button>
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||
import { WithDisposable } from '@blocksuite/global/lit';
|
||||
import { ViewBarIcon } from '@blocksuite/icons/lit';
|
||||
import type { BlockStdScope } from '@blocksuite/std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
|
||||
import { baseTheme } from '@toeverything/theme';
|
||||
import { css, html, LitElement, unsafeCSS } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
/**
|
||||
* Compact zoom indicator for narrow / mobile edgeless viewports.
|
||||
* Shows the live zoom percentage and a fit-to-screen action in a pill HUD
|
||||
* anchored to the bottom-left of the canvas.
|
||||
*/
|
||||
export class MobileZoomRuler extends WithDisposable(LitElement) {
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
pointer-events: auto;
|
||||
font-family: ${unsafeCSS(baseTheme.fontSansFamily)};
|
||||
}
|
||||
|
||||
.zoom-pill {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
background: var(--affine-background-overlay-panel-color);
|
||||
border: 1px solid var(--affine-border-color);
|
||||
border-radius: 999px;
|
||||
box-shadow: var(--affine-shadow-1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.zoom-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 44px;
|
||||
padding: 0 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
color: var(--affine-text-secondary-color);
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background: var(--affine-border-color);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fit-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--affine-icon-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fit-button:hover:not(:disabled) {
|
||||
background: var(--affine-hover-color);
|
||||
color: var(--affine-primary-color);
|
||||
}
|
||||
|
||||
.fit-button:disabled {
|
||||
cursor: not-allowed;
|
||||
color: var(--affine-text-disable-color);
|
||||
}
|
||||
|
||||
.fit-button svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
get gfx() {
|
||||
return this.std.get(GfxControllerIdentifier);
|
||||
}
|
||||
|
||||
get viewport() {
|
||||
return this.gfx.viewport;
|
||||
}
|
||||
|
||||
get zoom() {
|
||||
if (!this.viewport) {
|
||||
return 1;
|
||||
}
|
||||
return this.viewport.zoom;
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
const { disposables } = this;
|
||||
const viewport = this.viewport;
|
||||
if (!viewport) {
|
||||
return;
|
||||
}
|
||||
disposables.add(
|
||||
viewport.viewportUpdated.subscribe(() => this.requestUpdate())
|
||||
);
|
||||
disposables.add(viewport.zoomUpdated.subscribe(() => this.requestUpdate()));
|
||||
}
|
||||
|
||||
override render() {
|
||||
const formattedZoom = `${Math.round(this.zoom * 100)}%`;
|
||||
const locked = this.viewport?.locked || this.std.store.readonly;
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="zoom-pill"
|
||||
@dblclick=${stopPropagation}
|
||||
@mousedown=${stopPropagation}
|
||||
@mouseup=${stopPropagation}
|
||||
@pointerdown=${stopPropagation}
|
||||
>
|
||||
<span class="zoom-label">${formattedZoom}</span>
|
||||
<span class="divider"></span>
|
||||
<button
|
||||
class="fit-button"
|
||||
aria-label="Fit to screen"
|
||||
?disabled=${locked}
|
||||
@click=${() => this.gfx.fitToScreen()}
|
||||
>
|
||||
${ViewBarIcon()}
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor std!: BlockStdScope;
|
||||
}
|
||||
Reference in New Issue
Block a user