Skip to content

Commit 5f57d9c

Browse files
committed
Link options
1 parent c77f2c0 commit 5f57d9c

File tree

7 files changed

+138
-50
lines changed

7 files changed

+138
-50
lines changed

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ A powerful, user-friendly [Tiptap](https://tiptap.dev) field for [Kirby](https:/
1414
- 🧠 **One method to rule them all** with `tiptapText()` handling [UUID resolution](https://getkirby.com/docs/reference/templates/field-methods/permalinks-to-urls), [smartypants](https://getkirby.com/docs/reference/system/options/smartypants), automatic [inline mode](https://getkirby.com/docs/reference/templates/helpers/kirbytextinline) and more
1515
-**Intuitive drag & drop support** for pages and files with intelligent spacing
1616
- 👀 **Custom field preview** showing formatted text in structure fields
17-
- 🔗 **Improved link handling** with a dialog that automatically picks the right KirbyTag (`(link: )`, `(email: )`, `(file: )`or `(tel: )`) and a paste handler that adds link tags to the selected text
17+
- 🔗 **Improved link handling** with a dialog that allows custom link types and custom fields, automatically picks the right KirbyTag (`(link: )`, `(email: )`, `(file: )`or `(tel: )`) and pre-fills the link and link text fields according to the selected text
1818
- 🌈 **Custom highlights** via a regular expression config option, making it possible to e.g. highlight long words
1919
- 📋 **Abstracted JSON structure** for easy content manipulation with features like `offsetHeadings`
2020

@@ -56,6 +56,20 @@ tiptap:
5656
size: small # small, medium, large, huge or the default auto
5757
spellcheck: false # disable spellcheck
5858
pretty: true # pretty-print JSON in content file
59+
links:
60+
# Set link types in the link dialog
61+
options:
62+
- page
63+
- url
64+
# Add fields to the link dialog
65+
fields:
66+
class:
67+
label: Classes
68+
type: checkboxes
69+
options:
70+
border: Border
71+
shadow: Shadow
72+
rounded: Rounded
5973
required: true
6074
placeholder: My placeholder
6175
default: My default content

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "Kirby Tiptap",
44
"license": "MIT",
55
"type": "kirby-plugin",
6-
"version": "0.0.16",
6+
"version": "0.0.17",
77
"authors": [
88
{
99
"name": "Thomas Günther",

index.js

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@
4343
},
4444
'kirbytags' => function () {
4545
return array_keys($this->kirby()->extensions('tags'));
46-
}
46+
},
47+
'links' => function ($links = []) {
48+
return $links;
49+
},
4750
],
4851
'validations' => [
4952
'minlength' => function ($value) {

src/components/Toolbar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<nav class="k-toolbar tiptap-toolbar" v-if="editor">
33
<template v-for="button in normalizedButtons">
44
<component v-if="!isSeperator(button)" :is="buttonComponents[getComponentName(button)]" :key="getKey(button)"
5-
:editor="editor" :levels="getLevels(button)" :endpoints="endpoints" />
5+
:editor="editor" :levels="getLevels(button)" :links="links" :endpoints="endpoints" />
66
<hr v-else />
77
</template>
88
</nav>

src/components/props.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ export const props = {
1515
inline: Boolean,
1616
highlights: Array,
1717
kirbytags: Array,
18+
links: Object,
1819
endpoints: Object,
1920
}

src/components/toolbarButtons/LinkButton.vue

Lines changed: 101 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@
55
<script>
66
import ToolbarButton from './ToolbarButton.vue';
77
8+
/**
9+
* Normalizes options objects to arrays for the link dialog
10+
*/
11+
function normalizeOptions(options) {
12+
if (Array.isArray(options)) return options;
13+
if (typeof options === "object" && options !== null) {
14+
return Object.entries(options).map(([value, text]) => ({
15+
value,
16+
text
17+
}));
18+
}
19+
return [];
20+
}
21+
822
export default {
923
components: {
1024
ToolbarButton
@@ -14,6 +28,10 @@ export default {
1428
editor: {
1529
type: Object,
1630
required: true
31+
},
32+
links: {
33+
type: Object,
34+
default: () => ({})
1735
}
1836
},
1937
@@ -51,21 +69,23 @@ export default {
5169
text: ''
5270
};
5371
}
54-
5572
if (this.isValidUrl(selectedText)) {
5673
return {
5774
href: selectedText,
5875
text: ''
5976
};
6077
}
61-
6278
return {
6379
href: '',
6480
text: selectedText
6581
};
6682
},
6783
84+
/**
85+
* Opens the link dialog and inserts a KirbyTag on submit
86+
*/
6887
handleLink(editor) {
88+
6989
// Get text from current selection, or empty string if nothing selected
7090
const selectedText = !editor.state.selection.empty
7191
? editor.state.doc.textBetween(
@@ -79,17 +99,7 @@ export default {
7999
this.$panel.dialog.open({
80100
component: 'k-link-dialog',
81101
props: {
82-
fields: {
83-
href: {
84-
label: window.panel.$t("link"),
85-
type: "link",
86-
placeholder: window.panel.$t("url.placeholder")
87-
},
88-
text: {
89-
label: window.panel.$t("link.text"),
90-
type: "text",
91-
}
92-
},
102+
fields: this.linkFields,
93103
value: initialValues,
94104
submitButton: window.panel.$t("insert")
95105
},
@@ -99,47 +109,107 @@ export default {
99109
editor.chain().focus().run();
100110
},
101111
submit: (values) => {
102-
this.$panel.dialog.close();
103-
104-
if (!values.href?.length) {
105-
editor.chain().focus().unsetLink().run();
112+
if (!values.href) {
113+
this.$panel.notification.error(window.panel.$t('error.validation.required'));
106114
return;
107115
}
108116
117+
this.$panel.dialog.close();
118+
109119
// Convert permalinks to UUIDs
110-
const href = values.href
120+
values.href = values.href
111121
.replace("/@/file/", "file://")
112-
.replace("/@/page/", "page://")
122+
.replace("/@/page/", "page://");
113123
114-
let kirbyTag = this.getKirbyTag(href, values.text);
124+
let kirbyTag = this.getKirbyTag(values);
115125
editor.commands.insertContent(kirbyTag);
116126
}
117127
}
118128
});
119129
},
120130
121131
/**
122-
* Converts links to Kirbytags based on link type
132+
* Converts dialog values to a KirbyTag string.
123133
*/
124-
getKirbyTag(href, text) {
134+
getKirbyTag(values) {
135+
const { href, text, ...rest } = values;
136+
let tag = '';
125137
if (href.startsWith('mailto:')) {
126138
const email = href.replace('mailto:', '');
127-
return text
139+
tag = text
128140
? `(email: ${email} text: ${text})`
129141
: `(email: ${email})`;
130-
}
131-
132-
if (href.startsWith('tel:')) {
142+
} else if (href.startsWith('tel:')) {
133143
const phone = href.replace('tel:', '');
134-
return text
144+
tag = text
135145
? `(tel: ${phone} text: ${text})`
136146
: `(tel: ${phone})`;
147+
} else {
148+
tag = text
149+
? `(link: ${href} text: ${text})`
150+
: `(link: ${href})`;
151+
}
152+
153+
// Add all non-empty custom fields (arrays joined with space)
154+
const extraParams = Object.entries(rest)
155+
.filter(([key, value]) =>
156+
value !== '' && value !== false && value !== undefined && value !== null
157+
)
158+
.map(([key, value]) =>
159+
Array.isArray(value)
160+
? `${key}: ${value.join(' ')}`
161+
: `${key}: ${value}`
162+
)
163+
.join(' ');
164+
165+
if (extraParams) {
166+
tag = tag.replace(/\)$/, ` ${extraParams})`);
167+
}
168+
169+
return tag;
170+
},
171+
},
172+
173+
computed: {
174+
/**
175+
* Merges default and custom fields, normalizing options for custom fields.
176+
*/
177+
linkFields() {
178+
179+
// Set up href field with optional options (hah)
180+
const hrefField = {
181+
label: window.panel.$t("link"),
182+
required: true,
183+
type: "link"
184+
};
185+
if (this.links?.options && this.links.options.length > 0) {
186+
hrefField.options = this.links.options;
137187
}
138188
139-
return text
140-
? `(link: ${href} text: ${text})`
141-
: `(link: ${href})`;
189+
const defaults = {
190+
href: hrefField,
191+
text: {
192+
label: window.panel.$t("link.text"),
193+
type: "text"
194+
}
195+
};
196+
197+
// Normalize options for all custom fields
198+
const customFields = Object.fromEntries(
199+
Object.entries(this.links?.fields || {}).map(([name, field]) => {
200+
let normalized = { ...field };
201+
if (field.options) {
202+
normalized.options = normalizeOptions(field.options);
203+
}
204+
return [name, normalized];
205+
})
206+
);
207+
208+
return {
209+
...defaults,
210+
...customFields
211+
};
142212
}
143213
}
144-
}
214+
};
145215
</script>

0 commit comments

Comments
 (0)