-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: leaner enhanced-code-block components
- Loading branch information
1 parent
db201bb
commit 7146f36
Showing
15 changed files
with
1,023 additions
and
867 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
53 changes: 53 additions & 0 deletions
53
packages/preprocess-markdown/src/enhance-code-block/ButtonCollapse.svelte
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,53 @@ | ||
<script lang="ts" module> | ||
import type { HTMLLabelAttributes } from 'svelte/elements'; | ||
export interface ButtonCollapseProps extends HTMLLabelAttributes { | ||
id: string; | ||
/** $bindable */ | ||
collapsed: boolean; | ||
} | ||
</script> | ||
|
||
<script lang="ts"> | ||
let { id, collapsed = $bindable(), ...rest }: ButtonCollapseProps = $props(); | ||
</script> | ||
|
||
<label {...rest}> | ||
<input class="codeblock-collapsed sr-only" type="checkbox" bind:checked={collapsed} {id} /> | ||
<span class="sr-only">Collapse</span> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="20" | ||
height="20" | ||
fill="currentcolor" | ||
viewBox="0 0 256 256" | ||
> | ||
<path | ||
d="M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z" | ||
> | ||
</path> | ||
</svg> | ||
</label> | ||
|
||
<style lang="postcss"> | ||
/** copied from CodeBlock.svelte */ | ||
@custom-selector :--fullscreen :has(.codeblock-fullscreen:checked); | ||
@custom-selector :--collapsed :has(.codeblock-collapsed:checked); | ||
@custom-selector :--c-collapsible-not-fullscreen | ||
:global(.codeblock.collapsible:not(:--fullscreen)); | ||
@custom-selector :--c-not-collapsed :global(.codeblock:not(:--collapsed)); | ||
label { | ||
transform: rotate(180deg); | ||
display: none; | ||
transition: transform 150ms ease-out; | ||
:--c-collapsible-not-fullscreen & { | ||
display: block; | ||
} | ||
:--c-not-collapsed & { | ||
transform: rotate(0deg); | ||
} | ||
} | ||
</style> |
95 changes: 95 additions & 0 deletions
95
packages/preprocess-markdown/src/enhance-code-block/ButtonCopy.svelte
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,95 @@ | ||
<script lang="ts" module> | ||
import type { TextResolverInput } from '@svelte-put/copy'; | ||
import type { HTMLButtonAttributes } from 'svelte/elements'; | ||
export interface ButtonCopyProps extends HTMLButtonAttributes { | ||
/** $bindable. The button element. Pass as `trigger` arg to `@svelte-put/copy` */ | ||
trigger?: HTMLButtonElement; | ||
} | ||
/** | ||
* copy code exclude meta lines. Pass as `text` arg to `@svelte-put/copy` | ||
*/ | ||
export function copyCode(input: TextResolverInput<'click'>): string { | ||
const codeNode = input.node.getElementsByTagName('code')[0]; | ||
if (!codeNode) return ''; | ||
let text = ''; | ||
for (const lineNode of codeNode.children) { | ||
// assuming shiki build output and transformers set up at mdsvex.config.js | ||
if ((lineNode as HTMLElement).dataset.lineDiff === '-') continue; | ||
text += (lineNode.textContent || '') + '\n'; | ||
} | ||
return text; | ||
} | ||
</script> | ||
|
||
<script lang="ts"> | ||
import { onMount } from 'svelte'; | ||
import { fade } from 'svelte/transition'; | ||
let { trigger = $bindable(), ...rest }: ButtonCopyProps = $props(); | ||
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined; | ||
let optimistic = $state(false); | ||
function onClick() { | ||
optimistic = true; | ||
} | ||
function onMouseEnter() { | ||
clearTimeout(timeoutId); | ||
} | ||
function onMouseLeave() { | ||
timeoutId = setTimeout(() => { | ||
optimistic = false; | ||
}, 1800); | ||
} | ||
let hydrated = $state(false); | ||
onMount(() => { | ||
hydrated = true; | ||
}); | ||
</script> | ||
|
||
{#if hydrated} | ||
<button | ||
type="button" | ||
bind:this={trigger} | ||
disabled={optimistic} | ||
onmouseleave={onMouseLeave} | ||
onmouseenter={onMouseEnter} | ||
onclick={onClick} | ||
{...rest} | ||
> | ||
<span class="sr-only">Copy</span> | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="20" | ||
height="20" | ||
fill="currentcolor" | ||
viewBox="0 0 256 256" | ||
> | ||
{#if optimistic} | ||
<path | ||
d="M168,152a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,152Zm-8-40H96a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16Zm56-64V216a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V48A16,16,0,0,1,56,32H92.26a47.92,47.92,0,0,1,71.48,0H200A16,16,0,0,1,216,48ZM96,64h64a32,32,0,0,0-64,0ZM200,48H173.25A47.93,47.93,0,0,1,176,64v8a8,8,0,0,1-8,8H88a8,8,0,0,1-8-8V64a47.93,47.93,0,0,1,2.75-16H56V216H200Z" | ||
in:fade={{ duration: 150 }} | ||
> | ||
</path> | ||
{:else} | ||
<path | ||
d="M200,32H163.74a47.92,47.92,0,0,0-71.48,0H56A16,16,0,0,0,40,48V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V48A16,16,0,0,0,200,32Zm-72,0a32,32,0,0,1,32,32H96A32,32,0,0,1,128,32Zm72,184H56V48H82.75A47.93,47.93,0,0,0,80,64v8a8,8,0,0,0,8,8h80a8,8,0,0,0,8-8V64a47.93,47.93,0,0,0-2.75-16H200Z" | ||
in:fade={{ duration: 150 }} | ||
> | ||
</path> | ||
{/if} | ||
</svg> | ||
</button> | ||
{/if} | ||
|
||
<style lang="postcss"> | ||
button { | ||
&:disabled { | ||
/* allow clicking even if already copied */ | ||
cursor: pointer; | ||
} | ||
} | ||
</style> |
100 changes: 100 additions & 0 deletions
100
packages/preprocess-markdown/src/enhance-code-block/ButtonFullScreen.svelte
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,100 @@ | ||
<script lang="ts" module> | ||
import type { HTMLLabelAttributes } from 'svelte/elements'; | ||
export interface ButtonFullScreeenProps extends HTMLLabelAttributes { | ||
id: string; | ||
codeblock?: HTMLElement; | ||
} | ||
</script> | ||
|
||
<script lang="ts"> | ||
import { onMount } from 'svelte'; | ||
import { CodeBlockGroupContext } from './CodeBlockGroup.svelte'; | ||
let { id, codeblock, ...rest }: ButtonFullScreeenProps = $props(); | ||
let fullscreen = $state(false); | ||
const groupContext = CodeBlockGroupContext.get(); | ||
function onFullScreenChange() { | ||
fullscreen = !!document.fullscreenElement; | ||
} | ||
function goFullscreen() { | ||
if (!document.fullscreenElement) { | ||
if (groupContext?.node) { | ||
groupContext.node.requestFullscreen(); | ||
} else { | ||
codeblock?.requestFullscreen(); | ||
} | ||
} else if (document.exitFullscreen) { | ||
document.exitFullscreen(); | ||
} | ||
} | ||
function onKeyDown(e: KeyboardEvent) { | ||
if (e.key === 'Enter' || e.key === ' ') { | ||
e.preventDefault(); | ||
goFullscreen(); | ||
} | ||
} | ||
function onClick(e: MouseEvent) { | ||
e.preventDefault(); | ||
goFullscreen(); | ||
} | ||
onMount(() => { | ||
codeblock?.addEventListener('fullscreenchange', onFullScreenChange); | ||
return () => { | ||
codeblock?.removeEventListener('fullscreenchange', onFullScreenChange); | ||
}; | ||
}); | ||
</script> | ||
|
||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions --> | ||
<label for={id} {...rest} onclick={onClick} onkeydown={onKeyDown}> | ||
<span class="sr-only">Toggle full screen</span> | ||
{#if !groupContext} | ||
<input class="codeblock-fullscreen sr-only" type="checkbox" {id} bind:checked={fullscreen} /> | ||
{/if} | ||
<svg | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="20" | ||
height="20" | ||
fill="currentcolor" | ||
viewBox="0 0 256 256" | ||
> | ||
<path | ||
class="maximize" | ||
d="M216,48V88a8,8,0,0,1-16,0V56H168a8,8,0,0,1,0-16h40A8,8,0,0,1,216,48ZM88,200H56V168a8,8,0,0,0-16,0v40a8,8,0,0,0,8,8H88a8,8,0,0,0,0-16Zm120-40a8,8,0,0,0-8,8v32H168a8,8,0,0,0,0,16h40a8,8,0,0,0,8-8V168A8,8,0,0,0,208,160ZM88,40H48a8,8,0,0,0-8,8V88a8,8,0,0,0,16,0V56H88a8,8,0,0,0,0-16Z" | ||
></path> | ||
<path | ||
class="minimize" | ||
d="M152,96V48a8,8,0,0,1,16,0V88h40a8,8,0,0,1,0,16H160A8,8,0,0,1,152,96ZM96,152H48a8,8,0,0,0,0,16H88v40a8,8,0,0,0,16,0V160A8,8,0,0,0,96,152Zm112,0H160a8,8,0,0,0-8,8v48a8,8,0,0,0,16,0V168h40a8,8,0,0,0,0-16ZM96,40a8,8,0,0,0-8,8V88H48a8,8,0,0,0,0,16H96a8,8,0,0,0,8-8V48A8,8,0,0,0,96,40Z" | ||
></path> | ||
</svg> | ||
</label> | ||
|
||
<style lang="postcss"> | ||
/** copied from CodeBlock.svelte */ | ||
@custom-selector :--fullscreen :has(.codeblock-fullscreen:checked); | ||
@custom-selector :--g-fullscreen | ||
:global(.codeblock-group:has(.codeblock-group-fullscreen:checked)); | ||
.minimize { | ||
display: none; | ||
} | ||
:--g-fullscreen, | ||
label:has(:global(.codeblock-fullscreen:checked)) { | ||
& .maximize { | ||
display: none; | ||
} | ||
& .minimize { | ||
display: block; | ||
} | ||
} | ||
</style> |
Oops, something went wrong.