Skip to content

Commit

Permalink
basic code modals
Browse files Browse the repository at this point in the history
  • Loading branch information
ToBinio committed May 11, 2024
1 parent 99c0cec commit 8f29aa0
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 6 deletions.
23 changes: 23 additions & 0 deletions docs/guide/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,29 @@ const line3 = 'This is line 3'
const line4 = 'This is line 4'
```

## Code Modal

[//]: # (todo - expand on docs )

````md:modal
```ts {1}
// line-numbers is disabled by default
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```
```ts:line-numbers {1}
// line-numbers is enabled
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```
```ts:line-numbers=2 {1}
// line-numbers is enabled and start from 2
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
````
## Import Code Snippets

You can import code snippets from existing files via following syntax:
Expand Down
37 changes: 37 additions & 0 deletions src/client/app/composables/codeModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { inBrowser } from 'vitepress'

export function useCodeModal() {
if (inBrowser) {
window.addEventListener('click', (e) => {
const el = e.target as HTMLElement

if (el.matches('div[class*="language-"] > button.modal')) {
const parent = el.parentElement
const sibling = el.nextElementSibling
if (!parent || !sibling) {
return
}

sibling.classList.add('open')
}

if (el.matches('div[class*="language-"] > div.modal-container')) {
el.classList.remove('open')
}
})

window.addEventListener('keydown', (ev) => {
if (ev.key == 'Escape') {
let modal = window.document.querySelector(
'div[class*="language-"] > div.modal-container.open'
)

if (!modal) {
return
}

modal.classList.remove('open')
}
})
}
}
3 changes: 3 additions & 0 deletions src/client/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { usePrefetch } from './composables/preFetch'
import { dataSymbol, initData, siteDataRef, useData } from './data'
import { RouterSymbol, createRouter, scrollTo, type Router } from './router'
import { inBrowser, pathToFile } from './utils'
import { useCodeModal } from './composables/codeModal'

