Distinguish link types (e.g. External vs Internal) #2479
Replies: 23 comments
-
|
Thanks. First thing that comes to mind is doing this via CSS. Could you use CSS attribute matchers for this? https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors Otherwise, we'd need to look deeper to add support for this |
Beta Was this translation helpful? Give feedback.
-
Yes, that is the goal. We can't match on |
Beta Was this translation helpful? Give feedback.
-
|
I would probably implement this as being a different kind of inline content (custom inline content) so that you can separate the links meaningfully, as I doubt that all that you would need is to visually distinguish them, you'll also likely need different onclick behaviors at a minimum, and could likely need hover-overs. |
Beta Was this translation helpful? Give feedback.
-
if we make customizing default blocks / ic easier, do you think the better approach would be to have a different type of custom inline content - or would it be better to extend the default link with an additional prop? |
Beta Was this translation helpful? Give feedback.
-
|
It depends on how divergent the link ends up becoming. Like if you could list resources & then insert them into the document, and clicking on the resource takes you to a different part of the application. I would say this is different than just a link - I would say that this is more of a "reference" since it has more context about what the link is pointing to (e.g. like a mention). As for configuration for a link, I would expect only to expose:
Even just for understanding that these links are somehow "different" (e.g. iterating the document). I'd make them their own custom inline content. |
Beta Was this translation helpful? Give feedback.
-
For our specific use case all links are gonna behave the same way in terms of redirection, we only need to open actual pages that have a well-defined URL. But it's true that may need popovers or things like that in the future though. Would using a new type of inline content allow for a consistent user experience? E.g. same accessibility features, etc.? |
Beta Was this translation helpful? Give feedback.
-
|
Yep, it should, there shouldn't be anything special about your link type over ours. It can even still render to an |
Beta Was this translation helpful? Give feedback.
-
|
I'm having an additional issue with this approach: when inserting a custom link, I obviously can't use the I need the ability to create and edit internal links in-place, just like normal links. public createLink(url: string, text?: string) {
if (url === "") {
return;
}
const mark = this.pmSchema.mark("link", { href: url });
this.transact((tr) => {
const { from, to } = tr.selection;
if (text) {
tr.insertText(text, from, to).addMark(from, from + text.length, mark);
} else {
tr.setSelection(TextSelection.create(tr.doc, to)).addMark(
from,
to,
mark,
);
}
});
} |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
You can just import it from that package, it is already in your app bundle since we depend on it
inline content is represented as a node in prosemirror (as opposed to a mark), so the editor.insertInlineContent({ type: "InternalLink", props: {"your-props-here": true } }) |
Beta Was this translation helpful? Give feedback.
-
I can't, as it's not stated as an explicit dependency. Technically, it is in the bundle, but the compiler won't allow me to import it (for good reasons).
This doesn't work as the EDIT: Actually it seems like the method does replace the selected content, so this would actually work. |
Beta Was this translation helpful? Give feedback.
-
That's not how packaging/bundling works, if your explicit dependency on @tiptap/pm is compatible with the semver range that we declare, then it will not duplicate the package, there would only be a single version in your project, since they are compatible. This is kind of the point of packaging as deps, for de-duplication. insertInlineContent removes the word because it would be expected to be supplied in a editor.insertInlineContent({ type: "InternalLink", props: {"your-props-here": true }, content: editor.getSelectedText() }) |
Beta Was this translation helpful? Give feedback.
-
You're right, I'm mixing things up. The need to pinpoint the exact same version as BlockNote and manually update it every time still stands though, it's not a robust solution.
Yes that's what's I'm doing ; but that I want to know is the following: Let's say we have a custom link in the editor. The user puts their cursor somewhere in the middle of it, we show the toolbar, and they edit the link's target. How would that work? The user did NOT select anything, we just happen to have the cursor in the middle of the inline content. And given we can't update an existing inline content, I'm not sure what the right approach would be. |
Beta Was this translation helpful? Give feedback.
-
We don't pin to specific version for exactly this reason: BlockNote/packages/core/package.json Line 93 in c114ff0
Our inline content API could definitely be better. But, the idea would be to update the link by replacing the node content. You can do this in a couple of ways:
|
Beta Was this translation helpful? Give feedback.
-
Oh, would the main package's version and BlockNote's dependency version automatically align? I wasn't aware of such a feature.
Wouldn't this have an impact on UI? Like, displaying the text as selected even though the user did nothing to actually select the whole link's label? |
Beta Was this translation helpful? Give feedback.
-
Yea, they would, that is what
you can move a selection for an operation, and then put it back. Like: editor.transact(tr=>{
// cache the selection for restoration
const { from, to } = tr.selection
// set to beginning
tr.setSelection(TextSelection.create(0, 1))
// insert based on the selection
tr.insertText("1234")
// put back the selection
tr.setSelection(TextSelection.create(from, to))
})It depends on the UX you are going for of course whether you want to restore the selection or not. It can be somewhat surprising that modifying an attribute changes a whole range in some cases |
Beta Was this translation helpful? Give feedback.
-
|
I'll try that, thank you :) |
Beta Was this translation helpful? Give feedback.
-
|
This approach is actually very complex. Let's say the user's cursor is inside a link (internal or external). I have to consider many use cases:
In addition, finding the node is not that simple, as I need to call multiple methods in ProseMirror to find the relevant nodes, especially since the current node may not be the one I want - e.g. a link containing unstyled text, then bold text, then unstyled text ; the cursor in the middle of the bold text, I need to find the first ascending link node from the cursor position, which is more complex. So overall it's a lot of logic for such a simple thing as distinguishing internal/external links. Wouldn't there be a more simple approach than that one? |
Beta Was this translation helpful? Give feedback.
-
Welcome to our world! Yes, it is complex. And BlockNote tries to shield you from this sort of complexity, but we haven't gotten far enough with the inline content API to offer a simpler alternative than this. This is essentially the sort of logic that we'd be doing under the hood on our side to offer an API as simple as createLink & insertInlineContent. |
Beta Was this translation helpful? Give feedback.
-
|
Yes, I'm very aware of the complexities BlockNote is facing, and am very glad it exists :) My question was more on the line of: is there a much more simple alternative for my use case, and do you have any intent to support this kind of things natively in the future? |
Beta Was this translation helpful? Give feedback.
-
|
I think I already addressed that:
Yes we intend to improve this in the future, given the time & funding to complete it |
Beta Was this translation helpful? Give feedback.
-
|
I wasn't sure whether you meant you didn't had the time or you didn't have the intention to go that far. Thanks for clarifying :) I'll keep this issue open as a reference, when the time comes and the API evolves, we can then close it. Thanks again for your help! |
Beta Was this translation helpful? Give feedback.
-
|
Hey! Just chiming in as a +1 to this, we have need for this type of functionality as well. All we really want is to change the render method or props of links, without having to re-implement all the surrounding UX. The need comes from our (admittedly hacky) mobile apps that use web views to render our React app. In order for the apps to properly handle internal links without accidentally opening them in a new tab/subview/external browser, we would need to remove the |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
-
Is your feature request related to a problem? Please describe.
We currently need a very important feature that is not present in BlockNote.
Our documents can contain two types of links: they can either refer to external resources (URLs), or internal resources (documents, wikis, etc.). Inside the editor, these internal links are encoded with a complete URL for ease of use.
We need to make internal and external links visually distinct. The current design would be to add an icon after the label of external links, but this could change in the future.
It's pretty high priority for us, I'm not sure how hard it could be to implement, but if this is doable on your side, it would be very appreciated :)
Describe the solution you'd like
There are multiple ways this could be implemented, the easiest being attaching an
internal: trueproperty to links in BlockNote's AST, that would then be reflected in the DOM (e.g. a data attribute such asdata-blocknote-internal-link). We could then use some CSS based on that and add the icon using the::aftermarker.Describe alternatives you've considered
data-blocknote-*All these have quite a bit of drawbacks, so I think the first solution I described above would be a better trade-off.
Additional context
N/A
Bonus
[ ] I'm a sponsor and would appreciate if you could look into this sooner than later 💖
Beta Was this translation helpful? Give feedback.
All reactions