Skip to content

Commit 2cd0eef

Browse files
brc-ddkiaking
andauthored
feat(desc-text): add linkify option (#555) (#556)
close #555 --------- Co-authored-by: Kia King Ishii <[email protected]>
1 parent 89a9e8d commit 2cd0eef

File tree

5 files changed

+96
-5
lines changed

5 files changed

+96
-5
lines changed

docs/components/desc.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,20 @@ You may also use `:pre-wrap` to preserve line breaks in the text. This is especi
246246
</SDesc>
247247
```
248248

249+
By specifying `:linkify`, the component will automatically convert URLs and email addresses in the text into clickable links. It will not convert any Markdown syntax or raw HTML tags. Note that this feature only works when passing in text as a prop through `:value`.
250+
251+
```vue-html
252+
<SDesc cols="2" gap="24">
253+
<SDescItem span="1">
254+
<SDescLabel value="About" />
255+
<SDescText
256+
linkify
257+
value="Clickable link: https://example.com"
258+
/>
259+
</SDescItem>
260+
</SDesc>
261+
```
262+
249263
## Number value
250264

251265
Use `<SDescNumber>` which is a specialized version of `<SDescText>` that is tailored to display numbers. It provides additional styling and formatting options that are not available in `<SDescText>`.

lib/components/SDescText.vue

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script setup lang="ts">
2+
import { useLinkifyIt } from 'sefirot/composables/Markdown'
23
import { computed } from 'vue'
34
import { useHasSlotContent } from '../composables/Utils'
45
import SDescEmpty from './SDescEmpty.vue'
@@ -7,6 +8,7 @@ const props = defineProps<{
78
value?: string | null
89
lineClamp?: string | number
910
preWrap?: boolean
11+
linkify?: boolean
1012
}>()
1113
1214
const classes = computed(() => [
@@ -17,14 +19,23 @@ const classes = computed(() => [
1719
const hasSlot = useHasSlotContent()
1820
1921
const lineClamp = computed(() => props.lineClamp ?? 'none')
22+
23+
const linkifyIt = useLinkifyIt()
24+
25+
const _value = computed(() => {
26+
if (props.linkify) {
27+
return linkifyIt(props.value ?? '')
28+
}
29+
return props.value
30+
})
2031
</script>
2132

2233
<template>
23-
<div v-if="hasSlot || value" class="SDescText" :class="classes">
24-
<div class="value">
25-
<slot v-if="hasSlot" />
26-
<template v-else>{{ value }}</template>
34+
<div v-if="hasSlot || _value" class="SDescText" :class="classes">
35+
<div class="value" v-if="hasSlot">
36+
<slot />
2737
</div>
38+
<div class="value" v-else v-html="_value" />
2839
</div>
2940
<SDescEmpty v-else />
3041
</template>

lib/composables/Markdown.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ export function useMarkdown(options: UseMarkdownOptions = {}): UseMarkdown {
3232
}
3333
}
3434

35+
export function useLinkifyIt() {
36+
const md = new MarkdownIt('zero', { linkify: true })
37+
md.enable('linkify')
38+
39+
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
40+
const token = tokens[idx]
41+
token.attrSet('target', '_blank')
42+
token.attrSet('rel', 'noreferrer')
43+
return self.renderToken(tokens, idx, options)
44+
}
45+
46+
return (source: string) => md.renderInline(source)
47+
}
48+
3549
export interface UseLink {
3650
addListeners(): void
3751
removeListeners(): void
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<script setup lang="ts">
2+
import SDesc from 'sefirot/components/SDesc.vue'
3+
import SDescItem from 'sefirot/components/SDescItem.vue'
4+
import SDescLabel from 'sefirot/components/SDescLabel.vue'
5+
import SDescText from 'sefirot/components/SDescText.vue'
6+
7+
const title = 'Components / SDesc / 04. Text'
8+
const docs = '/components/desc'
9+
10+
function state() {
11+
return {
12+
lineClamp: 10,
13+
preWrap: false,
14+
linkify: true
15+
}
16+
}
17+
</script>
18+
19+
<template>
20+
<Story :title="title" :init-state="state" source="Not available" auto-props-disabled>
21+
<template #controls="{ state }">
22+
<HstNumber
23+
title="lineClamp"
24+
v-model="state.lineClamp"
25+
/>
26+
<HstCheckbox
27+
title="preWrap"
28+
v-model="state.preWrap"
29+
/>
30+
<HstCheckbox
31+
title="linkify"
32+
v-model="state.linkify"
33+
/>
34+
</template>
35+
36+
<template #default="{ state }">
37+
<Board :title="title" :docs="docs">
38+
<SDesc cols="2" gap="24">
39+
<SDescItem span="2">
40+
<SDescLabel>Label</SDescLabel>
41+
<SDescText
42+
:line-clamp="state.lineClamp"
43+
:pre-wrap="state.preWrap"
44+
:linkify="state.linkify"
45+
:value="'By specifying the `:linkify`, you can enable the automatic conversion of URLs such as https://globalbrains.com and emails such as [email protected] inside text to clickable links. It will only convert plain URLs and any other Markdown syntax such as [link](https://globalbrains.com) and **Strong Text** will not be parsed.\nWhen enabling `preWrap`, this sentence should have a line break.'"
46+
/>
47+
</SDescItem>
48+
</SDesc>
49+
</Board>
50+
</template>
51+
</Story>
52+
</template>

stories/components/SDesc.04_Avatar_Many.story.vue renamed to stories/components/SDesc.05_Avatar_Many.story.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import SDescAvatar from 'sefirot/components/SDescAvatar.vue'
44
import SDescItem from 'sefirot/components/SDescItem.vue'
55
import SDescLabel from 'sefirot/components/SDescLabel.vue'
66
7-
const title = 'Components / SDesc / 04. Avatar Many'
7+
const title = 'Components / SDesc / 05. Avatar Many'
88
const docs = '/components/desc'
99
1010
const avatars = [

0 commit comments

Comments
 (0)