function resolveThemeExtends(theme: typeof RawTheme): typeof RawTheme {
if (theme.extends) {
Expand Down Expand Up @@ -57,6 +58,8 @@ const VitePressApp = defineComponent({
useCopyCode()
// setup global code groups handler
useCodeGroups()
// setup global code modal handler
useCodeModal()

if (Theme.setup) Theme.setup()
return () => h(Theme.Layout!)
Expand Down
43 changes: 43 additions & 0 deletions src/client/theme-default/styles/components/vp-code.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,46 @@
html:not(.dark) .vp-code span {
color: var(--shiki-light, inherit);
}

.vp-code ~ .modal-container:not(.open) {
display: none;
}

.vp-code ~ .modal-container {
position: fixed;

z-index: 100;

top: 0;
left: 0;
width: 100vw;
height: 100vh;

display: flex;
justify-content: center;
align-items: center;

background-color: rgba(255, 255, 255, 0.02);
backdrop-filter: blur(2px);
}

html:not(.dark) .vp-code ~ .modal-container {
background-color: rgba(0, 0, 0, 0.1);
}

.vp-code ~ .modal-container [class*='language-'] {
position: relative;
margin: 20px;

max-width: 90%;
width: min-content;

border-radius: 8px;

background-color: var(--vp-code-block-bg);
box-shadow: var(--vp-shadow-2);
}

.vp-code ~ .modal-container [class*='language-']:hover span.lang {
opacity: 0;
}
20 changes: 14 additions & 6 deletions src/client/theme-default/styles/components/vp-doc.css
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,8 @@
color 0.5s;
}

.vp-doc [class*='language-'] > button.copy {
.vp-doc [class*='language-'] > button.copy,
.vp-doc [class*='language-'] > button.modal {
/*rtl:ignore*/
direction: ltr;
position: absolute;
Expand All @@ -454,13 +455,20 @@
opacity 0.25s;
}

.vp-doc [class*='language-'] > button.modal {
top: 64px;
}

.vp-doc [class*='language-']:hover > button.copy,
.vp-doc [class*='language-'] > button.copy:focus {
.vp-doc [class*='language-'] > button.copy:focus,
.vp-doc [class*='language-']:hover > button.modal,
.vp-doc [class*='language-'] > button.modal:focus {
opacity: 1;
}

.vp-doc [class*='language-'] > button.copy:hover,
.vp-doc [class*='language-'] > button.copy.copied {
.vp-doc [class*='language-'] > button.copy.copied,
.vp-doc [class*='language-'] > button.modal:hover {
border-color: var(--vp-code-copy-code-hover-border-color);
background-color: var(--vp-code-copy-code-hover-bg);
}
Expand Down Expand Up @@ -498,7 +506,7 @@
content: var(--vp-code-copy-copied-text-content);
}

.vp-doc [class*='language-'] > span.lang {
.vp-doc [class*='language-'] span.lang {
position: absolute;
top: 2px;
/*rtl:ignore*/
Expand All @@ -512,8 +520,8 @@
opacity 0.4s;
}

.vp-doc [class*='language-']:hover > button.copy + span.lang,
.vp-doc [class*='language-'] > button.copy:focus + span.lang {
.vp-doc [class*='language-']:hover button.copy + span.lang,
.vp-doc [class*='language-'] button.copy:focus + span.lang {
opacity: 0;
}

Expand Down
13 changes: 13 additions & 0 deletions src/node/markdown/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { linkPlugin } from './plugins/link'
import { preWrapperPlugin } from './plugins/preWrapper'
import { restoreEntities } from './plugins/restoreEntities'
import { snippetPlugin } from './plugins/snippet'
import { codeModalPlugin } from './plugins/codeModal'

export type { Header } from '../shared'

Expand Down Expand Up @@ -116,6 +117,16 @@ export interface MarkdownOptions extends Options {
* @default 'Copy Code'
*/
codeCopyButtonTitle?: string
/**
* Show an additional button to open a fullscreen modal in code blocks
* @default false
*/
codeModal?: boolean
/**
* The tooltip text for the modal button in code blocks
* @default 'Open Modal'
*/
codeModalButtonTitle?: string

/* ==================== Markdown It Plugins ==================== */

Expand Down Expand Up @@ -201,6 +212,7 @@ export const createMarkdownRenderer = async (
): Promise<MarkdownRenderer> => {
const theme = options.theme ?? { light: 'github-light', dark: 'github-dark' }
const codeCopyButtonTitle = options.codeCopyButtonTitle || 'Copy Code'
const codeModalButtonTitle = options.codeModalButtonTitle || 'Open Modal'
const hasSingleTheme = typeof theme === 'string' || 'name' in theme

const md = MarkdownIt({
Expand Down Expand Up @@ -230,6 +242,7 @@ export const createMarkdownRenderer = async (
base
)
.use(lineNumberPlugin, options.lineNumbers)
.use(codeModalPlugin, options.codeModal, { codeModalButtonTitle })

if (options.gfmAlerts !== false) {
md.use(gitHubAlertsPlugin)
Expand Down
36 changes: 36 additions & 0 deletions src/node/markdown/plugins/codeModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// markdown-it plugin for generating line numbers.
// It depends on preWrapper plugin.

import type MarkdownIt from 'markdown-it'
import type { MarkdownOptions } from '../markdown'

export const codeModalPlugin = (
md: MarkdownIt,
enable = false,
options: MarkdownOptions = {}
) => {
const fence = md.renderer.rules.fence!
md.renderer.rules.fence = (...args) => {
const rawCode = fence(...args)

const [tokens, idx] = args
const info = tokens[idx].info

if (
(!enable && !/:modal($| |=)/.test(info)) ||
(enable && /:no-modal($| )/.test(info))
) {
return rawCode
}

const code =
`<button title="${options.codeModalButtonTitle}" class="modal"></button>` +
'<div class="modal-container">' +
fence(...args) +
'</div>'

let end = rawCode.lastIndexOf('</div>')

return rawCode.substring(0, end) + code + '</div>'
}
}
2 changes: 2 additions & 0 deletions src/node/markdown/plugins/highlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export async function highlight(
const vueRE = /-vue$/
const lineNoStartRE = /=(\d*)/
const lineNoRE = /:(no-)?line-numbers(=\d*)?$/
const modalNoRE = /:(no-)?modal(=\d*)?$/
const mustacheRE = /\{\{.*?\}\}/g

return (str: string, lang: string, attrs: string) => {
Expand All @@ -102,6 +103,7 @@ export async function highlight(
lang
.replace(lineNoStartRE, '')
.replace(lineNoRE, '')
.replace(modalNoRE, '')
.replace(vueRE, '')
.toLowerCase() || defaultLang

Expand Down
1 change: 1 addition & 0 deletions src/node/markdown/plugins/preWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function extractLang(info: string) {
.trim()
.replace(/=(\d*)/, '')
.replace(/:(no-)?line-numbers({| |$|=\d*).*/, '')
.replace(/:(no-)?modal({| |$|=\d*).*/, '')
.replace(/(-vue|{| ).*$/, '')
.replace(/^vue-html$/, 'template')
.replace(/^ansi$/, '')
Expand Down

0 comments on commit 8f29aa0

Please sign in to comment.