This commit is contained in:
parent
afd77bbf0a
commit
c3bfda6b60
2 changed files with 153 additions and 36 deletions
|
@ -23,6 +23,7 @@
|
||||||
import Modal from "../components/Modal.svelte";
|
import Modal from "../components/Modal.svelte";
|
||||||
import PagePicker from "../components/PagePicker.svelte";
|
import PagePicker from "../components/PagePicker.svelte";
|
||||||
import type { WorldY } from "../lib/doc";
|
import type { WorldY } from "../lib/doc";
|
||||||
|
import Linking from "./menubar/Linking.svelte";
|
||||||
|
|
||||||
export let view: EditorView;
|
export let view: EditorView;
|
||||||
export let state: EditorState;
|
export let state: EditorState;
|
||||||
|
@ -133,7 +134,9 @@ transform: scale(${1 / viewport.scale});
|
||||||
</Modal>
|
</Modal>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="bubble" hidden={state.selection.empty} style={barStyle}>
|
<div class="floating" style={barStyle}>
|
||||||
|
<Linking {state} {view} />
|
||||||
|
<div class="bubble" hidden={state.selection.empty}>
|
||||||
{#if changingProp === false}
|
{#if changingProp === false}
|
||||||
<SimpleMarkItem {view} {state} type={view.state.schema.marks.strong}
|
<SimpleMarkItem {view} {state} type={view.state.schema.marks.strong}
|
||||||
><BoldIcon style={svgStyle} /></SimpleMarkItem
|
><BoldIcon style={svgStyle} /></SimpleMarkItem
|
||||||
|
@ -144,7 +147,10 @@ transform: scale(${1 / viewport.scale});
|
||||||
<SimpleMarkItem {view} {state} type={view.state.schema.marks.underline}
|
<SimpleMarkItem {view} {state} type={view.state.schema.marks.underline}
|
||||||
><UnderlineIcon style={svgStyle} /></SimpleMarkItem
|
><UnderlineIcon style={svgStyle} /></SimpleMarkItem
|
||||||
>
|
>
|
||||||
<SimpleMarkItem {view} {state} type={view.state.schema.marks.strikethrough}
|
<SimpleMarkItem
|
||||||
|
{view}
|
||||||
|
{state}
|
||||||
|
type={view.state.schema.marks.strikethrough}
|
||||||
><StrikethroughIcon style={svgStyle} /></SimpleMarkItem
|
><StrikethroughIcon style={svgStyle} /></SimpleMarkItem
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
@ -169,10 +175,12 @@ transform: scale(${1 / viewport.scale});
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.bubble {
|
.floating {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
|
@ -180,6 +188,12 @@ transform: scale(${1 / viewport.scale});
|
||||||
/* https://wicg.github.io/visual-viewport/examples/fixed-to-keyboard.html */
|
/* https://wicg.github.io/visual-viewport/examples/fixed-to-keyboard.html */
|
||||||
transform-origin: left bottom;
|
transform-origin: left bottom;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bubble {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
border-top: 1px solid var(--accent-bg);
|
border-top: 1px solid var(--accent-bg);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
103
src/editor/menubar/Linking.svelte
Normal file
103
src/editor/menubar/Linking.svelte
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
<script>
|
||||||
|
// Inspirado en https://collectednotes.com/blog/zettelkasten
|
||||||
|
|
||||||
|
import { schema } from "../schema";
|
||||||
|
import LinkIcon from "bootstrap-icons/icons/box-arrow-up-right.svg";
|
||||||
|
import InternalLinkIcon from "bootstrap-icons/icons/folder-symlink.svg";
|
||||||
|
|
||||||
|
const svgStyle = "width: 1em; height: 1em";
|
||||||
|
|
||||||
|
/** @type {import("prosemirror-view").EditorView} */
|
||||||
|
export let view;
|
||||||
|
/** @type {import("prosemirror-state").EditorState} */
|
||||||
|
export let state;
|
||||||
|
|
||||||
|
/** @typedef {object} ExternalLink
|
||||||
|
* @prop {'external'} type
|
||||||
|
* @prop {string} content
|
||||||
|
* @prop {string} href
|
||||||
|
*/
|
||||||
|
/** @typedef {object} InternalLink
|
||||||
|
* @prop {'internal'} type
|
||||||
|
* @prop {string} content
|
||||||
|
* @prop {string} id
|
||||||
|
*/
|
||||||
|
/** @typedef {ExternalLink | InternalLink} Link */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("prosemirror-model").Node} node
|
||||||
|
* @returns {Link[]}
|
||||||
|
*/
|
||||||
|
function getLinks(node) {
|
||||||
|
/** @type {Link[]} */
|
||||||
|
let links = [];
|
||||||
|
node.descendants((node) => {
|
||||||
|
for (const mark of node.marks) {
|
||||||
|
const content = node.textContent;
|
||||||
|
// no repetir marks interrumpidas por otras marks
|
||||||
|
const lastLink = links[links.length - 1] || null;
|
||||||
|
if (
|
||||||
|
lastLink &&
|
||||||
|
("href" in lastLink
|
||||||
|
? lastLink.href === mark.attrs.href
|
||||||
|
: lastLink.id === mark.attrs.id)
|
||||||
|
) {
|
||||||
|
lastLink.content += node.textContent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mark.type === schema.marks.link) {
|
||||||
|
links.push({
|
||||||
|
type: "external",
|
||||||
|
content,
|
||||||
|
href: mark.attrs.href,
|
||||||
|
});
|
||||||
|
} else if (mark.type === schema.marks.internal_link) {
|
||||||
|
links.push({
|
||||||
|
type: "internal",
|
||||||
|
content,
|
||||||
|
id: mark.attrs.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
|
$: links = getLinks(state.selection.$to.parent);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="linking">
|
||||||
|
{#each links as link}
|
||||||
|
<a
|
||||||
|
href={"href" in link ? link.href : link.id}
|
||||||
|
target={link.type === "external" ? "_blank" : null}
|
||||||
|
>
|
||||||
|
{#if link.type === "internal"}
|
||||||
|
<InternalLinkIcon style={svgStyle} />
|
||||||
|
{:else}
|
||||||
|
<LinkIcon style={svgStyle} />
|
||||||
|
{/if}
|
||||||
|
{link.content}
|
||||||
|
</a>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.linking {
|
||||||
|
max-width: 1280px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
background: ButtonFace;
|
||||||
|
color: ButtonText;
|
||||||
|
padding: 0.7em 1em;
|
||||||
|
margin: 0.3em;
|
||||||
|
border-radius: 2em;
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in a new issue