@blocksuite/blocks
Advanced tools
Comparing version 0.3.0-20221225004222-eacf37a to 0.3.0-20221225012015-8e5d6f5
@@ -37,2 +37,3 @@ import { ALLOW_DEFAULT, getCurrentRange, getNextBlock, isCollapsedAtBlockStart, isMultiBlockRange, matchFlavours, noop, PREVENT_DEFAULT, } from '../utils/index.js'; | ||
flavour: 'affine:code', | ||
type: 'code', | ||
}, | ||
@@ -39,0 +40,0 @@ ]; |
import type { Page } from '@blocksuite/store'; | ||
import type { Quill } from 'quill'; | ||
import type { ExtendedModel } from './types.js'; | ||
export declare function asyncFocusRichText(page: Page, id: string): void; | ||
export declare function asyncFocusRichText(page: Page, id: string): Promise<void>; | ||
export declare function isCollapsedAtBlockStart(quill: Quill): boolean; | ||
@@ -6,0 +6,0 @@ export declare function convertToList(page: Page, model: ExtendedModel, listType: 'bulleted' | 'numbered' | 'todo', prefix: string, otherProperties?: Record<string, unknown>): boolean; |
@@ -1,8 +0,7 @@ | ||
import { matchFlavours } from './std.js'; | ||
import { matchFlavours, sleep } from './std.js'; | ||
// XXX: workaround quill lifecycle issue | ||
export function asyncFocusRichText(page, id) { | ||
setTimeout(() => { | ||
const adapter = page.richTextAdapters.get(id); | ||
adapter?.quill.focus(); | ||
}); | ||
export async function asyncFocusRichText(page, id) { | ||
await sleep(); | ||
const adapter = page.richTextAdapters.get(id); | ||
adapter?.quill.focus(); | ||
} | ||
@@ -9,0 +8,0 @@ export function isCollapsedAtBlockStart(quill) { |
@@ -273,3 +273,3 @@ import { BLOCK_ID_ATTR as ATTR } from './consts.js'; | ||
if (!blockElement) { | ||
throw new Error('Failed to get block element'); | ||
throw new Error('Failed to get block element, block id: ' + selectedBlock.id); | ||
} | ||
@@ -276,0 +276,0 @@ const richText = blockElement.querySelector('rich-text'); |
@@ -10,2 +10,3 @@ import { LitElement } from 'lit'; | ||
langListElement: HTMLElement; | ||
langSelectionButton: HTMLElement; | ||
showLangList: string; | ||
@@ -12,0 +13,0 @@ disposeTimer: number; |
@@ -33,7 +33,4 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
onClick() { | ||
return () => { | ||
window.setTimeout(() => { | ||
this.showLangList = 'visible'; | ||
}, 0); | ||
}; | ||
this.showLangList = 'visible'; | ||
this.langSelectionButton.classList.add('clicked'); | ||
} | ||
@@ -46,7 +43,6 @@ render() { | ||
<div class="container"> | ||
<div class="lang-container has-tool-tip" @click=${this.onClick()}> | ||
<div class="lang-container" @click=${this.onClick}> | ||
<code-block-button width="101px" height="24px"> | ||
${this.model.language} ${ArrowDownIcon} | ||
</code-block-button> | ||
<tool-tip inert role="tooltip">switch language</tool-tip> | ||
</div> | ||
@@ -61,2 +57,3 @@ <lang-list | ||
this.showLangList = 'hidden'; | ||
this.langSelectionButton.classList.remove('clicked'); | ||
}} | ||
@@ -103,2 +100,5 @@ ></lang-list> | ||
__decorate([ | ||
query('.lang-container code-block-button') | ||
], CodeBlockComponent.prototype, "langSelectionButton", void 0); | ||
__decorate([ | ||
state() | ||
@@ -105,0 +105,0 @@ ], CodeBlockComponent.prototype, "showLangList", void 0); |
@@ -5,2 +5,3 @@ import { BaseBlockModel, IBaseBlockProps, Page } from '@blocksuite/store'; | ||
flavour: "affine:code"; | ||
type: "code"; | ||
tag: import("lit-html/static.js").StaticValue; | ||
@@ -7,0 +8,0 @@ language: string; |
@@ -7,2 +7,3 @@ import { BaseBlockModel } from '@blocksuite/store'; | ||
this.flavour = 'affine:code'; | ||
this.type = 'code'; | ||
this.tag = literal `affine-code`; | ||
@@ -9,0 +10,0 @@ this.language = 'javascript'; |
@@ -44,3 +44,3 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
const target = e.target; | ||
if (!target.closest('lang-list')?.closest(`[data-block-id="${this.id}"]`)) { | ||
if (!target.closest('.container')?.closest(`[data-block-id="${this.id}"]`)) { | ||
this.dispose(); | ||
@@ -47,0 +47,0 @@ } |
@@ -54,2 +54,7 @@ import type { BaseBlockModel, Page } from '@blocksuite/store'; | ||
}, { | ||
readonly flavour: "affine:code"; | ||
readonly type: "code"; | ||
readonly name: "Code Block"; | ||
readonly icon: import("lit-html").TemplateResult<2>; | ||
}, { | ||
readonly flavour: "affine:paragraph"; | ||
@@ -65,3 +70,3 @@ readonly type: "quote"; | ||
}; | ||
export declare const formatButtons: ({ | ||
export declare const formatButtons: { | ||
id: string; | ||
@@ -71,13 +76,7 @@ name: string; | ||
activeWhen: (format: Record<string, unknown>) => boolean; | ||
showWhen: (models: BaseBlockModel[]) => boolean; | ||
action: ({ page }: ActionProps) => void; | ||
showWhen?: undefined; | ||
} | { | ||
id: string; | ||
name: string; | ||
icon: import("lit-html").TemplateResult<2>; | ||
activeWhen: (format: Record<string, unknown>) => boolean; | ||
showWhen: (models: BaseBlockModel[]) => boolean; | ||
action: ({ page, abortController, format }: ActionProps) => void; | ||
})[]; | ||
}[]; | ||
export declare const noneCodeBlockSelected: (models: BaseBlockModel[]) => boolean; | ||
export {}; | ||
//# sourceMappingURL=config.d.ts.map |
import { handleFormat } from '../../page-block/utils/index.js'; | ||
import { createLink } from '../../__internal__/rich-text/link-node/index.js'; | ||
import './button'; | ||
import { BoldIcon, BulletedListIcon, H1Icon, H2Icon, H3Icon, H4Icon, H5Icon, H6Icon, InlineCodeIcon, ItalicIcon, LinkIcon, NumberedIcon, QuoteIcon, StrikethroughIcon, TextIcon, TodoIcon, UnderlineIcon, } from './icons.js'; | ||
import { BoldIcon, BulletedListIcon, CodeIcon, H1Icon, H2Icon, H3Icon, H4Icon, H5Icon, H6Icon, InlineCodeIcon, ItalicIcon, LinkIcon, NumberedIcon, QuoteIcon, StrikethroughIcon, TextIcon, TodoIcon, UnderlineIcon, } from './icons.js'; | ||
import { CodeBlockModel } from '../../code-block/index.js'; | ||
export const paragraphButtons = [ | ||
@@ -31,3 +32,3 @@ { | ||
{ flavour: 'affine:list', type: 'todo', name: 'To-do List', icon: TodoIcon }, | ||
// { flavour: 'affine:', type: 'code', name: 'Code Block', icon: CodeIcon }, | ||
{ flavour: 'affine:code', type: 'code', name: 'Code Block', icon: CodeIcon }, | ||
{ | ||
@@ -52,2 +53,3 @@ flavour: 'affine:paragraph', | ||
activeWhen: (format) => 'bold' in format, | ||
showWhen: (models) => noneCodeBlockSelected(models), | ||
action: ({ page }) => { | ||
@@ -62,2 +64,3 @@ handleFormat(page, 'bold'); | ||
activeWhen: (format) => 'italic' in format, | ||
showWhen: (models) => noneCodeBlockSelected(models), | ||
action: ({ page }) => { | ||
@@ -72,2 +75,3 @@ handleFormat(page, 'italic'); | ||
activeWhen: (format) => 'underline' in format, | ||
showWhen: (models) => noneCodeBlockSelected(models), | ||
action: ({ page }) => { | ||
@@ -82,2 +86,3 @@ handleFormat(page, 'underline'); | ||
activeWhen: (format) => 'strike' in format, | ||
showWhen: (models) => noneCodeBlockSelected(models), | ||
action: ({ page }) => { | ||
@@ -92,2 +97,3 @@ handleFormat(page, 'strike'); | ||
activeWhen: (format) => 'code' in format, | ||
showWhen: (models) => noneCodeBlockSelected(models), | ||
action: ({ page }) => { | ||
@@ -103,3 +109,3 @@ handleFormat(page, 'code'); | ||
// Only can show link button when selection is in one line paragraph | ||
showWhen: (models) => models.length === 1, | ||
showWhen: (models) => models.length === 1 && noneCodeBlockSelected(models), | ||
action: ({ page, abortController, format }) => { | ||
@@ -113,2 +119,5 @@ createLink(page); | ||
]; | ||
export const noneCodeBlockSelected = (models) => { | ||
return !models.every(model => model instanceof CodeBlockModel); | ||
}; | ||
//# sourceMappingURL=config.js.map |
@@ -106,4 +106,4 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
// Already in the target format, convert back to text | ||
const { flavour: defaultParagraph, type: defaultType } = paragraphButtons[0]; | ||
updateSelectedTextType(defaultParagraph, defaultType, this.page); | ||
const { flavour: defaultFlavour, type: defaultType } = paragraphButtons[0]; | ||
updateSelectedTextType(defaultFlavour, defaultType, this.page); | ||
this.paragraphType = defaultType; | ||
@@ -114,2 +114,3 @@ return; | ||
this.paragraphType = type; | ||
this.positionUpdated.emit(); | ||
}} | ||
@@ -138,10 +139,9 @@ > | ||
const paragraphPanel = this.paragraphPanelTemplate(); | ||
const formatItems = html ` | ||
${formatButtons | ||
const formatItems = formatButtons | ||
.filter(({ showWhen = () => true }) => showWhen(this.models)) | ||
.map(({ id, name, icon, action, activeWhen }) => html `<format-bar-button | ||
class="has-tool-tip" | ||
data-testid=${id} | ||
?active=${activeWhen(this.format)} | ||
@click=${() => { | ||
class="has-tool-tip" | ||
data-testid=${id} | ||
?active=${activeWhen(this.format)} | ||
@click=${() => { | ||
action({ | ||
@@ -154,8 +154,8 @@ page, | ||
this.format = getFormat(); | ||
this.positionUpdated.emit(); | ||
}} | ||
> | ||
${icon} | ||
<tool-tip inert role="tooltip">${name}</tool-tip> | ||
</format-bar-button>`)} | ||
`; | ||
> | ||
${icon} | ||
<tool-tip inert role="tooltip">${name}</tool-tip> | ||
</format-bar-button>`); | ||
const actionItems = html `<format-bar-button | ||
@@ -178,3 +178,3 @@ class="has-tool-tip" | ||
${formatItems} | ||
<div class="divider"></div> | ||
${formatItems.length ? html `<div class="divider"></div>` : ''} | ||
${actionItems} ${paragraphPanel} | ||
@@ -181,0 +181,0 @@ </div>`; |
import { DragDirection } from '../../page-block/utils/cursor.js'; | ||
import './button'; | ||
import './format-bar-node'; | ||
import './button.js'; | ||
import './format-bar-node.js'; | ||
export declare const showFormatQuickBar: ({ anchorEl, direction, container, abortController, }: { | ||
anchorEl: { | ||
anchorEl?: Range | { | ||
getBoundingClientRect: () => DOMRect; | ||
} | Range; | ||
} | undefined; | ||
direction?: DragDirection | undefined; | ||
@@ -9,0 +9,0 @@ container?: HTMLElement | undefined; |
import { Signal } from '@blocksuite/store'; | ||
import { calcPositionPointByRange, calcSafeCoordinate, } from '../../page-block/utils/cursor.js'; | ||
import { getContainerByModel, getCurrentRange, getModelsByRange, sleep, throttle, } from '../../__internal__/utils/index.js'; | ||
import './button'; | ||
import './format-bar-node'; | ||
import './button.js'; | ||
import './format-bar-node.js'; | ||
export const showFormatQuickBar = async ({ anchorEl, direction = 'right-bottom', container = document.body, abortController = new AbortController(), }) => { | ||
@@ -15,5 +15,6 @@ // Init format quick bar | ||
const updatePos = throttle(() => { | ||
const positioningPoint = anchorEl instanceof Range | ||
? calcPositionPointByRange(anchorEl, direction) | ||
: anchorEl.getBoundingClientRect(); | ||
const positioningEl = anchorEl ?? getCurrentRange(); | ||
const positioningPoint = positioningEl instanceof Range | ||
? calcPositionPointByRange(positioningEl, direction) | ||
: positioningEl.getBoundingClientRect(); | ||
// TODO maybe use the editor container as the boundary rect to avoid the format bar being covered by other elements | ||
@@ -49,11 +50,2 @@ const boundaryRect = document.body.getBoundingClientRect(); | ||
window.addEventListener('resize', updatePos, { passive: true }); | ||
// Handle click outside | ||
const clickAwayListener = (e) => { | ||
if (e.target === formatQuickBar) { | ||
return; | ||
} | ||
abortController.abort(); | ||
window.removeEventListener('mousedown', clickAwayListener); | ||
}; | ||
window.addEventListener('mousedown', clickAwayListener); | ||
// Handle selection change | ||
@@ -60,0 +52,0 @@ const selectionChangeHandler = () => { |
/// <reference types="./index.d.ts" /> | ||
/**/ | ||
import { al as m, aY as k, aW as S, aV as y, aA as C, ah as h, ar as u, aj as M, aB as v, a_ as P, aZ as D, aC as T, as as x, at as b, ak as E, am as F, aU as I, aT as L, au as A, aX as N, aR as R, aS as f, aD as z, av as G, aw as H, a$ as q, aq as O, b1 as U, b2 as j, b0 as w, ai as K, ap as Q, ag as W, ao as _, an as J, aa as V, a6 as X, a7 as Y, a as Z, aF as $, a9 as aa, af as ea, d as oa, c as ta, b as sa, ax as la, ab as na, ay as ca, aH as ra, C as ia, f as Ba, r as da, q as pa, x as ga, w as ma, p as ka, aE as Sa, aL as ya, v as Ca, u as ha, n as ua, m as Ma, j as va, o as Pa, l as Da, y as Ta, t as xa, g as ba, k as Ea, s as Fa, z as Ia, aK as La, aP as Aa, aM as Na, aN as Ra, aO as fa, i as za, a8 as Ga, ac as Ha, aG as qa, ad as Oa, ae as Ua, az as ja, aJ as wa, aQ as Ka, aI as Qa } from "./index.621f59bc.js"; | ||
import { al as m, aY as k, aW as S, aV as y, aN as C, ah as h, aE as u, aj as M, aO as v, a_ as P, aZ as D, aP as T, aF as x, aG as b, ak as E, am as F, aU as I, aT as L, aH as A, aX as N, aR as R, aS as f, aQ as z, aI as G, aJ as H, a$ as q, aD as O, b1 as U, b2 as j, b0 as w, ai as K, ap as Q, ag as W, ao as _, an as J, aa as V, a6 as X, a7 as Y, a as Z, ar as $, a9 as aa, af as ea, d as oa, c as ta, b as sa, aK as la, ab as na, aL as ca, at as ra, C as ia, f as Ba, r as da, q as pa, x as ga, w as ma, p as ka, aq as Sa, ax as ya, v as Ca, u as ha, n as ua, m as Ma, j as va, o as Pa, l as Da, y as Ta, t as xa, g as ba, k as Ea, s as Fa, z as Ia, aw as La, aB as Aa, ay as Na, az as Ra, aA as fa, i as za, a8 as Ga, ac as Ha, as as qa, ad as Oa, ae as Ua, aM as ja, av as wa, aC as Ka, au as Qa } from "./index.df04b79c.js"; | ||
import "lit"; | ||
@@ -6,0 +6,0 @@ import "lit/decorators.js"; |
@@ -213,3 +213,3 @@ import { showFormatQuickBar } from '../../components/format-quick-bar/index.js'; | ||
} | ||
showFormatQuickBar({ anchorEl: range, direction: 'center-bottom' }); | ||
showFormatQuickBar({ direction: 'center-bottom' }); | ||
}; | ||
@@ -348,3 +348,3 @@ this._onContainerContextMenu = (e) => { | ||
if (this.state.type === 'native') { | ||
const { anchor, direction, selectedType } = getNativeSelectionMouseDragInfo(e); | ||
const { direction, selectedType } = getNativeSelectionMouseDragInfo(e); | ||
if (selectedType === 'Caret') { | ||
@@ -354,3 +354,3 @@ // If nothing is selected, then we should not show the format bar | ||
} | ||
showFormatQuickBar({ anchorEl: anchor, direction }); | ||
showFormatQuickBar({ direction }); | ||
} | ||
@@ -357,0 +357,0 @@ else if (this.state.type === 'block') { |
@@ -281,11 +281,29 @@ import { toast } from '../../components/toast.js'; | ||
}); | ||
hotkey.addListener(H1, () => updateSelectedTextType('affine:paragraph', 'h1', page)); | ||
hotkey.addListener(H2, () => updateSelectedTextType('affine:paragraph', 'h2', page)); | ||
hotkey.addListener(H3, () => updateSelectedTextType('affine:paragraph', 'h3', page)); | ||
hotkey.addListener(H4, () => updateSelectedTextType('affine:paragraph', 'h4', page)); | ||
hotkey.addListener(H5, () => updateSelectedTextType('affine:paragraph', 'h5', page)); | ||
hotkey.addListener(H6, () => updateSelectedTextType('affine:paragraph', 'h6', page)); | ||
hotkey.addListener(NUMBERED_LIST, () => updateSelectedTextType('affine:list', 'numbered', page)); | ||
hotkey.addListener(BULLETED, () => updateSelectedTextType('affine:list', 'bulleted', page)); | ||
hotkey.addListener(TEXT, () => updateSelectedTextType('affine:paragraph', 'text', page)); | ||
hotkey.addListener(H1, () => { | ||
updateSelectedTextType('affine:paragraph', 'h1', page); | ||
}); | ||
hotkey.addListener(H2, () => { | ||
updateSelectedTextType('affine:paragraph', 'h2', page); | ||
}); | ||
hotkey.addListener(H3, () => { | ||
updateSelectedTextType('affine:paragraph', 'h3', page); | ||
}); | ||
hotkey.addListener(H4, () => { | ||
updateSelectedTextType('affine:paragraph', 'h4', page); | ||
}); | ||
hotkey.addListener(H5, () => { | ||
updateSelectedTextType('affine:paragraph', 'h5', page); | ||
}); | ||
hotkey.addListener(H6, () => { | ||
updateSelectedTextType('affine:paragraph', 'h6', page); | ||
}); | ||
hotkey.addListener(NUMBERED_LIST, () => { | ||
updateSelectedTextType('affine:list', 'numbered', page); | ||
}); | ||
hotkey.addListener(BULLETED, () => { | ||
updateSelectedTextType('affine:list', 'bulleted', page); | ||
}); | ||
hotkey.addListener(TEXT, () => { | ||
updateSelectedTextType('affine:paragraph', 'text', page); | ||
}); | ||
hotkey.addListener(SHIFT_UP, e => { | ||
@@ -292,0 +310,0 @@ // TODO expand selection up |
@@ -141,3 +141,3 @@ import { getSelectionBoxBound, pick } from '../utils.js'; | ||
if (this.isActive) { | ||
const { anchor, direction, selectedType } = getNativeSelectionMouseDragInfo(e); | ||
const { direction, selectedType } = getNativeSelectionMouseDragInfo(e); | ||
if (selectedType === 'Caret') { | ||
@@ -147,3 +147,3 @@ // If nothing is selected, then we should not show the format bar | ||
} | ||
showFormatQuickBar({ anchorEl: anchor, direction }); | ||
showFormatQuickBar({ direction }); | ||
} | ||
@@ -150,0 +150,0 @@ else if (this.blockSelectionState.type === 'single') { |
@@ -1,7 +0,7 @@ | ||
import type { BaseBlockModel, Page } from '@blocksuite/store'; | ||
import { BaseBlockModel, Page } from '@blocksuite/store'; | ||
import { ExtendedModel } from '../../__internal__/index.js'; | ||
import type { DefaultSelectionManager } from '../default/selection-manager.js'; | ||
export declare function deleteModels(page: Page, models: BaseBlockModel[]): void; | ||
export declare function updateSelectedTextType(flavour: string, type: string, page: Page): void; | ||
export declare function transformBlock(page: Page, model: BaseBlockModel, flavour: string, type: string): void; | ||
export declare function updateSelectedTextType(flavour: string, type: string, page: Page): Promise<void>; | ||
export declare function transformBlock(page: Page, model: BaseBlockModel, flavour: string, type: string): string; | ||
export declare function handleBackspace(page: Page, e: KeyboardEvent): void; | ||
@@ -8,0 +8,0 @@ export declare const getFormat: () => import("quill").StringMap; |
@@ -0,1 +1,2 @@ | ||
import { Text } from '@blocksuite/store'; | ||
import { almostEqual, assertExists, assertFlavours, } from '../../__internal__/index.js'; | ||
@@ -6,2 +7,3 @@ import { asyncFocusRichText } from '../../__internal__/utils/common-operations.js'; | ||
import { DEFAULT_SPACING } from '../edgeless/utils.js'; | ||
import { CodeBlockModel } from '../../code-block/index.js'; | ||
export function deleteModels(page, models) { | ||
@@ -35,8 +37,30 @@ const selection = window.getSelection(); | ||
} | ||
export function updateSelectedTextType(flavour, type, page) { | ||
function mergeTextOfBlocks(page, models, blockProps) { | ||
const parent = page.getParent(models[0]); | ||
assertExists(parent); | ||
const index = parent.children.indexOf(models[0]); | ||
const id = page.addBlock(blockProps, parent, index); | ||
const codeBlock = page.getBlockById(id); | ||
models.forEach(model => { | ||
if (model.text instanceof Text) { | ||
const text = codeBlock.text; | ||
assertExists(text); | ||
text.join(model.text); | ||
text.insert('\n', text.length); | ||
} | ||
page.deleteBlock(model); | ||
}); | ||
} | ||
export async function updateSelectedTextType(flavour, type, page) { | ||
const range = getCurrentRange(); | ||
const modelsInRange = getModelsByRange(range); | ||
page.captureSync(); | ||
if (flavour === 'affine:code') { | ||
mergeTextOfBlocks(page, modelsInRange, { flavour: 'affine:code' }); | ||
return; | ||
} | ||
const selectedBlocks = saveBlockSelection(); | ||
let lastNewId = null; | ||
modelsInRange.forEach(model => { | ||
assertFlavours(model, ['affine:paragraph', 'affine:list']); | ||
assertFlavours(model, ['affine:paragraph', 'affine:list', 'affine:code']); | ||
if (model.flavour === flavour) { | ||
@@ -46,5 +70,17 @@ page.updateBlock(model, { type }); | ||
else { | ||
transformBlock(page, model, flavour, type); | ||
const oldId = model.id; | ||
const newId = transformBlock(page, model, flavour, type); | ||
// Replace selected block id | ||
const blocks = selectedBlocks.filter(block => block.id === oldId); | ||
// Because selectedBlocks maybe contains same block when only select one block, so we need to replace all of them | ||
blocks.forEach(block => { | ||
block.id = newId; | ||
}); | ||
lastNewId = newId; | ||
} | ||
}); | ||
if (lastNewId) { | ||
await asyncFocusRichText(page, lastNewId); | ||
} | ||
restoreSelection(selectedBlocks); | ||
} | ||
@@ -63,3 +99,3 @@ export function transformBlock(page, model, flavour, type) { | ||
const id = page.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(page, id); | ||
return id; | ||
} | ||
@@ -105,3 +141,5 @@ export function handleBackspace(page, e) { | ||
const formatArr = []; | ||
formatArr.push(firstFormat, lastFormat); | ||
!(models[0] instanceof CodeBlockModel) && formatArr.push(firstFormat); | ||
!(models[models.length - 1] instanceof CodeBlockModel) && | ||
formatArr.push(lastFormat); | ||
for (let i = 1; i < models.length - 1; i++) { | ||
@@ -115,2 +153,5 @@ const richText = getRichTextByModel(models[i]); | ||
} | ||
if (models[i] instanceof CodeBlockModel) { | ||
continue; | ||
} | ||
const format = richText.quill.getFormat(0, richText.quill.getLength() - 1); | ||
@@ -160,3 +201,5 @@ formatArr.push(format); | ||
if (isRangeSelection()) { | ||
const models = getModelsByRange(getCurrentRange()); | ||
const models = getModelsByRange(getCurrentRange()).filter(model => { | ||
return !(model instanceof CodeBlockModel); | ||
}); | ||
if (models.length === 1) { | ||
@@ -213,2 +256,3 @@ const richText = getRichTextByModel(models[0]); | ||
} | ||
// TODO should show format bar after select all | ||
function initQuickBarEventHandlersAfterSelectAll(nearestCommonAncestor) { | ||
@@ -215,0 +259,0 @@ nearestCommonAncestor.addEventListener('mousemove', e => { |
@@ -11,7 +11,6 @@ import { SelectionEvent } from '../../__internal__/index.js'; | ||
* ```ts | ||
* const { selectedType, direction, anchor } = getNativeSelectionMouseDragInfo(e); | ||
* const { selectedType, direction } = getNativeSelectionMouseDragInfo(e); | ||
* if (selectedType === 'Caret') { | ||
* return; | ||
* } | ||
* const rect = anchor.getBoundingClientRect(); | ||
* ``` | ||
@@ -22,3 +21,2 @@ */ | ||
direction: DragDirection; | ||
anchor: Range; | ||
}; | ||
@@ -25,0 +23,0 @@ export declare function calcPositionPointByRange(range: Range, direction: DragDirection): { |
@@ -47,7 +47,6 @@ import { caretRangeFromPoint, clamp, getCurrentRange, isMultiLineRange, resetNativeSelection, } from '../../__internal__/index.js'; | ||
* ```ts | ||
* const { selectedType, direction, anchor } = getNativeSelectionMouseDragInfo(e); | ||
* const { selectedType, direction } = getNativeSelectionMouseDragInfo(e); | ||
* if (selectedType === 'Caret') { | ||
* return; | ||
* } | ||
* const rect = anchor.getBoundingClientRect(); | ||
* ``` | ||
@@ -65,3 +64,3 @@ */ | ||
const selectedType = isSelectedNothing ? 'Caret' : 'Text'; | ||
return { selectedType, direction, anchor: curRange }; | ||
return { selectedType, direction }; | ||
} | ||
@@ -68,0 +67,0 @@ export function calcPositionPointByRange(range, direction) { |
@@ -6,3 +6,6 @@ // https://github.com/tldraw/tldraw/blob/24cad6959f59f93e20e556d018c391fd89d4ecca/packages/tldraw/src/state/shapes/RectangleUtil/rectangleHelpers.ts | ||
import { getShapeStyle } from './shape-style.js'; | ||
import { getStrokePoints, getStroke } from 'perfect-freehand'; | ||
import PF from 'perfect-freehand'; | ||
import * as DPF from 'perfect-freehand'; | ||
const getStrokePoints = DPF.getStrokePoints; | ||
const getStroke = PF; | ||
function getRectangleDrawPoints(id, style, size) { | ||
@@ -9,0 +12,0 @@ const styles = getShapeStyle(style); |
@@ -7,3 +7,6 @@ // https://github.com/tldraw/tldraw/blob/ca91e56b29212897b6c97650f567f7ea3e818d1e/packages/tldraw/src/state/shapes/TriangleUtil/triangleHelpers.ts | ||
import { getOffsetPolygon } from './polygon-utils.js'; | ||
import { getStrokePoints, getStroke } from 'perfect-freehand'; | ||
import PF from 'perfect-freehand'; | ||
import * as DPF from 'perfect-freehand'; | ||
const getStrokePoints = DPF.getStrokePoints; | ||
const getStroke = PF; | ||
export function getTrianglePoints(size, offset = 0, rotation = 0) { | ||
@@ -10,0 +13,0 @@ const [w, h] = size; |
import * as std from './__internal__/utils/index.js'; | ||
export * from './page-block/utils/index.js'; | ||
export * from './__internal__/utils/index.js'; | ||
export default std; | ||
//# sourceMappingURL=std.d.ts.map |
187
dist/std.js
/// <reference types="./std.d.ts" /> | ||
/**/ | ||
import { B as a, P as e, A as s, H as t, a as o, i as l, c as i, b as n, d as c, e as r, R as g, h as B, g as d, f as S, j as y, k as T, l as u, m as p, n as k, o as v, p as h, q as R, r as m, s as x, t as f, u as E, v as N, w as L, x as M, y as P, z as C, C as D, D as F, E as I, F as A, G as b, I as O, J as _, K as H, L as z, M as j, N as K, O as U, Q as q, S as Q, T as V, U as W, V as Y, W as w, X as G, Y as J, Z as X, _ as Z, $, a0 as aa, a1 as ea, a2 as sa, a3 as ta, a4 as oa, a5 as la, a6 as ia, a7 as na, a8 as ca, a9 as ra, aa as ga, ab as Ba, ac as da, ad as Sa, ae as ya, af as Ta, ag as ua, ah as pa, ai as ka, aj as va, ak as ha, al as Ra, am as ma, an as xa, ao as fa, ap as Ea } from "./index.621f59bc.js"; | ||
import { A as Ua, al as qa, B as Qa, E as Va, D as Wa, ah as Ya, aj as wa, ak as Ga, am as Ja, H as Xa, P as Za, e as $a, R as ae, ai as ee, ap as se, ag as te, ao as oe, an as le, aa as ie, a6 as ne, a7 as ce, a as re, a9 as ge, af as Be, d as de, c as Se, b as ye, ab as Te, L as ue, K as pe, J as ke, N as ve, O as he, C as Re, f as me, r as xe, q as fe, x as Ee, w as Ne, p as Le, a1 as Me, a0 as Pe, v as Ce, u as De, n as Fe, m as Ie, j as Ae, o as be, l as Oe, y as _e, t as He, W as ze, g as je, k as Ke, a2 as Ue, s as qe, z as Qe, Z as Ve, _ as We, X as Ye, h as we, I as Ge, Y as Je, i as Xe, S as Ze, a3 as $e, G as as, U as es, V as ss, Q as ts, F as os, T as ls, $ as is, a8 as ns, ac as cs, M as rs, a5 as gs, a4 as Bs, ad as ds, ae as Ss } from "./index.621f59bc.js"; | ||
import { B as a, P as e, A as s, H as t, a as o, i as l, c as i, b as n, d as c, e as r, R as g, h as d, g as B, f as S, j as y, k as u, l as T, m as p, n as k, o as h, p as m, q as v, r as R, s as x, t as f, u as N, v as E, w as C, x as L, y as M, z as D, C as P, D as F, E as A, F as I, G as b, I as O, J as _, K as H, L as z, M as K, N as U, O as j, Q as q, S as Q, T as V, U as W, V as Y, W as w, X as G, Y as J, Z as X, _ as Z, $, a0 as aa, a1 as ea, a2 as sa, a3 as ta, a4 as oa, a5 as la, a6 as ia, a7 as na, a8 as ca, a9 as ra, aa as ga, ab as da, ac as Ba, ad as Sa, ae as ya, af as ua, ag as Ta, ah as pa, ai as ka, aj as ha, ak as ma, al as va, am as Ra, an as xa, ao as fa, ap as Na } from "./index.df04b79c.js"; | ||
import { A as ja, al as qa, B as Qa, E as Va, D as Wa, ah as Ya, aj as wa, ak as Ga, am as Ja, H as Xa, P as Za, e as $a, R as ae, ai as ee, ap as se, ag as te, ao as oe, an as le, aa as ie, a6 as ne, a7 as ce, a as re, ar as ge, a9 as de, af as Be, d as Se, c as ye, b as ue, ab as Te, at as pe, L as ke, K as he, J as me, N as ve, O as Re, C as xe, f as fe, r as Ne, q as Ee, x as Ce, w as Le, p as Me, aq as De, a1 as Pe, ax as Fe, a0 as Ae, v as Ie, u as be, n as Oe, m as _e, j as He, o as ze, l as Ke, y as Ue, t as je, W as qe, g as Qe, k as Ve, a2 as We, s as Ye, z as we, aw as Ge, aB as Je, ay as Xe, Z as Ze, _ as $e, X as as, az as es, aA as ss, h as ts, I as os, Y as ls, i as is, S as ns, a3 as cs, G as rs, U as gs, V as ds, Q as Bs, F as Ss, T as ys, $ as us, a8 as Ts, ac as ps, as as ks, M as hs, a5 as ms, a4 as vs, ad as Rs, ae as xs, av as fs, aC as Ns, au as Es } from "./index.df04b79c.js"; | ||
import "lit"; | ||
@@ -31,26 +31,26 @@ import "lit/decorators.js"; | ||
Rect: g, | ||
hotkey: B, | ||
getShapeBlockHitBox: d, | ||
hotkey: d, | ||
getShapeBlockHitBox: B, | ||
getBlockById: S, | ||
getParentBlockById: y, | ||
getSiblingsById: T, | ||
getPreviousSiblingById: u, | ||
getSiblingsById: u, | ||
getPreviousSiblingById: T, | ||
getNextSiblingById: p, | ||
getNextBlock: k, | ||
getPreviousBlock: v, | ||
getDefaultPageBlock: h, | ||
getContainerByModel: R, | ||
getBlockElementByModel: m, | ||
getPreviousBlock: h, | ||
getDefaultPageBlock: m, | ||
getContainerByModel: v, | ||
getBlockElementByModel: R, | ||
getStartModelBySelection: x, | ||
getRichTextByModel: f, | ||
getModelsByRange: E, | ||
getModelByElement: N, | ||
getDOMRectByLine: L, | ||
getCurrentRange: M, | ||
getQuillIndexByNativeSelection: P, | ||
getTextNodeBySelectedBlock: C, | ||
getAllBlocks: D, | ||
getModelsByRange: N, | ||
getModelByElement: E, | ||
getDOMRectByLine: C, | ||
getCurrentRange: L, | ||
getQuillIndexByNativeSelection: M, | ||
getTextNodeBySelectedBlock: D, | ||
getAllBlocks: P, | ||
BlockElement: F, | ||
BlockChildrenContainer: I, | ||
isPageTitle: A, | ||
BlockChildrenContainer: A, | ||
isPageTitle: I, | ||
isInput: b, | ||
@@ -61,5 +61,5 @@ initMouseEventHandlers: O, | ||
focusNextBlock: z, | ||
resetNativeSelection: j, | ||
focusRichTextByOffset: K, | ||
focusRichTextStart: U, | ||
resetNativeSelection: K, | ||
focusRichTextByOffset: U, | ||
focusRichTextStart: j, | ||
isNoneSelection: q, | ||
@@ -87,20 +87,20 @@ isCollapsedSelection: Q, | ||
almostEqual: ga, | ||
createEvent: Ba, | ||
noop: da, | ||
createEvent: da, | ||
noop: Ba, | ||
sleep: Sa, | ||
throttle: ya, | ||
clamp: Ta, | ||
TDShapeType: ua, | ||
clamp: ua, | ||
TDShapeType: Ta, | ||
ColorStyle: pa, | ||
SizeStyle: ka, | ||
DashStyle: va, | ||
FontSize: ha, | ||
AlignStyle: Ra, | ||
FontStyle: ma, | ||
DashStyle: ha, | ||
FontSize: ma, | ||
AlignStyle: va, | ||
FontStyle: Ra, | ||
TLBoundsEdge: xa, | ||
TLBoundsCorner: fa, | ||
SnapPoints: Ea | ||
SnapPoints: Na | ||
}, Symbol.toStringTag, { value: "Module" })); | ||
export { | ||
Ua as ALLOW_DEFAULT, | ||
ja as ALLOW_DEFAULT, | ||
qa as AlignStyle, | ||
@@ -127,61 +127,74 @@ Qa as BLOCK_ID_ATTR, | ||
re as asyncFocusRichText, | ||
ge as caretRangeFromPoint, | ||
ge as bindCommonHotkey, | ||
de as caretRangeFromPoint, | ||
Be as clamp, | ||
de as convertToDivider, | ||
Se as convertToList, | ||
ye as convertToParagraph, | ||
Se as convertToDivider, | ||
ye as convertToList, | ||
ue as convertToParagraph, | ||
Te as createEvent, | ||
za as default, | ||
ue as focusNextBlock, | ||
pe as focusPreviousBlock, | ||
ke as focusRichText, | ||
pe as deleteModels, | ||
ke as focusNextBlock, | ||
he as focusPreviousBlock, | ||
me as focusRichText, | ||
ve as focusRichTextByOffset, | ||
he as focusRichTextStart, | ||
Re as getAllBlocks, | ||
me as getBlockById, | ||
xe as getBlockElementByModel, | ||
fe as getContainerByModel, | ||
Ee as getCurrentRange, | ||
Ne as getDOMRectByLine, | ||
Le as getDefaultPageBlock, | ||
Me as getFirstTextNode, | ||
Pe as getLastTextNode, | ||
Ce as getModelByElement, | ||
De as getModelsByRange, | ||
Fe as getNextBlock, | ||
Ie as getNextSiblingById, | ||
Ae as getParentBlockById, | ||
be as getPreviousBlock, | ||
Oe as getPreviousSiblingById, | ||
_e as getQuillIndexByNativeSelection, | ||
He as getRichTextByModel, | ||
ze as getSelectInfo, | ||
je as getShapeBlockHitBox, | ||
Ke as getSiblingsById, | ||
Ue as getSplicedTitle, | ||
qe as getStartModelBySelection, | ||
Qe as getTextNodeBySelectedBlock, | ||
Ve as handleNativeRangeClick, | ||
We as handleNativeRangeDblClick, | ||
Ye as handleNativeRangeDragMove, | ||
we as hotkey, | ||
Ge as initMouseEventHandlers, | ||
Je as isBlankArea, | ||
Xe as isCollapsedAtBlockStart, | ||
Ze as isCollapsedSelection, | ||
$e as isEmbed, | ||
as as isInput, | ||
es as isMultiBlockRange, | ||
ss as isMultiLineRange, | ||
ts as isNoneSelection, | ||
os as isPageTitle, | ||
ls as isRangeSelection, | ||
is as leftFirstSearchLeafNodes, | ||
ns as matchFlavours, | ||
cs as noop, | ||
rs as resetNativeSelection, | ||
gs as restoreSelection, | ||
Bs as saveBlockSelection, | ||
ds as sleep, | ||
Ss as throttle | ||
Re as focusRichTextStart, | ||
xe as getAllBlocks, | ||
fe as getBlockById, | ||
Ne as getBlockElementByModel, | ||
Ee as getContainerByModel, | ||
Ce as getCurrentRange, | ||
Le as getDOMRectByLine, | ||
Me as getDefaultPageBlock, | ||
De as getDragDirection, | ||
Pe as getFirstTextNode, | ||
Fe as getFormat, | ||
Ae as getLastTextNode, | ||
Ie as getModelByElement, | ||
be as getModelsByRange, | ||
Oe as getNextBlock, | ||
_e as getNextSiblingById, | ||
He as getParentBlockById, | ||
ze as getPreviousBlock, | ||
Ke as getPreviousSiblingById, | ||
Ue as getQuillIndexByNativeSelection, | ||
je as getRichTextByModel, | ||
qe as getSelectInfo, | ||
Qe as getShapeBlockHitBox, | ||
Ve as getSiblingsById, | ||
We as getSplicedTitle, | ||
Ye as getStartModelBySelection, | ||
we as getTextNodeBySelectedBlock, | ||
Ge as handleBackspace, | ||
Je as handleBlockSelectionBatchDelete, | ||
Xe as handleFormat, | ||
Ze as handleNativeRangeClick, | ||
$e as handleNativeRangeDblClick, | ||
as as handleNativeRangeDragMove, | ||
es as handleNativeSelectAll, | ||
ss as handleSelectAll, | ||
ts as hotkey, | ||
os as initMouseEventHandlers, | ||
ls as isBlankArea, | ||
is as isCollapsedAtBlockStart, | ||
ns as isCollapsedSelection, | ||
cs as isEmbed, | ||
rs as isInput, | ||
gs as isMultiBlockRange, | ||
ds as isMultiLineRange, | ||
Bs as isNoneSelection, | ||
Ss as isPageTitle, | ||
ys as isRangeSelection, | ||
us as leftFirstSearchLeafNodes, | ||
Ts as matchFlavours, | ||
ps as noop, | ||
ks as removeCommonHotKey, | ||
hs as resetNativeSelection, | ||
ms as restoreSelection, | ||
vs as saveBlockSelection, | ||
Rs as sleep, | ||
xs as throttle, | ||
fs as transformBlock, | ||
Ns as tryUpdateGroupSize, | ||
Es as updateSelectedTextType | ||
}; |
{ | ||
"name": "@blocksuite/blocks", | ||
"version": "0.3.0-20221225004222-eacf37a", | ||
"version": "0.3.0-20221225012015-8e5d6f5", | ||
"description": "Default BlockSuite editable blocks.", | ||
@@ -11,3 +11,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@blocksuite/store": "0.3.0-20221225004222-eacf37a", | ||
"@blocksuite/store": "0.3.0-20221225012015-8e5d6f5", | ||
"@tldraw/intersect": "^1.8.0", | ||
@@ -22,3 +22,3 @@ "highlight.js": "^11.7.0", | ||
"exports": { | ||
"./style": "./dist/style.css", | ||
"./dist/*": "./dist/*", | ||
"./src/*": "./dist/*.js", | ||
@@ -25,0 +25,0 @@ ".": "./dist/index.js", |
@@ -114,2 +114,3 @@ import type { BaseBlockModel, Page } from '@blocksuite/store'; | ||
flavour: 'affine:code', | ||
type: 'code', | ||
}, | ||
@@ -116,0 +117,0 @@ ]; |
import type { Page } from '@blocksuite/store'; | ||
import type { Quill } from 'quill'; | ||
import { matchFlavours } from './std.js'; | ||
import { matchFlavours, sleep } from './std.js'; | ||
import type { ExtendedModel } from './types.js'; | ||
// XXX: workaround quill lifecycle issue | ||
export function asyncFocusRichText(page: Page, id: string) { | ||
setTimeout(() => { | ||
const adapter = page.richTextAdapters.get(id); | ||
adapter?.quill.focus(); | ||
}); | ||
export async function asyncFocusRichText(page: Page, id: string) { | ||
await sleep(); | ||
const adapter = page.richTextAdapters.get(id); | ||
adapter?.quill.focus(); | ||
} | ||
@@ -13,0 +12,0 @@ |
@@ -360,3 +360,5 @@ import type { BaseBlockModel } from '@blocksuite/store'; | ||
if (!blockElement) { | ||
throw new Error('Failed to get block element'); | ||
throw new Error( | ||
'Failed to get block element, block id: ' + selectedBlock.id | ||
); | ||
} | ||
@@ -363,0 +365,0 @@ const richText = blockElement.querySelector('rich-text'); |
@@ -38,2 +38,5 @@ import { customElement, property, query, state } from 'lit/decorators.js'; | ||
@query('.lang-container code-block-button') | ||
langSelectionButton!: HTMLElement; | ||
@state() | ||
@@ -62,7 +65,4 @@ showLangList = 'hidden'; | ||
private onClick() { | ||
return () => { | ||
window.setTimeout(() => { | ||
this.showLangList = 'visible'; | ||
}, 0); | ||
}; | ||
this.showLangList = 'visible'; | ||
this.langSelectionButton.classList.add('clicked'); | ||
} | ||
@@ -76,7 +76,6 @@ | ||
<div class="container"> | ||
<div class="lang-container has-tool-tip" @click=${this.onClick()}> | ||
<div class="lang-container" @click=${this.onClick}> | ||
<code-block-button width="101px" height="24px"> | ||
${this.model.language} ${ArrowDownIcon} | ||
</code-block-button> | ||
<tool-tip inert role="tooltip">switch language</tool-tip> | ||
</div> | ||
@@ -91,2 +90,3 @@ <lang-list | ||
this.showLangList = 'hidden'; | ||
this.langSelectionButton.classList.remove('clicked'); | ||
}} | ||
@@ -93,0 +93,0 @@ ></lang-list> |
@@ -7,2 +7,3 @@ import { BaseBlockModel, IBaseBlockProps, Page } from '@blocksuite/store'; | ||
flavour = 'affine:code' as const; | ||
type = 'code' as const; | ||
tag = literal`affine-code`; | ||
@@ -9,0 +10,0 @@ |
@@ -251,3 +251,5 @@ import { customElement, property, query, state } from 'lit/decorators.js'; | ||
const target = e.target as HTMLElement; | ||
if (!target.closest('lang-list')?.closest(`[data-block-id="${this.id}"]`)) { | ||
if ( | ||
!target.closest('.container')?.closest(`[data-block-id="${this.id}"]`) | ||
) { | ||
this.dispose(); | ||
@@ -254,0 +256,0 @@ } |
@@ -8,2 +8,3 @@ import type { BaseBlockModel, Page } from '@blocksuite/store'; | ||
BulletedListIcon, | ||
CodeIcon, | ||
H1Icon, | ||
@@ -25,2 +26,3 @@ H2Icon, | ||
} from './icons.js'; | ||
import { CodeBlockModel } from '../../code-block/index.js'; | ||
@@ -53,3 +55,3 @@ export const paragraphButtons = [ | ||
{ flavour: 'affine:list', type: 'todo', name: 'To-do List', icon: TodoIcon }, | ||
// { flavour: 'affine:', type: 'code', name: 'Code Block', icon: CodeIcon }, | ||
{ flavour: 'affine:code', type: 'code', name: 'Code Block', icon: CodeIcon }, | ||
{ | ||
@@ -81,2 +83,3 @@ flavour: 'affine:paragraph', | ||
activeWhen: (format: Record<string, unknown>) => 'bold' in format, | ||
showWhen: (models: BaseBlockModel[]) => noneCodeBlockSelected(models), | ||
action: ({ page }: ActionProps) => { | ||
@@ -91,2 +94,3 @@ handleFormat(page, 'bold'); | ||
activeWhen: (format: Record<string, unknown>) => 'italic' in format, | ||
showWhen: (models: BaseBlockModel[]) => noneCodeBlockSelected(models), | ||
action: ({ page }: ActionProps) => { | ||
@@ -101,2 +105,3 @@ handleFormat(page, 'italic'); | ||
activeWhen: (format: Record<string, unknown>) => 'underline' in format, | ||
showWhen: (models: BaseBlockModel[]) => noneCodeBlockSelected(models), | ||
action: ({ page }: ActionProps) => { | ||
@@ -111,2 +116,3 @@ handleFormat(page, 'underline'); | ||
activeWhen: (format: Record<string, unknown>) => 'strike' in format, | ||
showWhen: (models: BaseBlockModel[]) => noneCodeBlockSelected(models), | ||
action: ({ page }: ActionProps) => { | ||
@@ -121,2 +127,3 @@ handleFormat(page, 'strike'); | ||
activeWhen: (format: Record<string, unknown>) => 'code' in format, | ||
showWhen: (models: BaseBlockModel[]) => noneCodeBlockSelected(models), | ||
action: ({ page }: ActionProps) => { | ||
@@ -132,3 +139,4 @@ handleFormat(page, 'code'); | ||
// Only can show link button when selection is in one line paragraph | ||
showWhen: (models: BaseBlockModel[]) => models.length === 1, | ||
showWhen: (models: BaseBlockModel[]) => | ||
models.length === 1 && noneCodeBlockSelected(models), | ||
action: ({ page, abortController, format }: ActionProps) => { | ||
@@ -142,1 +150,5 @@ createLink(page); | ||
]; | ||
export const noneCodeBlockSelected = (models: BaseBlockModel[]) => { | ||
return !models.every(model => model instanceof CodeBlockModel); | ||
}; |
@@ -140,5 +140,5 @@ import { BaseBlockModel, Page, Signal } from '@blocksuite/store'; | ||
// Already in the target format, convert back to text | ||
const { flavour: defaultParagraph, type: defaultType } = | ||
const { flavour: defaultFlavour, type: defaultType } = | ||
paragraphButtons[0]; | ||
updateSelectedTextType(defaultParagraph, defaultType, this.page); | ||
updateSelectedTextType(defaultFlavour, defaultType, this.page); | ||
this.paragraphType = defaultType; | ||
@@ -149,2 +149,3 @@ return; | ||
this.paragraphType = type; | ||
this.positionUpdated.emit(); | ||
}} | ||
@@ -183,25 +184,24 @@ > | ||
const formatItems = html` | ||
${formatButtons | ||
.filter(({ showWhen = () => true }) => showWhen(this.models)) | ||
.map( | ||
({ id, name, icon, action, activeWhen }) => html`<format-bar-button | ||
class="has-tool-tip" | ||
data-testid=${id} | ||
?active=${activeWhen(this.format)} | ||
@click=${() => { | ||
action({ | ||
page, | ||
abortController: this.abortController, | ||
format: this.format, | ||
}); | ||
// format state need to update after format | ||
this.format = getFormat(); | ||
}} | ||
> | ||
${icon} | ||
<tool-tip inert role="tooltip">${name}</tool-tip> | ||
</format-bar-button>` | ||
)} | ||
`; | ||
const formatItems = formatButtons | ||
.filter(({ showWhen = () => true }) => showWhen(this.models)) | ||
.map( | ||
({ id, name, icon, action, activeWhen }) => html`<format-bar-button | ||
class="has-tool-tip" | ||
data-testid=${id} | ||
?active=${activeWhen(this.format)} | ||
@click=${() => { | ||
action({ | ||
page, | ||
abortController: this.abortController, | ||
format: this.format, | ||
}); | ||
// format state need to update after format | ||
this.format = getFormat(); | ||
this.positionUpdated.emit(); | ||
}} | ||
> | ||
${icon} | ||
<tool-tip inert role="tooltip">${name}</tool-tip> | ||
</format-bar-button>` | ||
); | ||
@@ -226,3 +226,3 @@ const actionItems = html`<format-bar-button | ||
${formatItems} | ||
<div class="divider"></div> | ||
${formatItems.length ? html`<div class="divider"></div>` : ''} | ||
${actionItems} ${paragraphPanel} | ||
@@ -229,0 +229,0 @@ </div>`; |
@@ -14,5 +14,4 @@ import { Signal } from '@blocksuite/store'; | ||
} from '../../__internal__/utils/index.js'; | ||
import './button'; | ||
import './format-bar-node'; | ||
import type { FormatQuickBar } from './format-bar-node.js'; | ||
import './button.js'; | ||
import './format-bar-node.js'; | ||
@@ -25,3 +24,3 @@ export const showFormatQuickBar = async ({ | ||
}: { | ||
anchorEl: | ||
anchorEl?: | ||
| { | ||
@@ -38,5 +37,3 @@ getBoundingClientRect: () => DOMRect; | ||
const formatQuickBar = document.createElement( | ||
'format-quick-bar' | ||
) as FormatQuickBar; | ||
const formatQuickBar = document.createElement('format-quick-bar'); | ||
formatQuickBar.abortController = abortController; | ||
@@ -50,6 +47,8 @@ const positionUpdatedSignal = new Signal(); | ||
const updatePos = throttle(() => { | ||
const positioningEl = anchorEl ?? getCurrentRange(); | ||
const positioningPoint = | ||
anchorEl instanceof Range | ||
? calcPositionPointByRange(anchorEl, direction) | ||
: anchorEl.getBoundingClientRect(); | ||
positioningEl instanceof Range | ||
? calcPositionPointByRange(positioningEl, direction) | ||
: positioningEl.getBoundingClientRect(); | ||
@@ -61,3 +60,2 @@ // TODO maybe use the editor container as the boundary rect to avoid the format bar being covered by other elements | ||
// Add offset to avoid the quick bar being covered by the window border | ||
const gapY = 5; | ||
@@ -95,13 +93,2 @@ const isBottom = direction.includes('bottom'); | ||
// Handle click outside | ||
const clickAwayListener = (e: MouseEvent) => { | ||
if (e.target === formatQuickBar) { | ||
return; | ||
} | ||
abortController.abort(); | ||
window.removeEventListener('mousedown', clickAwayListener); | ||
}; | ||
window.addEventListener('mousedown', clickAwayListener); | ||
// Handle selection change | ||
@@ -108,0 +95,0 @@ |
@@ -359,4 +359,3 @@ import type { BaseBlockModel, Page } from '@blocksuite/store'; | ||
if (this.state.type === 'native') { | ||
const { anchor, direction, selectedType } = | ||
getNativeSelectionMouseDragInfo(e); | ||
const { direction, selectedType } = getNativeSelectionMouseDragInfo(e); | ||
if (selectedType === 'Caret') { | ||
@@ -367,3 +366,3 @@ // If nothing is selected, then we should not show the format bar | ||
showFormatQuickBar({ anchorEl: anchor, direction }); | ||
showFormatQuickBar({ direction }); | ||
} else if (this.state.type === 'block') { | ||
@@ -461,3 +460,3 @@ // TODO handle block selection | ||
} | ||
showFormatQuickBar({ anchorEl: range, direction: 'center-bottom' }); | ||
showFormatQuickBar({ direction: 'center-bottom' }); | ||
}; | ||
@@ -464,0 +463,0 @@ |
@@ -413,29 +413,29 @@ import type { BaseBlockModel, Page } from '@blocksuite/store'; | ||
hotkey.addListener(H1, () => | ||
updateSelectedTextType('affine:paragraph', 'h1', page) | ||
); | ||
hotkey.addListener(H2, () => | ||
updateSelectedTextType('affine:paragraph', 'h2', page) | ||
); | ||
hotkey.addListener(H3, () => | ||
updateSelectedTextType('affine:paragraph', 'h3', page) | ||
); | ||
hotkey.addListener(H4, () => | ||
updateSelectedTextType('affine:paragraph', 'h4', page) | ||
); | ||
hotkey.addListener(H5, () => | ||
updateSelectedTextType('affine:paragraph', 'h5', page) | ||
); | ||
hotkey.addListener(H6, () => | ||
updateSelectedTextType('affine:paragraph', 'h6', page) | ||
); | ||
hotkey.addListener(NUMBERED_LIST, () => | ||
updateSelectedTextType('affine:list', 'numbered', page) | ||
); | ||
hotkey.addListener(BULLETED, () => | ||
updateSelectedTextType('affine:list', 'bulleted', page) | ||
); | ||
hotkey.addListener(TEXT, () => | ||
updateSelectedTextType('affine:paragraph', 'text', page) | ||
); | ||
hotkey.addListener(H1, () => { | ||
updateSelectedTextType('affine:paragraph', 'h1', page); | ||
}); | ||
hotkey.addListener(H2, () => { | ||
updateSelectedTextType('affine:paragraph', 'h2', page); | ||
}); | ||
hotkey.addListener(H3, () => { | ||
updateSelectedTextType('affine:paragraph', 'h3', page); | ||
}); | ||
hotkey.addListener(H4, () => { | ||
updateSelectedTextType('affine:paragraph', 'h4', page); | ||
}); | ||
hotkey.addListener(H5, () => { | ||
updateSelectedTextType('affine:paragraph', 'h5', page); | ||
}); | ||
hotkey.addListener(H6, () => { | ||
updateSelectedTextType('affine:paragraph', 'h6', page); | ||
}); | ||
hotkey.addListener(NUMBERED_LIST, () => { | ||
updateSelectedTextType('affine:list', 'numbered', page); | ||
}); | ||
hotkey.addListener(BULLETED, () => { | ||
updateSelectedTextType('affine:list', 'bulleted', page); | ||
}); | ||
hotkey.addListener(TEXT, () => { | ||
updateSelectedTextType('affine:paragraph', 'text', page); | ||
}); | ||
hotkey.addListener(SHIFT_UP, e => { | ||
@@ -442,0 +442,0 @@ // TODO expand selection up |
@@ -179,4 +179,3 @@ import type { XYWH } from '../selection-manager.js'; | ||
if (this.isActive) { | ||
const { anchor, direction, selectedType } = | ||
getNativeSelectionMouseDragInfo(e); | ||
const { direction, selectedType } = getNativeSelectionMouseDragInfo(e); | ||
if (selectedType === 'Caret') { | ||
@@ -186,3 +185,3 @@ // If nothing is selected, then we should not show the format bar | ||
} | ||
showFormatQuickBar({ anchorEl: anchor, direction }); | ||
showFormatQuickBar({ direction }); | ||
} else if (this.blockSelectionState.type === 'single') { | ||
@@ -189,0 +188,0 @@ if (!this._frameSelectionState) { |
@@ -1,2 +0,2 @@ | ||
import type { BaseBlockModel, Page, Text } from '@blocksuite/store'; | ||
import { BaseBlockModel, Page, Text } from '@blocksuite/store'; | ||
import { | ||
@@ -30,2 +30,3 @@ almostEqual, | ||
import { DEFAULT_SPACING } from '../edgeless/utils.js'; | ||
import { CodeBlockModel } from '../../code-block/index.js'; | ||
@@ -78,3 +79,25 @@ export function deleteModels(page: Page, models: BaseBlockModel[]) { | ||
export function updateSelectedTextType( | ||
function mergeTextOfBlocks( | ||
page: Page, | ||
models: BaseBlockModel[], | ||
blockProps: Record<string, unknown> | ||
) { | ||
const parent = page.getParent(models[0]); | ||
assertExists(parent); | ||
const index = parent.children.indexOf(models[0]); | ||
const id = page.addBlock(blockProps, parent, index); | ||
const codeBlock = page.getBlockById(id) as CodeBlockModel; | ||
models.forEach(model => { | ||
if (model.text instanceof Text) { | ||
const text = codeBlock.text; | ||
assertExists(text); | ||
text.join(model.text); | ||
text.insert('\n', text.length); | ||
} | ||
page.deleteBlock(model); | ||
}); | ||
} | ||
export async function updateSelectedTextType( | ||
flavour: string, | ||
@@ -87,10 +110,29 @@ type: string, | ||
page.captureSync(); | ||
if (flavour === 'affine:code') { | ||
mergeTextOfBlocks(page, modelsInRange, { flavour: 'affine:code' }); | ||
return; | ||
} | ||
const selectedBlocks = saveBlockSelection(); | ||
let lastNewId: string | null = null; | ||
modelsInRange.forEach(model => { | ||
assertFlavours(model, ['affine:paragraph', 'affine:list']); | ||
assertFlavours(model, ['affine:paragraph', 'affine:list', 'affine:code']); | ||
if (model.flavour === flavour) { | ||
page.updateBlock(model, { type }); | ||
} else { | ||
transformBlock(page, model, flavour, type); | ||
const oldId = model.id; | ||
const newId = transformBlock(page, model, flavour, type); | ||
// Replace selected block id | ||
const blocks = selectedBlocks.filter(block => block.id === oldId); | ||
// Because selectedBlocks maybe contains same block when only select one block, so we need to replace all of them | ||
blocks.forEach(block => { | ||
block.id = newId; | ||
}); | ||
lastNewId = newId; | ||
} | ||
}); | ||
if (lastNewId) { | ||
await asyncFocusRichText(page, lastNewId); | ||
} | ||
restoreSelection(selectedBlocks); | ||
} | ||
@@ -115,3 +157,3 @@ | ||
const id = page.addBlock(blockProps, parent, index); | ||
asyncFocusRichText(page, id); | ||
return id; | ||
} | ||
@@ -170,3 +212,5 @@ | ||
const formatArr = []; | ||
formatArr.push(firstFormat, lastFormat); | ||
!(models[0] instanceof CodeBlockModel) && formatArr.push(firstFormat); | ||
!(models[models.length - 1] instanceof CodeBlockModel) && | ||
formatArr.push(lastFormat); | ||
for (let i = 1; i < models.length - 1; i++) { | ||
@@ -180,2 +224,5 @@ const richText = getRichTextByModel(models[i]); | ||
} | ||
if (models[i] instanceof CodeBlockModel) { | ||
continue; | ||
} | ||
const format = richText.quill.getFormat(0, richText.quill.getLength() - 1); | ||
@@ -245,3 +292,5 @@ formatArr.push(format); | ||
if (isRangeSelection()) { | ||
const models = getModelsByRange(getCurrentRange()); | ||
const models = getModelsByRange(getCurrentRange()).filter(model => { | ||
return !(model instanceof CodeBlockModel); | ||
}); | ||
if (models.length === 1) { | ||
@@ -309,2 +358,3 @@ const richText = getRichTextByModel(models[0]); | ||
// TODO should show format bar after select all | ||
function initQuickBarEventHandlersAfterSelectAll(nearestCommonAncestor: Node) { | ||
@@ -311,0 +361,0 @@ nearestCommonAncestor.addEventListener( |
@@ -73,7 +73,6 @@ import { | ||
* ```ts | ||
* const { selectedType, direction, anchor } = getNativeSelectionMouseDragInfo(e); | ||
* const { selectedType, direction } = getNativeSelectionMouseDragInfo(e); | ||
* if (selectedType === 'Caret') { | ||
* return; | ||
* } | ||
* const rect = anchor.getBoundingClientRect(); | ||
* ``` | ||
@@ -93,3 +92,3 @@ */ | ||
const selectedType: SelectedBlockType = isSelectedNothing ? 'Caret' : 'Text'; | ||
return { selectedType, direction, anchor: curRange }; | ||
return { selectedType, direction }; | ||
} | ||
@@ -96,0 +95,0 @@ |
@@ -9,3 +9,7 @@ // https://github.com/tldraw/tldraw/blob/24cad6959f59f93e20e556d018c391fd89d4ecca/packages/tldraw/src/state/shapes/RectangleUtil/rectangleHelpers.ts | ||
import { getStrokePoints, getStroke } from 'perfect-freehand'; | ||
import PF from 'perfect-freehand'; | ||
import type { getStroke as getStrokeType } from 'perfect-freehand'; | ||
import * as DPF from 'perfect-freehand'; | ||
const getStrokePoints = DPF.getStrokePoints; | ||
const getStroke = PF as unknown as typeof getStrokeType; | ||
@@ -12,0 +16,0 @@ function getRectangleDrawPoints( |
@@ -10,3 +10,7 @@ // https://github.com/tldraw/tldraw/blob/ca91e56b29212897b6c97650f567f7ea3e818d1e/packages/tldraw/src/state/shapes/TriangleUtil/triangleHelpers.ts | ||
import { getStrokePoints, getStroke } from 'perfect-freehand'; | ||
import PF from 'perfect-freehand'; | ||
import type { getStroke as getStrokeType } from 'perfect-freehand'; | ||
import * as DPF from 'perfect-freehand'; | ||
const getStrokePoints = DPF.getStrokePoints; | ||
const getStroke = PF as unknown as typeof getStrokeType; | ||
@@ -13,0 +17,0 @@ export function getTrianglePoints(size: number[], offset = 0, rotation = 0) { |
import * as std from './__internal__/utils/index.js'; | ||
export * from './page-block/utils/index.js'; | ||
export * from './__internal__/utils/index.js'; | ||
export default std; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1783928
35460
+ Added@blocksuite/store@0.3.0-20221225012015-8e5d6f5(transitive)
- Removed@blocksuite/store@0.3.0-20221225004222-eacf37a(transitive)