Add navrow + markdown parsing

This commit is contained in:
Jai A 2023-03-28 10:34:48 -07:00
parent de844e9b23
commit 8167f6f232
No known key found for this signature in database
GPG Key ID: 9A9F9B7250E9883C
15 changed files with 280 additions and 40 deletions

View File

@ -38,6 +38,7 @@ export default {
{ text: 'Search Filter', link: '/components/search-filter' },
{ text: 'Toggle', link: '/components/toggle' },
{ text: 'Promotion', link: '/components/promotion' },
{ text: 'Navigation Row', link: '/components/nav-row' },
],
},
],

View File

@ -0,0 +1,47 @@
# NavRow
Note: the links and animation do not work in the documentation as Vue Router is not used for routing
<DemoContainer>
<NavRow :links="[
{
label: 'Avatar',
href: '/components/avatar.html',
},
{
label: 'Badge',
href: '/components/nav-row.html',
},
{
label: 'Pagination',
href: '/components/Pagination.html',
},
{
label: 'NavRow',
href: '/components/nav-row.html',
}
]"
/>
</DemoContainer>
```vue
<NavRow :links="[
{
label: 'Avatar',
href: '/components/avatar.html',
},
{
label: 'Badge',
href: '/components/nav-row.html',
},
{
label: 'Pagination',
href: '/components/Pagination.html',
},
{
label: 'NavRow',
href: '/components/nav-row.html',
}
]"
/>
```

View File

