mirror of
https://github.com/coracle-social/flotilla.git
synced 2025-12-10 19:07:06 +00:00
Create new EditorContent component
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte"
|
import {onMount, onDestroy} from "svelte"
|
||||||
import {writable} from "svelte/store"
|
import {writable} from "svelte/store"
|
||||||
import {EditorContent} from "svelte-tiptap"
|
|
||||||
import {isMobile, preventDefault} from "@lib/html"
|
import {isMobile, preventDefault} from "@lib/html"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import EditorContent from "@lib/components/EditorContent.svelte"
|
||||||
import {makeEditor} from "@app/editor"
|
import {makeEditor} from "@app/editor"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -14,29 +14,35 @@
|
|||||||
|
|
||||||
const {onSubmit, content = ""}: Props = $props()
|
const {onSubmit, content = ""}: Props = $props()
|
||||||
|
|
||||||
export const focus = () => $editor.chain().focus().run()
|
const autofocus = !isMobile
|
||||||
|
|
||||||
const uploading = writable(false)
|
const uploading = writable(false)
|
||||||
|
|
||||||
const uploadFiles = () => $editor!.chain().selectFiles().run()
|
export const focus = () => editor.chain().focus().run()
|
||||||
|
|
||||||
|
const uploadFiles = () => editor.chain().selectFiles().run()
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
if ($uploading) return
|
if ($uploading) return
|
||||||
|
|
||||||
const content = $editor!.getText({blockSeparator: "\n"}).trim()
|
const content = editor.getText({blockSeparator: "\n"}).trim()
|
||||||
const tags = $editor!.storage.nostr.getEditorTags()
|
const tags = editor.storage.nostr.getEditorTags()
|
||||||
|
|
||||||
if (!content) return
|
if (!content) return
|
||||||
|
|
||||||
onSubmit({content, tags})
|
onSubmit({content, tags})
|
||||||
|
|
||||||
$editor!.chain().clearContent().run()
|
editor.chain().clearContent().run()
|
||||||
}
|
}
|
||||||
|
|
||||||
const editor = makeEditor({autofocus: !isMobile, submit, uploading, aggressive: true})
|
const editor = makeEditor({autofocus, submit, uploading, aggressive: true})
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
$editor!.chain().setContent(content).run()
|
editor.chain().setContent(content).run()
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
editor.destroy()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -53,7 +59,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Button>
|
</Button>
|
||||||
<div class="chat-editor flex-grow overflow-hidden">
|
<div class="chat-editor flex-grow overflow-hidden">
|
||||||
<EditorContent editor={$editor} />
|
<EditorContent {editor} />
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
data-tip="{window.navigator.platform.includes('Mac') ? 'cmd' : 'ctrl'}+enter to send"
|
data-tip="{window.navigator.platform.includes('Mac') ? 'cmd' : 'ctrl'}+enter to send"
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
const {url, event} = $props()
|
const {url, event} = $props()
|
||||||
|
|
||||||
const confirm = async () => {
|
const confirm = async () => {
|
||||||
await publishDelete({event, relays: [url]})
|
const snapshot = $state.snapshot(event)
|
||||||
|
|
||||||
|
await publishDelete({event: snapshot, relays: [url]})
|
||||||
|
|
||||||
clearModals()
|
clearModals()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {EditorContent} from "svelte-tiptap"
|
|
||||||
import {writable} from "svelte/store"
|
import {writable} from "svelte/store"
|
||||||
import {randomId} from "@welshman/lib"
|
import {randomId} from "@welshman/lib"
|
||||||
import {createEvent, EVENT_TIME} from "@welshman/util"
|
import {createEvent, EVENT_TIME} from "@welshman/util"
|
||||||
@@ -11,6 +10,7 @@
|
|||||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
import DateTimeInput from "@lib/components/DateTimeInput.svelte"
|
import DateTimeInput from "@lib/components/DateTimeInput.svelte"
|
||||||
|
import EditorContent from "@lib/components/EditorContent.svelte"
|
||||||
import {PROTECTED} from "@app/state"
|
import {PROTECTED} from "@app/state"
|
||||||
import {makeEditor} from "@app/editor"
|
import {makeEditor} from "@app/editor"
|
||||||
import {pushToast} from "@app/toast"
|
import {pushToast} from "@app/toast"
|
||||||
@@ -39,14 +39,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const event = createEvent(EVENT_TIME, {
|
const event = createEvent(EVENT_TIME, {
|
||||||
content: $editor.getText({blockSeparator: "\n"}).trim(),
|
content: editor.getText({blockSeparator: "\n"}).trim(),
|
||||||
tags: [
|
tags: [
|
||||||
["d", randomId()],
|
["d", randomId()],
|
||||||
["title", title],
|
["title", title],
|
||||||
["location", location],
|
["location", location],
|
||||||
["start", dateToSeconds(start).toString()],
|
["start", dateToSeconds(start).toString()],
|
||||||
["end", dateToSeconds(end).toString()],
|
["end", dateToSeconds(end).toString()],
|
||||||
...$editor.storage.nostr.getEditorTags(),
|
...editor.storage.nostr.getEditorTags(),
|
||||||
PROTECTED,
|
PROTECTED,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@@ -89,12 +89,12 @@
|
|||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<div class="relative z-feature flex gap-2 border-t border-solid border-base-100 bg-base-100">
|
<div class="relative z-feature flex gap-2 border-t border-solid border-base-100 bg-base-100">
|
||||||
<div class="input-editor flex-grow overflow-hidden">
|
<div class="input-editor flex-grow overflow-hidden">
|
||||||
<EditorContent editor={$editor} />
|
<EditorContent {editor} />
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
data-tip="Add an image"
|
data-tip="Add an image"
|
||||||
class="center btn tooltip"
|
class="center btn tooltip"
|
||||||
onclick={() => $editor.chain().selectFiles().run()}>
|
onclick={() => editor.chain().selectFiles().run()}>
|
||||||
{#if $uploading}
|
{#if $uploading}
|
||||||
<span class="loading loading-spinner loading-xs"></span>
|
<span class="loading loading-spinner loading-xs"></span>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {writable} from "svelte/store"
|
import {writable} from "svelte/store"
|
||||||
import {EditorContent} from "svelte-tiptap"
|
|
||||||
import {createEvent, THREAD} from "@welshman/util"
|
import {createEvent, THREAD} from "@welshman/util"
|
||||||
import {publishThunk} from "@welshman/app"
|
import {publishThunk} from "@welshman/app"
|
||||||
import {isMobile, preventDefault} from "@lib/html"
|
import {isMobile, preventDefault} from "@lib/html"
|
||||||
@@ -9,6 +8,7 @@
|
|||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
|
import EditorContent from "@lib/components/EditorContent.svelte"
|
||||||
import {pushToast} from "@app/toast"
|
import {pushToast} from "@app/toast"
|
||||||
import {GENERAL, tagRoom, PROTECTED} from "@app/state"
|
import {GENERAL, tagRoom, PROTECTED} from "@app/state"
|
||||||
import {makeEditor} from "@app/editor"
|
import {makeEditor} from "@app/editor"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = $editor.getText({blockSeparator: "\n"}).trim()
|
const content = editor.getText({blockSeparator: "\n"}).trim()
|
||||||
|
|
||||||
if (!content.trim()) {
|
if (!content.trim()) {
|
||||||
return pushToast({
|
return pushToast({
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tags = [
|
const tags = [
|
||||||
...$editor.storage.nostr.getEditorTags(),
|
...editor.storage.nostr.getEditorTags(),
|
||||||
tagRoom(GENERAL, url),
|
tagRoom(GENERAL, url),
|
||||||
["title", title],
|
["title", title],
|
||||||
PROTECTED,
|
PROTECTED,
|
||||||
@@ -90,14 +90,14 @@
|
|||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<div class="note-editor flex-grow overflow-hidden">
|
<div class="note-editor flex-grow overflow-hidden">
|
||||||
<EditorContent editor={$editor} />
|
<EditorContent {editor} />
|
||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</Field>
|
</Field>
|
||||||
<Button
|
<Button
|
||||||
data-tip="Add an image"
|
data-tip="Add an image"
|
||||||
class="tooltip tooltip-left absolute bottom-1 right-2"
|
class="tooltip tooltip-left absolute bottom-1 right-2"
|
||||||
onclick={$editor.commands.selectFiles}>
|
onclick={editor.commands.selectFiles}>
|
||||||
{#if $uploading}
|
{#if $uploading}
|
||||||
<span class="loading loading-spinner loading-xs"></span>
|
<span class="loading loading-spinner loading-xs"></span>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {writable} from "svelte/store"
|
import {writable} from "svelte/store"
|
||||||
import {EditorContent} from "svelte-tiptap"
|
|
||||||
import {isMobile, preventDefault} from "@lib/html"
|
import {isMobile, preventDefault} from "@lib/html"
|
||||||
import {fly, slideAndFade} from "@lib/transition"
|
import {fly, slideAndFade} from "@lib/transition"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
|
import EditorContent from "@lib/components/EditorContent.svelte"
|
||||||
import {publishComment} from "@app/commands"
|
import {publishComment} from "@app/commands"
|
||||||
import {tagRoom, GENERAL, PROTECTED} from "@app/state"
|
import {tagRoom, GENERAL, PROTECTED} from "@app/state"
|
||||||
import {makeEditor} from "@app/editor"
|
import {makeEditor} from "@app/editor"
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
const submit = () => {
|
const submit = () => {
|
||||||
if ($uploading) return
|
if ($uploading) return
|
||||||
|
|
||||||
const content = $editor.getText({blockSeparator: "\n"}).trim()
|
const content = editor.getText({blockSeparator: "\n"}).trim()
|
||||||
const tags = [...$editor.storage.nostr.getEditorTags(), tagRoom(GENERAL, url), PROTECTED]
|
const tags = [...editor.storage.nostr.getEditorTags(), tagRoom(GENERAL, url), PROTECTED]
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
return pushToast({
|
return pushToast({
|
||||||
@@ -41,12 +41,12 @@
|
|||||||
class="card2 sticky bottom-2 z-feature mx-2 mt-4 bg-neutral">
|
class="card2 sticky bottom-2 z-feature mx-2 mt-4 bg-neutral">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="note-editor flex-grow overflow-hidden">
|
<div class="note-editor flex-grow overflow-hidden">
|
||||||
<EditorContent editor={$editor} />
|
<EditorContent {editor} />
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
data-tip="Add an image"
|
data-tip="Add an image"
|
||||||
class="tooltip tooltip-left absolute bottom-1 right-2"
|
class="tooltip tooltip-left absolute bottom-1 right-2"
|
||||||
onclick={$editor.commands.selectFiles}>
|
onclick={editor.commands.selectFiles}>
|
||||||
{#if $uploading}
|
{#if $uploading}
|
||||||
<span class="loading loading-spinner loading-xs"></span>
|
<span class="loading loading-spinner loading-xs"></span>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {asClassComponent} from "svelte/legacy"
|
import {asClassComponent} from "svelte/legacy"
|
||||||
import type {Writable} from "svelte/store"
|
import type {Writable} from "svelte/store"
|
||||||
import {derived, readable} from "svelte/store"
|
import {derived} from "svelte/store"
|
||||||
import {Editor, SvelteNodeViewRenderer} from "svelte-tiptap"
|
import {Editor, SvelteNodeViewRenderer} from "svelte-tiptap"
|
||||||
import {ctx} from "@welshman/lib"
|
import {ctx} from "@welshman/lib"
|
||||||
import type {StampedEvent} from "@welshman/util"
|
import type {StampedEvent} from "@welshman/util"
|
||||||
@@ -44,10 +44,8 @@ export const makeEditor = ({
|
|||||||
submit: () => void
|
submit: () => void
|
||||||
uploading?: Writable<boolean>
|
uploading?: Writable<boolean>
|
||||||
wordCount?: Writable<number>
|
wordCount?: Writable<number>
|
||||||
}) => {
|
}) =>
|
||||||
let setter: (editor: Editor) => void
|
new Editor({
|
||||||
|
|
||||||
const _editor = new Editor({
|
|
||||||
content,
|
content,
|
||||||
autofocus,
|
autofocus,
|
||||||
extensions: [
|
extensions: [
|
||||||
@@ -96,15 +94,7 @@ export const makeEditor = ({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
onUpdate({editor}) {
|
onUpdate({editor}) {
|
||||||
setter?.(editor)
|
|
||||||
wordCount?.set(editor.storage.wordCount.words)
|
wordCount?.set(editor.storage.wordCount.words)
|
||||||
charCount?.set(editor.storage.wordCount.chars)
|
charCount?.set(editor.storage.wordCount.chars)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return readable(_editor, set => {
|
|
||||||
setter = set
|
|
||||||
|
|
||||||
return () => _editor.destroy()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
22
src/lib/components/EditorContent.svelte
Normal file
22
src/lib/components/EditorContent.svelte
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {onDestroy, onMount} from "svelte"
|
||||||
|
|
||||||
|
const {editor} = $props()
|
||||||
|
|
||||||
|
let element: HTMLElement
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (element) {
|
||||||
|
element.append(...(Array.from(editor.options.element.childNodes) as any))
|
||||||
|
editor.setOptions({element})
|
||||||
|
editor.contentElement = element
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
editor.contentElement = null
|
||||||
|
editor.setOptions({element: null})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={element}></div>
|
||||||
Reference in New Issue
Block a user