-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #222 from appwrite/feat-code-block
feat: code block
- Loading branch information
Showing
11 changed files
with
1,066 additions
and
3 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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 @@ | ||
export declare function copy(value: string): Promise<boolean>; |
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,42 @@ | ||
async function securedCopy(value: string) { | ||
try { | ||
await navigator.clipboard.writeText(value); | ||
} catch { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
function unsecuredCopy(value: string) { | ||
const textArea = document.createElement('textarea'); | ||
textArea.value = value; | ||
|
||
// Avoid scrolling to bottom | ||
textArea.style.top = '0'; | ||
textArea.style.left = '0'; | ||
textArea.style.position = 'fixed'; | ||
|
||
document.body.appendChild(textArea); | ||
textArea.focus(); | ||
textArea.select(); | ||
|
||
let success = true; | ||
try { | ||
document.execCommand('copy'); | ||
} catch { | ||
success = false; | ||
} finally { | ||
document.body.removeChild(textArea); | ||
} | ||
|
||
return success; | ||
} | ||
|
||
export async function copy(value: string) { | ||
// securedCopy works only in HTTPS environment. | ||
// unsecuredCopy works in HTTP and only runs if securedCopy fails. | ||
const success = (await securedCopy(value)) || unsecuredCopy(value); | ||
|
||
return success; | ||
} |
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,184 @@ | ||
<script lang="ts"> | ||
import { | ||
createCssVariablesTheme, | ||
createHighlighter, | ||
type BuiltinLanguage, | ||
type PlainTextLanguage, | ||
type HighlighterGeneric, | ||
type BundledLanguage, | ||
type BundledTheme | ||
} from 'shiki'; | ||
import { onMount } from 'svelte'; | ||
import { fade } from 'svelte/transition'; | ||
import { copy } from '../helpers/copy.ts'; | ||
import Button from './button/Button.svelte'; | ||
import Icon from '$lib/Icon.svelte'; | ||
import { IconDuplicate } from '@appwrite.io/pink-icons-svelte'; | ||
import Tooltip from './Tooltip.svelte'; | ||
import Spinner from './Spinner.svelte'; | ||
import Select from './input/Select.svelte'; | ||
type Language = BuiltinLanguage | PlainTextLanguage; | ||
const languages: Language[] = [ | ||
'js', | ||
'javascript', | ||
'dart', | ||
'ts', | ||
'typescript', | ||
'xml', | ||
'html', | ||
'sh', | ||
'md', | ||
'json', | ||
'swift', | ||
'php', | ||
'diff', | ||
'python', | ||
'ruby', | ||
'csharp', | ||
'kotlin', | ||
'java', | ||
'cpp', | ||
'bash', | ||
'powershell', | ||
'cmd', | ||
'yaml', | ||
'text', | ||
'graphql', | ||
'http', | ||
'go', | ||
'py', | ||
'rb', | ||
'cs', | ||
'css', | ||
'groovy', | ||
'ini', | ||
'txt', | ||
'dotenv' | ||
]; | ||
const theme = createCssVariablesTheme({ | ||
name: 'appwrite', | ||
variablePrefix: '--shiki-', | ||
fontStyle: true | ||
}); | ||
export let code: string; | ||
export let lang: Language = 'javascript'; | ||
export let lineNumbers = true; | ||
let tooltipContent = 'Copy'; | ||
let highlighter: HighlighterGeneric<BundledLanguage, BundledTheme>; | ||
let htmlCode: string; | ||
onMount(async () => { | ||
highlighter = await createHighlighter({ | ||
langs: languages, | ||
themes: [theme] | ||
}); | ||
htmlCode = highlightCode(code, lang); | ||
}); | ||
function highlightCode(code: string, lang: Language) { | ||
return highlighter?.codeToHtml(code.trim(), { | ||
lang, | ||
theme: 'appwrite', | ||
transformers: [ | ||
{ | ||
code(node) { | ||
if (lineNumbers) this.addClassToHast(node, 'line-numbers'); | ||
} | ||
} | ||
] | ||
}); | ||
} | ||
async function copyCode() { | ||
const success = await copy(code); | ||
if (success) { | ||
tooltipContent = 'Copied'; | ||
} else { | ||
tooltipContent = 'Failed to copy'; | ||
} | ||
setTimeout(() => { | ||
tooltipContent = 'Copy'; | ||
}, 3000); | ||
} | ||
$: if (lang && highlighter) { | ||
htmlCode = highlightCode(code, lang); | ||
} | ||
</script> | ||
|
||
<header role="generic"> | ||
<p class="lang">{lang}</p> | ||
<div> | ||
<!-- <Select bind:value={lang} options={languages} /> --> | ||
{#key tooltipContent} | ||
<Tooltip> | ||
<Button variant="text" icon size="small" on:click={copyCode}> | ||
<Icon size="small" icon={IconDuplicate} /> | ||
</Button> | ||
<p slot="tooltip">{tooltipContent}</p> | ||
</Tooltip> | ||
{/key} | ||
</div> | ||
</header> | ||
<div class="code-block"> | ||
{#if htmlCode} | ||
<div transition:fade={{ duration: 300 }}> | ||
{@html htmlCode} | ||
</div> | ||
{:else} | ||
<div class="loader"> | ||
<Spinner /> | ||
</div> | ||
{/if} | ||
</div> | ||
|
||
<style lang="scss"> | ||
header { | ||
display: flex; | ||
padding: var(--space-3) var(--space-4); | ||
justify-content: space-between; | ||
align-items: center; | ||
border-radius: var(--border-radius-s) var(--border-radius-s) var(--border-radius-none) | ||
var(--border-radius-none); | ||
border: var(--border-width-s, 1px) solid var(--color-border-neutral); | ||
border-bottom: none; | ||
background: var(--color-bgcolor-neutral-secondary); | ||
.lang { | ||
min-width: var(--icon-size-m); | ||
padding: var(--space-1) var(--space-2); | ||
border-radius: var(--border-radius-xs); | ||
background: var(--color-overlay-on-neutral); | ||
} | ||
} | ||
.code-block { | ||
border-radius: var(--border-radius-none) var(--border-radius-none) var(--border-radius-s) | ||
var(--border-radius-s); | ||
border: var(--border-width-s) solid var(--color-border-neutral); | ||
background: var(--color-bgcolor-neutral-primary); | ||
} | ||
.loader { | ||
padding: var(--space-4) var(--space-6); | ||
} | ||
:global(.shiki .line-numbers .line) { | ||
counter-increment: line; | ||
} | ||
:global(.shiki :not(.line-numbers) .line) { | ||
padding-inline-start: 1rem; | ||
} | ||
:global(.shiki .line-numbers .line:before) { | ||
content: counter(line); | ||
display: inline-block; | ||
text-align: right; | ||
color: #6c6c71; | ||
padding-inline-end: 1rem; | ||
width: 2.5rem; | ||
} | ||
</style> |
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,19 @@ | ||
<script lang="ts"> | ||
import Code from './typography/Code.svelte'; | ||
export let code: string; | ||
export let size: 'small' | 'medium' = 'medium'; | ||
</script> | ||
|
||
<pre class="inline-code"> | ||
<Code {size}>{code?.trim()}</Code> | ||
</pre> | ||
|
||
<style> | ||
.inline-code { | ||
padding: var(--space-0) var(--space-2); | ||
border-radius: var(--border-radius-xxs); | ||
background: var(--color-overlay-on-neutral); | ||
display: inline-flex; | ||
} | ||
</style> |
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,71 @@ | ||
<script context="module" lang="ts"> | ||
import { Code } from '$lib/index.js'; | ||
import type { MetaProps } from '@storybook/addon-svelte-csf'; | ||
const codeExample = ` | ||
import { writable } from 'svelte/store'; | ||
export const count = writable(0); | ||
`; | ||
export const meta: MetaProps = { | ||
title: 'Components/Code', | ||
component: Code, | ||
args: { | ||
code: codeExample, | ||
lineNumbers: true | ||
}, | ||
argTypes: { | ||
lang: { | ||
options: [ | ||
'js', | ||
'javascript', | ||
'dart', | ||
'ts', | ||
'typescript', | ||
'xml', | ||
'html', | ||
'sh', | ||
'md', | ||
'json', | ||
'swift', | ||
'php', | ||
'diff', | ||
'python', | ||
'ruby', | ||
'csharp', | ||
'kotlin', | ||
'java', | ||
'cpp', | ||
'bash', | ||
'powershell', | ||
'cmd', | ||
'yaml', | ||
'text', | ||
'graphql', | ||
'http', | ||
'go', | ||
'py', | ||
'rb', | ||
'cs', | ||
'css', | ||
'groovy', | ||
'ini', | ||
'txt', | ||
'dotenv' | ||
], | ||
control: { type: 'select' } | ||
} | ||
} | ||
}; | ||
</script> | ||
|
||
<script> | ||
import { Story, Template } from '@storybook/addon-svelte-csf'; | ||
</script> | ||
|
||
<Template let:args> | ||
<Code {...args} /> | ||
</Template> | ||
|
||
<Story name="Default" /> |
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,30 @@ | ||
<script context="module" lang="ts"> | ||
import InlineCode from '$lib/InlineCode.svelte'; | ||
import type { MetaProps } from '@storybook/addon-svelte-csf'; | ||
const code = `npm install appwrite`; | ||
export const meta: MetaProps = { | ||
title: 'Components/InlineCode', | ||
component: InlineCode, | ||
args: { | ||
code, | ||
size: `medium` | ||
}, | ||
argTypes: { | ||
size: { | ||
options: ['small', 'medium'], | ||
control: { type: 'select' } | ||
} | ||
} | ||
}; | ||
</script> | ||
|
||
<script> | ||
import { Story } from '@storybook/addon-svelte-csf'; | ||
</script> | ||
|
||
<Story name="Default" let:args> | ||
Example of inline code <InlineCode {...args} /> in a sentence. | ||
</Story> |
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
Oops, something went wrong.