mirror of
https://github.com/Blah-IM/Weblah.git
synced 2025-05-01 08:41:08 +00:00
fix: rich text rendering
This commit is contained in:
parent
1bbc149536
commit
29fa1988d4
4 changed files with 54 additions and 37 deletions
|
@ -2,6 +2,7 @@
|
||||||
import type { BlahRichText } from '$lib/richText';
|
import type { BlahRichText } from '$lib/richText';
|
||||||
import { tw } from '$lib/tw';
|
import { tw } from '$lib/tw';
|
||||||
import RichTextSpan from './RichTextRenderer/RichTextSpan.svelte';
|
import RichTextSpan from './RichTextRenderer/RichTextSpan.svelte';
|
||||||
|
import PlainTextRenderer from './RichTextRenderer/PlainTextRenderer.svelte';
|
||||||
|
|
||||||
export let content: BlahRichText;
|
export let content: BlahRichText;
|
||||||
let className = '';
|
let className = '';
|
||||||
|
@ -9,20 +10,14 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={tw('rich-text', className)}>
|
<div class={tw('rich-text', className)}>
|
||||||
{#each content as block}
|
<p>
|
||||||
<p>
|
{#each content as span}
|
||||||
{#each block as span}
|
{#if typeof span === 'string'}
|
||||||
{#if typeof span === 'string'}
|
<PlainTextRenderer text={span} />
|
||||||
{#if span === ''}
|
{:else}
|
||||||
<br />
|
{@const [text, attributes] = span}
|
||||||
{:else}
|
<RichTextSpan {text} {attributes} />
|
||||||
{span}
|
{/if}
|
||||||
{/if}
|
{/each}
|
||||||
{:else}
|
</p>
|
||||||
{@const [text, attributes] = span}
|
|
||||||
<RichTextSpan {text} {attributes} />
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</p>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
10
src/lib/components/RichTextRenderer/PlainTextRenderer.svelte
Normal file
10
src/lib/components/RichTextRenderer/PlainTextRenderer.svelte
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let text: string;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each text.split('\n') as segment, idx}
|
||||||
|
{#if idx > 0}
|
||||||
|
<br />
|
||||||
|
{/if}
|
||||||
|
{segment}
|
||||||
|
{/each}
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { BlahRichTextSpanAttributes } from '$lib/richText';
|
import type { BlahRichTextSpanAttributes } from '$lib/richText';
|
||||||
|
import PlainTextRenderer from './PlainTextRenderer.svelte';
|
||||||
|
|
||||||
// From outside to inside, better align this with the RichTextInput
|
// From outside to inside, better align this with the RichTextInput
|
||||||
const renderOrder: (keyof BlahRichTextSpanAttributes)[] = [
|
const renderOrder: (keyof BlahRichTextSpanAttributes)[] = [
|
||||||
|
@ -30,8 +31,10 @@
|
||||||
const nextAttribute = attribute ? (renderOrder[renderOrder.indexOf(attribute) + 1] ?? '') : null;
|
const nextAttribute = attribute ? (renderOrder[renderOrder.indexOf(attribute) + 1] ?? '') : null;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if attribute === '' || !attributes[attribute]}
|
{#if attribute === ''}
|
||||||
{text}
|
<PlainTextRenderer {text} />
|
||||||
|
{:else if !attributes[attribute]}
|
||||||
|
<svelte:self {...$$props} attribute={nextAttribute} />
|
||||||
{:else if attribute === 'link'}
|
{:else if attribute === 'link'}
|
||||||
<a href={attributes.link} target="_blank">
|
<a href={attributes.link} target="_blank">
|
||||||
<svelte:self {...$$props} attribute={nextAttribute} />
|
<svelte:self {...$$props} attribute={nextAttribute} />
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import canonicalize from 'canonicalize';
|
||||||
import type { AttributeMap, Delta } from 'typewriter-editor';
|
import type { AttributeMap, Delta } from 'typewriter-editor';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
@ -18,10 +19,7 @@ export const blahRichTextSpanSchema = z.union([
|
||||||
]);
|
]);
|
||||||
export type BlahRichTextSpan = z.input<typeof blahRichTextSpanSchema>;
|
export type BlahRichTextSpan = z.input<typeof blahRichTextSpanSchema>;
|
||||||
|
|
||||||
export const blahRichTextBlockSchema = z.array(blahRichTextSpanSchema);
|
export const blahRichTextSchema = z.array(blahRichTextSpanSchema);
|
||||||
export type BlahRichTextBlock = z.input<typeof blahRichTextBlockSchema>;
|
|
||||||
|
|
||||||
export const blahRichTextSchema = z.array(blahRichTextBlockSchema);
|
|
||||||
export type BlahRichText = z.input<typeof blahRichTextSchema>;
|
export type BlahRichText = z.input<typeof blahRichTextSchema>;
|
||||||
|
|
||||||
function isObjectEmpty(obj: object) {
|
function isObjectEmpty(obj: object) {
|
||||||
|
@ -48,23 +46,34 @@ function deltaAttributesToBlahRichTextSpanAttributes(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deltaToBlahRichText(delta: Delta): BlahRichText {
|
export function deltaToBlahRichText(delta: Delta): BlahRichText {
|
||||||
const blocks: BlahRichText = [];
|
const spans: BlahRichText = [];
|
||||||
|
|
||||||
let block: BlahRichTextBlock = [];
|
let lastText = '';
|
||||||
for (const op of delta.ops) {
|
let lastAttributes: BlahRichTextSpanAttributes | null = null;
|
||||||
const lines = op.insert?.split('\n');
|
let canonicalizedLastAttributes: string = 'null';
|
||||||
if (!lines) continue;
|
|
||||||
const attributes = deltaAttributesToBlahRichTextSpanAttributes(op.attributes);
|
|
||||||
|
|
||||||
const line = lines.shift();
|
function commitSpan() {
|
||||||
block.push(attributes ? [line, attributes] : line);
|
spans.push(lastAttributes === null ? lastText : [lastText, lastAttributes]);
|
||||||
|
|
||||||
for (const line of lines) {
|
|
||||||
blocks.push(block);
|
|
||||||
block = [];
|
|
||||||
block.push(attributes ? [line, attributes] : line);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocks;
|
for (const op of delta.ops) {
|
||||||
|
// Not sure in what cases op.insert would not be a string, but let's be safe
|
||||||
|
if (typeof op.insert !== 'string') continue;
|
||||||
|
|
||||||
|
const attributes = deltaAttributesToBlahRichTextSpanAttributes(op.attributes);
|
||||||
|
const canonicalizedAttributes = canonicalize(attributes) ?? 'null';
|
||||||
|
|
||||||
|
if (canonicalizedAttributes === canonicalizedLastAttributes) {
|
||||||
|
lastText += op.insert;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
commitSpan();
|
||||||
|
lastText = op.insert;
|
||||||
|
lastAttributes = attributes;
|
||||||
|
canonicalizedLastAttributes = canonicalizedAttributes;
|
||||||
|
}
|
||||||
|
commitSpan();
|
||||||
|
|
||||||
|
return spans;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue