links flotantes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Cat /dev/Nulo 2023-04-18 15:14:43 -03:00
parent afd77bbf0a
commit c3bfda6b60
2 changed files with 153 additions and 36 deletions

View file

@ -23,6 +23,7 @@
import Modal from "../components/Modal.svelte";
import PagePicker from "../components/PagePicker.svelte";
import type { WorldY } from "../lib/doc";
import Linking from "./menubar/Linking.svelte";
export let view: EditorView;
export let state: EditorState;
@ -133,46 +134,53 @@ transform: scale(${1 / viewport.scale});
</Modal>
{/if}
<div class="bubble" hidden={state.selection.empty} style={barStyle}>
{#if changingProp === false}
<SimpleMarkItem {view} {state} type={view.state.schema.marks.strong}
><BoldIcon style={svgStyle} /></SimpleMarkItem
>
<SimpleMarkItem {view} {state} type={view.state.schema.marks.em}
><ItalicIcon style={svgStyle} /></SimpleMarkItem
>
<SimpleMarkItem {view} {state} type={view.state.schema.marks.underline}
><UnderlineIcon style={svgStyle} /></SimpleMarkItem
>
<SimpleMarkItem {view} {state} type={view.state.schema.marks.strikethrough}
><StrikethroughIcon style={svgStyle} /></SimpleMarkItem
>
<Button
active={markIsActive(state, view.state.schema.marks.link)}
onClick={startEditingLink}><LinkIcon style={svgStyle} /></Button
>
<Button
active={markIsActive(state, view.state.schema.marks.internal_link)}
onClick={startMakingInternalLink}
><InternalLinkIcon style={svgStyle} /></Button
>
{:else if changingProp.type === "link"}
<input
bind:this={linkInputEl}
type="text"
placeholder="https://"
on:change|preventDefault={onChangeLink}
value={changingProp.url}
/>
<Button title="Borrar enlace" onClick={removeLink}
><CloseIcon style={svgStyle} /></Button
>
{/if}
<div class="floating" style={barStyle}>
<Linking {state} {view} />
<div class="bubble" hidden={state.selection.empty}>
{#if changingProp === false}
<SimpleMarkItem {view} {state} type={view.state.schema.marks.strong}
><BoldIcon style={svgStyle} /></SimpleMarkItem
>
<SimpleMarkItem {view} {state} type={view.state.schema.marks.em}
><ItalicIcon style={svgStyle} /></SimpleMarkItem
>
<SimpleMarkItem {view} {state} type={view.state.schema.marks.underline}
><UnderlineIcon style={svgStyle} /></SimpleMarkItem
>
<SimpleMarkItem
{view}
{state}
type={view.state.schema.marks.strikethrough}
><StrikethroughIcon style={svgStyle} /></SimpleMarkItem
>
<Button
active={markIsActive(state, view.state.schema.marks.link)}
onClick={startEditingLink}><LinkIcon style={svgStyle} /></Button
>
<Button
active={markIsActive(state, view.state.schema.marks.internal_link)}
onClick={startMakingInternalLink}
><InternalLinkIcon style={svgStyle} /></Button
>
{:else if changingProp.type === "link"}
<input
bind:this={linkInputEl}
type="text"
placeholder="https://"
on:change|preventDefault={onChangeLink}
value={changingProp.url}
/>
<Button title="Borrar enlace" onClick={removeLink}
><CloseIcon style={svgStyle} /></Button
>
{/if}
</div>
</div>
<style>
.bubble {
.floating {
display: flex;
flex-direction: column;
position: fixed;
left: 0px;
bottom: 0px;
@ -180,6 +188,12 @@ transform: scale(${1 / viewport.scale});
/* https://wicg.github.io/visual-viewport/examples/fixed-to-keyboard.html */
transform-origin: left bottom;
width: 100%;
}
.bubble {
display: flex;
background: var(--background);
border-top: 1px solid var(--accent-bg);
width: 100%;

View 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>