Skip to content

Commit

Permalink
Review
Browse files Browse the repository at this point in the history
  • Loading branch information
kazcw committed May 22, 2024
1 parent 9ae2e19 commit f0c9856
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 132 deletions.
104 changes: 0 additions & 104 deletions app/gui2/src/components/lexical/FloatingSelectionMenu.vue

This file was deleted.

7 changes: 3 additions & 4 deletions app/gui2/src/components/lexical/MarkdownEditorImpl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import {
import { HeadingNode, QuoteNode, registerRichText } from '@lexical/rich-text'
import { TableCellNode, TableNode, TableRowNode } from '@lexical/table'
import { syncRef } from '@vueuse/core'
import { ref, type ComponentInstance } from 'vue'
import { shallowRef, type ComponentInstance } from 'vue'
const markdown = defineModel<string>({ required: true })
const contentElement = ref<ComponentInstance<typeof LexicalContent>>()
const contentElement = shallowRef<ComponentInstance<typeof LexicalContent>>()
const markdownPlugin: LexicalPlugin = {
nodes: [
Expand Down Expand Up @@ -64,13 +64,12 @@ const { editor } = useLexical(contentElement, 'MarkdownEditor', [
<template>
<div class="MarkdownEditor fullHeight">
<LexicalContent ref="contentElement" class="fullHeight" @wheel.stop @contextmenu.stop />
<SelectionFormattingToolbar v-if="contentElement" :editor="editor" />
<SelectionFormattingToolbar :editor="editor" :editorRoot="contentElement" />
</div>
</template>

<style scoped>
.MarkdownEditor {
position: relative;
}
.fullHeight {
Expand Down
60 changes: 36 additions & 24 deletions app/gui2/src/components/lexical/SelectionFormattingToolbar.vue
Original file line number Diff line number Diff line change
@@ -1,40 +1,52 @@
<script setup lang="ts">
import ToggleIcon from '@/components/ToggleIcon.vue'
import FloatingSelectionMenu from '@/components/lexical/FloatingSelectionMenu.vue'
import { useFormatting } from '@/components/lexical/formatting'
import { useSelectionBounds } from '@/composables/domSelection'
import { flip, offset, useFloating } from '@floating-ui/vue'
import type { MaybeElement } from '@vueuse/core'
import type { LexicalEditor } from 'lexical'
import { onMounted, ref } from 'vue'
import { computed, shallowRef, toRef } from 'vue'
const props = defineProps<{ editor: LexicalEditor }>()
const props = defineProps<{ editor: LexicalEditor; editorRoot: MaybeElement }>()
const { bold, italic, strikethrough } = useFormatting(props.editor)
const editorRoot = ref<HTMLElement>()
const rootElement = shallowRef<HTMLElement>()
onMounted(() => {
editorRoot.value = props.editor.getRootElement() ?? undefined
const { bounds: selectionBounds } = useSelectionBounds(toRef(props, 'editorRoot'))
const virtualElement = computed(() => {
const rect = selectionBounds.value?.toDomRect()
return rect ? { getBoundingClientRect: () => rect } : undefined
})
const { floatingStyles } = useFloating(virtualElement, rootElement, {
placement: 'top-start',
middleware: [
offset({
mainAxis: 4,
alignmentAxis: -2,
}),
flip(),
],
})
const { bold, italic, strikethrough } = useFormatting(props.editor)
</script>

<template>
<FloatingSelectionMenu
v-if="editorRoot"
:selectionElement="editorRoot"
:verticalPaddingPx="4"
:horizontalOffsetPx="-2"
<div
v-if="selectionBounds"
ref="rootElement"
:style="floatingStyles"
class="SelectionFormattingToolbar"
>
<div class="SelectionFormattingToolbar">
<div class="button">
<ToggleIcon v-model="bold" icon="bold" />
</div>
<div class="button">
<ToggleIcon v-model="italic" icon="italic" />
</div>
<div class="button">
<ToggleIcon v-model="strikethrough" icon="strike-through" />
</div>
<div class="button">
<ToggleIcon v-model="bold" icon="bold" />
</div>
<div class="button">
<ToggleIcon v-model="italic" icon="italic" />
</div>
<div class="button">
<ToggleIcon v-model="strikethrough" icon="strike-through" />
</div>
</FloatingSelectionMenu>
</div>
</template>

<style scoped>
Expand Down
42 changes: 42 additions & 0 deletions app/gui2/src/composables/domSelection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { unrefElement, useEvent, useResizeObserver } from '@/composables/events'
import { Rect } from '@/util/data/rect'
import type { MaybeElement } from '@vueuse/core'
import { ref, watch, type Ref } from 'vue'

export function useSelectionBounds(boundingElement: Ref<MaybeElement>) {
const bounds = ref<Rect>()

function getSelectionBounds(selection: Selection, element: Element) {
if (!selection.isCollapsed && element.contains(selection.anchorNode)) {
const domRange = selection.getRangeAt(0)
if (selection.anchorNode === element) {
let inner = element
while (inner.firstElementChild != null) {
inner = inner.firstElementChild as HTMLElement
}
return Rect.FromDomRect(inner.getBoundingClientRect())
} else {
return Rect.FromDomRect(domRange.getBoundingClientRect())
}
} else {
return undefined
}
}

function update() {
const selection = window.getSelection()
const element = unrefElement(boundingElement)
if (selection != null && element != null) {
bounds.value = getSelectionBounds(selection, element)
} else {
bounds.value = undefined
}
}

useEvent(document, 'selectionchange', update)

const size = useResizeObserver(boundingElement)
watch(size, update)

return { bounds }
}
9 changes: 9 additions & 0 deletions app/gui2/src/util/data/rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,15 @@ export class Rect {
reflectXY() {
return new Rect(this.pos.reflectXY(), this.size.reflectXY())
}

toDomRect(): DOMRect {
return DOMRect.fromRect({
x: this.pos.x,
y: this.pos.y,
width: this.size.x,
height: this.size.y,
})
}
}

Rect.Zero = new Rect(Vec2.Zero, Vec2.Zero)
Expand Down

0 comments on commit f0c9856

Please sign in to comment.