@ -609,6 +609,7 @@ a,
}
a {
cursor: pointer;
color: var(--color-link);
&:focus-visible,

View File

@ -7,7 +7,7 @@
</template>
<script>
import { fileIsValid } from '@/components/utils'
import { fileIsValid } from '@/helpers/utils.js'
import { defineComponent } from 'vue'
export default defineComponent({
props: {

View File

@ -56,7 +56,7 @@
</template>
<script setup>
import { Modal, Chips, XIcon, CheckIcon, DropdownSelect } from '@/components'
import { renderString } from '@/components/parse.js'
import { renderString } from '@/helpers/parse.js'
import { ref } from 'vue'
const modal = ref('modal')

View File

@ -76,7 +76,7 @@ import {
Categories,
EnvironmentIndicator,
} from '@/components'
import { formatNumber } from '@/components/utils'
import { formatNumber } from '@/helpers/utils.js'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)

View File

@ -1,51 +1,166 @@
<script setup>
defineProps({})
</script>
<template>
<div class="omorphia__navrow">
<slot />
<div class="right-slot">
<slot name="right" />
</div>
</div>
<nav class="navigation">
<router-link
v-for="(link, index) in filteredLinks"
v-show="link.shown === undefined ? true : link.shown"
:key="index"
ref="linkElements"
:to="query ? (link.href ? `?${query}=${link.href}` : '?') : link.href"
class="nav-link button-animation"
>
<span>{{ link.label }}</span>
</router-link>
<div
class="nav-indicator"
:style="{
left: positionToMoveX,
top: positionToMoveY,
width: sliderWidth,
opacity: activeIndex === -1 ? 0 : 1,
}"
aria-hidden="true"
/>
</nav>
</template>
<script>
export default {
props: {
links: {
default: () => [],
type: Array,
},
query: {
default: null,
type: String,
},
},
data() {
return {
sliderPositionX: 0,
sliderPositionY: 18,
selectedElementWidth: 0,
activeIndex: -1,
oldIndex: -1,
}
},
computed: {
filteredLinks() {
return this.links.filter((x) => (x.shown === undefined ? true : x.shown))
},
positionToMoveX() {
return `${this.sliderPositionX}px`
},
positionToMoveY() {
return `${this.sliderPositionY}px`
},
sliderWidth() {
return `${this.selectedElementWidth}px`
},
},
watch: {
'$route.path': {
handler() {
this.pickLink()
},
},
'$route.query': {
handler() {
if (this.query) this.pickLink()
},
},
},
mounted() {
window.addEventListener('resize', this.pickLink)
this.pickLink()
},
unmounted() {
window.removeEventListener('resize', this.pickLink)
},
methods: {
pickLink() {
this.activeIndex = this.query
? this.filteredLinks.findIndex(
(x) => (x.href === '' ? undefined : x.href) === this.$route.path[this.query]
)
: this.filteredLinks.findIndex((x) => x.href === decodeURIComponent(this.$route.path))
if (this.activeIndex !== -1) {
this.startAnimation()
} else {
this.oldIndex = -1
this.sliderPositionX = 0
this.selectedElementWidth = 0
}
},
startAnimation() {
const el = this.$refs.linkElements[this.activeIndex].$el
this.sliderPositionX = el.offsetLeft
this.sliderPositionY = el.offsetTop + el.offsetHeight
this.selectedElementWidth = el.offsetWidth
},
},
}
</script>
<style lang="scss" scoped>
.omorphia__navrow {
.navigation {
display: flex;
flex-direction: row;
align-items: center;
grid-gap: 1rem;
flex-wrap: wrap;
position: relative;
.right-slot {
margin-left: auto;
}
:deep(.btn) {
.nav-link {
text-transform: capitalize;
font-weight: var(--font-weight-bold);
color: var(--color-text);
position: relative;
&::after {
content: '';
position: absolute;
width: calc(100% - var(--gap-lg) * 2);
height: 4px;
bottom: 4px;
border-radius: var(--radius-max);
background-color: var(--color-brand);
opacity: 0;
&:hover {
color: var(--color-text);
&::after {
opacity: 0.4;
}
}
&:hover::after {
opacity: 50%;
&:active::after {
opacity: 0.2;
}
&.selected {
color: var(--color-contrast);
font-weight: bold;
&.router-link-exact-active {
color: var(--color-text);
&::after {
opacity: 1;
}
}
}
&.use-animation {
.nav-link {
&.is-active::after {
opacity: 0;
}
}
}
.nav-indicator {
position: absolute;
height: 0.25rem;
bottom: -5px;
left: 0;
width: 3rem;
transition: all ease-in-out 0.2s;
border-radius: var(--size-rounded-max);
background-color: var(--color-brand);
@media (prefers-reduced-motion) {
transition: none !important;
}
}
}
</style>

View File

@ -9,7 +9,7 @@
</div>
</template>
<script setup>
import { formatCategory } from '@/components/utils'
import { formatCategory } from '@/helpers/utils.js'
</script>
<script>
export default {

63
lib/helpers/highlight.js Normal file
View File

@ -0,0 +1,63 @@
import hljs from 'highlight.js/lib/core'
// Scripting
import javascript from 'highlight.js/lib/languages/javascript'
import python from 'highlight.js/lib/languages/python'
import lua from 'highlight.js/lib/languages/lua'
// Coding
import java from 'highlight.js/lib/languages/java'
import kotlin from 'highlight.js/lib/languages/kotlin'
import scala from 'highlight.js/lib/languages/scala'
import groovy from 'highlight.js/lib/languages/groovy'
// Configs
import gradle from 'highlight.js/lib/languages/gradle'
import json from 'highlight.js/lib/languages/json'
import ini from 'highlight.js/lib/languages/ini'
import yaml from 'highlight.js/lib/languages/yaml'
import xml from 'highlight.js/lib/languages/xml'
import properties from 'highlight.js/lib/languages/properties'
import { md, configuredXss } from '@/helpers/parse'
/* REGISTRATION */
// Scripting
hljs.registerLanguage('javascript', javascript)
hljs.registerLanguage('python', python)
hljs.registerLanguage('lua', lua)
// Coding
hljs.registerLanguage('java', java)
hljs.registerLanguage('kotlin', kotlin)
hljs.registerLanguage('scala', scala)
hljs.registerLanguage('groovy', groovy)
// Configs
hljs.registerLanguage('gradle', gradle)
hljs.registerLanguage('json', json)
hljs.registerLanguage('ini', ini)
hljs.registerLanguage('yaml', yaml)
hljs.registerLanguage('xml', xml)
hljs.registerLanguage('properties', properties)
/* ALIASES */
// Scripting
hljs.registerAliases(['js'], { languageName: 'javascript' })
hljs.registerAliases(['py'], { languageName: 'python' })
// Coding
hljs.registerAliases(['kt'], { languageName: 'kotlin' })
// Configs
hljs.registerAliases(['json5'], { languageName: 'json' })
hljs.registerAliases(['toml'], { languageName: 'ini' })
hljs.registerAliases(['yml'], { languageName: 'yaml' })
hljs.registerAliases(['html', 'htm', 'xhtml', 'mcui', 'fxml'], { languageName: 'xml' })
export const renderHighlightedString = (string) =>
configuredXss.process(
md({
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, { language: lang }).value
} catch (__) { /* empty */ }
}
return ''
},
}).render(string)
)

3
lib/helpers/index.js Normal file
View File

@ -0,0 +1,3 @@
export * from './highlight'
export * from './parse'
export * from './utils'

View File

@ -31,6 +31,10 @@ export const configuredXss = new xss.FilterXSS({
/^https?:\/\/(www\.)?youtube(-nocookie)?\.com\/embed\/[a-zA-Z0-9_-]{11}(\?&autoplay=[0-1]{1})?$/,
remove: ['&autoplay=1'], // Prevents autoplay
},
{
regex: /^https?:\/\/(www\.)?discord\.com\/widget\?id=\d{18,19}(&theme=\w+)?$/,
remove: [/&theme=\w+/],
},
]
for (const source of allowedSources) {
@ -82,9 +86,7 @@ export const md = (options = {}) => {
if (allowedHostnames.includes(url.hostname)) {
return defaultLinkOpenRenderer(tokens, idx, options, env, self)
}
} catch (err) {
// Ignore
}
} catch (err) {}
}
tokens[idx].attrSet('rel', 'noopener nofollow ugc')
@ -109,21 +111,22 @@ export const md = (options = {}) => {
const url = new URL(src)
const allowedHostnames = [
'imgur.com',
'i.imgur.com',
'cdn-raw.modrinth.com',
'cdn.modrinth.com',
'staging-cdn-raw.modrinth.com',
'staging-cdn.modrinth.com',
'github.com',
'raw.githubusercontent.com',
'img.shields.io',
'i.postimg.cc',
]
if (allowedHostnames.includes(url.hostname)) {
return defaultImageRenderer(tokens, idx, options, env, self)
}
} catch (err) {
/* empty */
}
} catch (err) {}
token.attrs[index][1] = `//wsrv.nl/?url=${encodeURIComponent(src)}`
}

View File

@ -10,5 +10,6 @@ function install(app) {
export default { install }
export * from './components'
export * from './helpers'
import './assets/omorphia.scss'

View File

@ -26,6 +26,7 @@
"dependencies": {
"dayjs": "^1.11.7",
"floating-vue": "^2.0.0-beta.20",
"highlight.js": "^11.7.0",
"markdown-it": "^13.0.1",
"vue": "^3.2.45",
"vue-router": "^4.1.6",

View File

@ -1064,6 +1064,11 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
highlight.js@^11.7.0:
version "11.7.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.7.0.tgz#3ff0165bc843f8c9bce1fd89e2fda9143d24b11e"
integrity sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==
ignore@^5.2.0:
version "5.2.4"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz"