Skip to content

Commit 1bb5f32

Browse files
authored
feat: add experimental page link mention support (#145)
* feat: add experimental page link mention support * chore: cleanup * chore: make icon url optional * fix: update decorator order, add overrides * chore: remove logs --------- Co-authored-by: janniks <[email protected]>
1 parent 49d4b6d commit 1bb5f32

File tree

7 files changed

+74
-10
lines changed

7 files changed

+74
-10
lines changed

dev/serve.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export default {
2626
// table tester: bd1de400a8b349dc824f4f00e61d0797
2727
// todo tester: 235057194b954a60ace89c052a65d102
2828
// indent tester: 5b494cf668b04197882fe1b66c6ee2a8
29+
// mention tester: f53ddc084206442098cab6b4fa016d94
2930
this.blockMap = await getPageBlocks("2e22de6b770e4166be301490f6ffd420");
3031
},
3132
};

docs/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The `NotionRenderer` component offers a few properties
1616
- [`blockMap`](#blockMap) – required
1717
- [`blockOverrides`](#blockOverrides) – default: `{}`
1818
- [`contentId`](#contentId) – default: `undefined`
19+
- [`decoratorOverrides`](#decoratorOverrides) – default: `{}`
1920
- [`embedAllow`](#embedAllow) – default: `"fullscreen"`
2021
- [`fullPage`](#fullPage) – default: `false`
2122
- [`hideList`](#hideList) – default: `[]`
@@ -52,6 +53,11 @@ blockOverrides: {
5253
If this is `undefined` the _first_ block is rendered.
5354
_Usually the first block contains the rest of the page._
5455

56+
### `decoratorOverrides`: Object
57+
58+
– the Notion text decorators that should be overriden by custom registered Vue components.
59+
A key-value pair Object of Notion decorator names to Vue component names.
60+
5561
### `embedAllow`: String
5662

5763
– the [`allow` feature policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow) for embedded `<iframe>`s (e.g. YouTube videos).
@@ -223,7 +229,7 @@ There are a few required steps to allow Nuxt to work with vue-notion
223229
// nuxt.config.js
224230
export default {
225231
// ...
226-
buildModules: ["vue-notion/nuxt"]
232+
buildModules: ["vue-notion/nuxt"],
227233
};
228234
```
229235

src/blocks/decorator.vue

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
<template>
22
<component
3-
v-if="isPageLink && hasPageLinkOptions"
3+
v-if="decoratorOverrides.hasOwnProperty(decoratorKey)"
4+
:is="decoratorOverrides[decoratorKey]"
5+
v-bind="pass"
6+
/>
7+
<NotionMention
8+
v-else-if="isPageLink && decoratorKey === 'lm'"
9+
:mention="decoratorValue"
10+
v-bind="pass"
11+
/>
12+
<component
13+
v-else-if="isPageLink && hasPageLinkOptions"
414
class="notion-link"
515
v-bind="pageLinkProps(decoratorValue)"
616
:is="pageLinkOptions.component"
717
>
818
{{ pageLinkTitle }}
919
</component>
1020
<a
11-
v-else-if="isPageLink"
21+
v-else-if="isPageLink && typeof decoratorValue === 'string'"
1222
class="notion-link"
1323
:target="pageLinkTarget"
1424
:href="mapPageUrl(decoratorValue)"
@@ -67,11 +77,13 @@
6777

6878
<script>
6979
import { Blockable, blockProps } from "@/lib/blockable";
80+
import NotionMention from "@/blocks/helpers/mention";
7081
7182
export default {
7283
extends: Blockable,
7384
name: "NotionDecorator",
7485
props: { ...blockProps, content: Array },
86+
components: { NotionMention },
7587
computed: {
7688
text() {
7789
return this.content?.[0];

src/blocks/helpers/mention.vue

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<template>
2+
<a :href="mention.href" class="notion-text-mention">
3+
<img
4+
class="notion-text-mention-icon"
5+
v-if="mention.icon_url"
6+
:src="mention.icon_url"
7+
/>
8+
<span class="notion-text-mention-title">{{ mention.title }}</span>
9+
</a>
10+
</template>
11+
12+
<script>
13+
import { Blockable, blockComputed, blockProps } from "@/lib/blockable";
14+
15+
export default {
16+
extends: Blockable,
17+
name: "NotionMention",
18+
props: { ...blockProps, mention: Object },
19+
computed: {
20+
...blockComputed,
21+
},
22+
};
23+
</script>

src/components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export { default as NotionColumnSpacer } from "../blocks/helpers/column-spacer.v
2222
export { default as NotionFigure } from "../blocks/helpers/figure.vue";
2323
export { default as NotionFragment } from "../blocks/helpers/fragment.vue";
2424
export { default as NotionImage } from "../blocks/helpers/image.vue";
25+
export { default as NotionMention } from "../blocks/helpers/mention.vue";
2526
export { default as NotionNestedList } from "../blocks/helpers/nested-list.vue";
2627
export { default as NotionPageHeader } from "../blocks/helpers/page-header.vue";
2728
export { default as NotionPageIcon } from "../blocks/helpers/page-icon.vue";

src/lib/blockable.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const blockProps = {
55
blockOverrides: { type: Object, default: () => ({}) },
66
contentId: { type: String, required: false },
77
contentIndex: { type: Number, default: 0 },
8+
decoratorOverrides: { type: Object, default: () => ({}) },
89
embedAllow: { type: String, default: "fullscreen" },
910
fullPage: { type: Boolean, default: false },
1011
hideList: { type: Array, default: () => [] },
@@ -17,7 +18,7 @@ export const blockProps = {
1718
pageLinkTarget: { type: String, default: "_self" },
1819
prism: { type: Boolean, default: false },
1920
textLinkTarget: { type: String, default: "_blank" },
20-
todo: { type: Boolean, default: false }
21+
todo: { type: Boolean, default: false },
2122
};
2223

2324
export const blockComputed = {
@@ -28,6 +29,7 @@ export const blockComputed = {
2829
blockOverrides: this.blockOverrides,
2930
contentId: this.contentId,
3031
contentIndex: this.contentIndex,
32+
decoratorOverrides: this.decoratorOverrides,
3133
embedAllow: this.embedAllow,
3234
fullPage: this.fullPage,
3335
hideList: this.hideList,
@@ -38,7 +40,7 @@ export const blockComputed = {
3840
mapPageUrl: this.mapPageUrl,
3941
pageLinkOptions: this.pageLinkOptions,
4042
prism: this.prism,
41-
todo: this.todo
43+
todo: this.todo,
4244
};
4345
},
4446
alt() {
@@ -63,7 +65,7 @@ export const blockComputed = {
6365
block_color: this.format?.block_color,
6466
bookmark_icon: this.format?.bookmark_icon,
6567
bookmark_cover: this.format?.bookmark_cover,
66-
display_source: this.format?.display_source
68+
display_source: this.format?.display_source,
6769
};
6870
},
6971
icon() {
@@ -98,7 +100,7 @@ export const blockComputed = {
98100
},
99101
parent() {
100102
return this.blockMap[this.value?.parent_id];
101-
}
103+
},
102104
};
103105

104106
export const Blockable = {
@@ -119,8 +121,8 @@ export const Blockable = {
119121
},
120122
pageLinkProps(id) {
121123
return {
122-
[this.pageLinkOptions?.href || "href"]: this.mapPageUrl(id)
124+
[this.pageLinkOptions?.href || "href"]: this.mapPageUrl(id),
123125
};
124-
}
125-
}
126+
},
127+
},
126128
};

src/styles.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,3 +726,22 @@ img.notion-nav-icon {
726726
flex-direction: column;
727727
padding-left: 1.5em;
728728
}
729+
730+
.notion-text-mention {
731+
text-decoration: none;
732+
}
733+
734+
.notion-text-mention-icon {
735+
padding: 0;
736+
width: 1.2em;
737+
height: 1.2em;
738+
border-radius: 3px;
739+
vertical-align: -0.15em;
740+
margin-right: 0.3em;
741+
}
742+
743+
.notion-text-mention-title {
744+
border-bottom: 0.05em solid solid rgba(55, 55, 55, 1);
745+
font-weight: 500;
746+
flex-shrink: 0;
747+
}

0 commit comments

Comments
 (0)