-
-
Notifications
You must be signed in to change notification settings - Fork 626
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support code component (#1791)
* feat: support code component * feat: using shiki * types: fix code type * feat(code): optimize logic based on review comment * feat(code): support highlighter-provider * feat(code): fix test cases * feat(code): optimize docs based on review comment * feat(code): optimize according to review comments
- Loading branch information
Showing
34 changed files
with
995 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<template> | ||
<div :class="n()" v-html="highlightedCode"></div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { computed, defineComponent, ref, watch } from 'vue' | ||
import { createNamespace } from '../utils/components' | ||
import { props } from './props' | ||
import { injectHighlighterProvider } from '../highlighter-provider/provide' | ||
import { isFunction } from '@varlet/shared' | ||
const { name, n } = createNamespace('code') | ||
export default defineComponent({ | ||
name, | ||
props, | ||
setup(props) { | ||
const { highlighter, theme } = injectHighlighterProvider() | ||
const highlightedCode = ref<string | undefined>(props.code) | ||
const getTheme = computed(() => props.theme || theme) | ||
isFunction(highlighter?.codeToHtml) && | ||
watch( | ||
() => [props.code, props.language, getTheme.value], | ||
async ([code, lang, theme]) => { | ||
highlightedCode.value = await highlighter.codeToHtml(code || '', { lang, theme }) | ||
}, | ||
{ immediate: true } | ||
) | ||
return { | ||
highlightedCode, | ||
n, | ||
} | ||
}, | ||
}) | ||
</script> | ||
|
||
<style lang="less"> | ||
@import '../styles/common'; | ||
@import './code'; | ||
</style> |
52 changes: 52 additions & 0 deletions
52
packages/varlet-ui/src/code/__tests__/__snapshots__/index.spec.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html | ||
|
||
exports[`test code component props > test code content 1`] = ` | ||
"<div class="var-highlighter-provider"> | ||
<div class="var-code"><pre class="shiki monokai" style="background-color:#272822;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#66D9EF;font-style:italic">function</span><span style="color:#A6E22E"> twoSum</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">nums</span><span style="color:#F8F8F2">, </span><span style="color:#FD971F;font-style:italic">target</span><span style="color:#F8F8F2">) {</span></span> | ||
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> map </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> Map</span><span style="color:#F8F8F2">();</span></span> | ||
<span class="line"><span style="color:#F92672"> for</span><span style="color:#F8F8F2"> (</span><span style="color:#66D9EF;font-style:italic">let</span><span style="color:#F8F8F2"> i </span><span style="color:#F92672">=</span><span style="color:#AE81FF"> 0</span><span style="color:#F8F8F2">; i </span><span style="color:#F92672"><</span><span style="color:#F8F8F2"> nums.length; i</span><span style="color:#F92672">++</span><span style="color:#F8F8F2">) {</span></span> | ||
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> num </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> nums[i];</span></span> | ||
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> theOther </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> target </span><span style="color:#F92672">-</span><span style="color:#F8F8F2"> num;</span></span> | ||
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (map.</span><span style="color:#A6E22E">has</span><span style="color:#F8F8F2">(theOther)) {</span></span> | ||
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> [map.</span><span style="color:#A6E22E">get</span><span style="color:#F8F8F2">(theOther), i];</span></span> | ||
<span class="line"><span style="color:#F8F8F2"> }</span></span> | ||
<span class="line"><span style="color:#F8F8F2"> map.</span><span style="color:#A6E22E">set</span><span style="color:#F8F8F2">(num, i);</span></span> | ||
<span class="line"><span style="color:#F8F8F2"> }</span></span> | ||
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre> | ||
</div> | ||
</div>" | ||
`; | ||
exports[`test code component props > test code lang 1`] = ` | ||
"<div class="var-highlighter-provider"> | ||
<div class="var-code" content="function twoSum(nums, target) { | ||
const map = new Map(); | ||
for (let i = 0; i < nums.length; i++) { | ||
const num = nums[i]; | ||
const theOther = target - num; | ||
if (map.has(theOther)) { | ||
return [map.get(theOther), i]; | ||
} | ||
map.set(num, i); | ||
} | ||
};" lang="javascript"><pre class="shiki nord" style="background-color:#2e3440ff;color:#d8dee9ff" tabindex="0"><code><span class="line"><span></span></span></code></pre> | ||
</div> | ||
</div>" | ||
`; | ||
exports[`test code component props > test code theme 1`] = ` | ||
"<div class="var-highlighter-provider"> | ||
<div class="var-code"><pre class="shiki material-theme" style="background-color:#263238;color:#EEFFFF" tabindex="0"><code><span class="line"><span style="color:#C792EA">function</span><span style="color:#82AAFF"> twoSum</span><span style="color:#89DDFF">(</span><span style="color:#EEFFFF;font-style:italic">nums</span><span style="color:#89DDFF">,</span><span style="color:#EEFFFF;font-style:italic"> target</span><span style="color:#89DDFF">)</span><span style="color:#89DDFF"> {</span></span> | ||
<span class="line"><span style="color:#C792EA"> const</span><span style="color:#EEFFFF"> map</span><span style="color:#89DDFF"> =</span><span style="color:#89DDFF"> new</span><span style="color:#82AAFF"> Map</span><span style="color:#F07178">()</span><span style="color:#89DDFF">;</span></span> | ||
<span class="line"><span style="color:#89DDFF;font-style:italic"> for</span><span style="color:#F07178"> (</span><span style="color:#C792EA">let</span><span style="color:#EEFFFF"> i</span><span style="color:#89DDFF"> =</span><span style="color:#F78C6C"> 0</span><span style="color:#89DDFF">;</span><span style="color:#EEFFFF"> i</span><span style="color:#89DDFF"> <</span><span style="color:#EEFFFF"> nums</span><span style="color:#89DDFF">.</span><span style="color:#EEFFFF">length</span><span style="color:#89DDFF">;</span><span style="color:#EEFFFF"> i</span><span style="color:#89DDFF">++</span><span style="color:#F07178">) </span><span style="color:#89DDFF">{</span></span> | ||
<span class="line"><span style="color:#C792EA"> const</span><span style="color:#EEFFFF"> num</span><span style="color:#89DDFF"> =</span><span style="color:#EEFFFF"> nums</span><span style="color:#F07178">[</span><span style="color:#EEFFFF">i</span><span style="color:#F07178">]</span><span style="color:#89DDFF">;</span></span> | ||
<span class="line"><span style="color:#C792EA"> const</span><span style="color:#EEFFFF"> theOther</span><span style="color:#89DDFF"> =</span><span style="color:#EEFFFF"> target</span><span style="color:#89DDFF"> -</span><span style="color:#EEFFFF"> num</span><span style="color:#89DDFF">;</span></span> | ||
<span class="line"><span style="color:#89DDFF;font-style:italic"> if</span><span style="color:#F07178"> (</span><span style="color:#EEFFFF">map</span><span style="color:#89DDFF">.</span><span style="color:#82AAFF">has</span><span style="color:#F07178">(</span><span style="color:#EEFFFF">theOther</span><span style="color:#F07178">)) </span><span style="color:#89DDFF">{</span></span> | ||
<span class="line"><span style="color:#89DDFF;font-style:italic"> return</span><span style="color:#F07178"> [</span><span style="color:#EEFFFF">map</span><span style="color:#89DDFF">.</span><span style="color:#82AAFF">get</span><span style="color:#F07178">(</span><span style="color:#EEFFFF">theOther</span><span style="color:#F07178">)</span><span style="color:#89DDFF">,</span><span style="color:#EEFFFF"> i</span><span style="color:#F07178">]</span><span style="color:#89DDFF">;</span></span> | ||
<span class="line"><span style="color:#89DDFF"> }</span></span> | ||
<span class="line"><span style="color:#EEFFFF"> map</span><span style="color:#89DDFF">.</span><span style="color:#82AAFF">set</span><span style="color:#F07178">(</span><span style="color:#EEFFFF">num</span><span style="color:#89DDFF">,</span><span style="color:#EEFFFF"> i</span><span style="color:#F07178">)</span><span style="color:#89DDFF">;</span></span> | ||
<span class="line"><span style="color:#89DDFF"> }</span></span> | ||
<span class="line"><span style="color:#89DDFF">};</span></span></code></pre> | ||
</div> | ||
</div>" | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import Code from '..' | ||
import VarCode from '../Code' | ||
import VarHighlighterProvider from '../../highlighter-provider' | ||
import { mount } from '@vue/test-utils' | ||
import { createApp, h } from 'vue' | ||
import { delay } from '../../utils/test' | ||
import { expect, describe, test } from 'vitest' | ||
import { codeToHtml } from 'shiki' | ||
|
||
const code = `function twoSum(nums, target) { | ||
const map = new Map(); | ||
for (let i = 0; i < nums.length; i++) { | ||
const num = nums[i]; | ||
const theOther = target - num; | ||
if (map.has(theOther)) { | ||
return [map.get(theOther), i]; | ||
} | ||
map.set(num, i); | ||
} | ||
};` | ||
|
||
test('test code use', () => { | ||
const app = createApp({}).use(Code) | ||
expect(app.component(Code.name)).toBeTruthy() | ||
}) | ||
|
||
describe('test code component props', () => { | ||
test('test code content', async () => { | ||
const wrapper = mount(VarHighlighterProvider, { | ||
props: { | ||
highlighter: { | ||
codeToHtml, | ||
}, | ||
}, | ||
slots: { | ||
default: () => | ||
h(VarCode, { | ||
code, | ||
language: 'javascript', | ||
theme: 'monokai', | ||
}), | ||
}, | ||
}) | ||
|
||
await delay(300) | ||
expect(wrapper.html()).toMatchSnapshot() | ||
wrapper.unmount() | ||
}) | ||
|
||
test('test code lang', async () => { | ||
const wrapper = mount(VarHighlighterProvider, { | ||
props: { | ||
highlighter: { | ||
codeToHtml, | ||
}, | ||
}, | ||
slots: { | ||
default: () => | ||
h(VarCode, { | ||
content: code, | ||
lang: 'javascript', | ||
}), | ||
}, | ||
}) | ||
|
||
await delay(300) | ||
expect(wrapper.html()).toMatchSnapshot() | ||
wrapper.unmount() | ||
}) | ||
|
||
test('test code theme', async () => { | ||
const wrapper = mount(VarHighlighterProvider, { | ||
props: { | ||
highlighter: { | ||
codeToHtml, | ||
}, | ||
}, | ||
slots: { | ||
default: () => | ||
h(VarCode, { | ||
code, | ||
language: 'javascript', | ||
theme: 'material-theme', | ||
}), | ||
}, | ||
}) | ||
|
||
await delay(300) | ||
expect(wrapper.html()).toMatchSnapshot() | ||
wrapper.unmount() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
:root { | ||
--code-border-radius: 4px; | ||
--code-content-padding: 16px; | ||
} | ||
|
||
.var-code { | ||
border-radius: var(--code-border-radius); | ||
position: relative; | ||
overflow: hidden; | ||
width: 100%; | ||
height: 100%; | ||
|
||
pre { | ||
padding: var(--code-content-padding); | ||
border-radius: var(--code-border-radius); | ||
width: 100%; | ||
height: 100%; | ||
margin: 0; | ||
overflow: auto; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Code | ||
|
||
### Intro | ||
|
||
Code component, used to display code blocks and highlight code syntax. | ||
|
||
### Notes | ||
|
||
- Due to package size considerations, Varlet does not include a built-in code highlighter. If you need to use the code block component, please ensure you use the HighlighterProvider component to customize the highlighter. | ||
- Using the HighlighterProvider component, you can set different highlighters for different code blocks. It is recommended to use [Shiki](https://shiki.style/) as the highlighter, as it has built-in support for `codeToHtml` and offers more flexibility in switching languages and themes. | ||
|
||
### Basic Usage | ||
|
||
```html | ||
<script setup> | ||
import { codeToHtml } from 'shiki' | ||
function createHighlighter() { | ||
return { | ||
codeToHtml, | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<var-highlighter-provider :highlighter="createHighlighter()" theme="nord"> | ||
<var-code code="console.log('varlet')" language="javascript" /> | ||
<var-code code="console.log('varlet')" language="javascript" theme='one-dark' /> | ||
<var-code code="console.log('varlet')" language="javascript" theme='one-dark-pro' /> | ||
</var-highlighter-provider> | ||
</template> | ||
``` | ||
|
||
## API | ||
|
||
### Props | ||
|
||
#### Code Props | ||
|
||
| Prop | Description | Type | Default | | ||
|------------------|--------------------------------------------------------------|----------------|------------------| | ||
| `code` | Code Snippet | _string_ | `-` | | ||
| `language` | Language | _string_ | `-` | | ||
| `theme` | Theme | _string_ | `-` | | ||
|
||
#### HighlighterProvider Props | ||
|
||
| Prop | Description | Type | Default | | ||
|------------------|--------------------------------------------------------------|----------------|------------------| | ||
| `highlighter` | Shader | `Highlighter` | `-` | | ||
| `theme` | Theme | _string_ | `-` | | ||
| `tag` | Tag name | _string_ | `div` | | ||
|
||
|
||
#### Highlighter | ||
|
||
| Prop | Description | Type | Default | | ||
| ------ | ------ | ------ | ------ | | ||
| `codeToHtml` | Callback this function when the content, theme, or language changes, and specify the lang and theme options. It will return an HTML string. | `(code: string, options: CodeToHtmlOptions) => Promise<string>` | `-` | ||
|
||
#### CodeToHtmlOptions | ||
|
||
| Prop | Description | Type | Default | | ||
| ------ | ------ | ------ | ------ | | ||
| `lang` | language | _string_ | `-` | | ||
| `theme` | theme | _string_ | `-` | | ||
|
||
### Slots | ||
|
||
#### HighlighterProvider Slots | ||
|
||
| Name | Description | SlotProps | | ||
| --- | --- | --- | | ||
| `default` | Component content | `-` | | ||
|
||
### Style Variables | ||
|
||
Here are the CSS variables used by the component. Styles can be customized using [StyleProvider](#/en-US/style-provider). | ||
|
||
| Variable | Default | | ||
| --- | --- | | ||
| `--code-border-radius` | `4px` | | ||
| `--code-content-padding` | `16px` | |
Oops, something went wrong.