Skip to content

Commit

Permalink
feat(web-core): new VtsLink component
Browse files Browse the repository at this point in the history
  • Loading branch information
ByScripts committed Sep 20, 2024
1 parent 02ed1bc commit eba6c5d
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 0 deletions.
41 changes: 41 additions & 0 deletions @xen-orchestra/lite/src/stories/web-core/vts-link.story.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<ComponentStory
v-slot="{ properties }"
:params="[
prop('accent').required().enum('brand', 'success', 'warning', 'danger').preset('brand').widget(),
prop('size').required().enum('small', 'medium').preset('medium').widget(),
prop('to').type('IconDefinition').widget(text()),
prop('href').str().widget(),
iconProp(),
prop('disabled').bool().widget(),
prop('target').enum('_blank', '_self').widget(),
]"
>
<VtsLink v-bind="properties">This is a link</VtsLink>

<div v-if="!properties.to && !properties.href" class="info">
<UiIcon :icon="faInfoCircle" color="normal" />
Link is disabled because no `href` or `to` is provided
</div>
<div v-else-if="properties.to && properties.href" class="info">
<UiIcon :icon="faExclamationTriangle" color="warning" />
`to` is ignored when `href` is provided
</div>
</ComponentStory>
</template>

<script lang="ts" setup>
import ComponentStory from '@/components/component-story/ComponentStory.vue'
import { iconProp, prop } from '@/libs/story/story-param'
import { text } from '@/libs/story/story-widget'
import UiIcon from '@core/components/icon/UiIcon.vue'
import VtsLink from '@core/components/link/VtsLink.vue'
import { faExclamationTriangle, faInfoCircle } from '@fortawesome/free-solid-svg-icons'
</script>

<style lang="postcss" scoped>
.info {
margin-top: 4rem;
font-style: italic;
}
</style>
164 changes: 164 additions & 0 deletions @xen-orchestra/web-core/lib/components/link/VtsLink.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<template>
<component :is="config.component" :class="classes" class="vts-link" v-bind="config.attributes">
<UiIcon :icon="icon" color="current" />
<slot />
<UiIcon
v-if="config.attributes.target === '_blank'"
:icon="faArrowUpRightFromSquare"
class="external-icon"
color="current"
/>
</component>
</template>

<script lang="ts" setup>
import UiIcon from '@core/components/icon/UiIcon.vue'
import { toVariants } from '@core/utils/to-variants.util'
import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
import { faArrowUpRightFromSquare } from '@fortawesome/free-solid-svg-icons'
import { computed } from 'vue'
import type { RouteLocationRaw } from 'vue-router'
const props = defineProps<{
size: 'small' | 'medium'
accent: 'brand' | 'success' | 'warning' | 'danger'
icon?: IconDefinition
disabled?: boolean
href?: string
to?: RouteLocationRaw
target?: '_blank' | '_self'
}>()
const typoClasses = {
small: 'typo p3-regular',
medium: 'typo p1-regular',
}
const isDisabled = computed(() => props.disabled || (!props.to && !props.href))
const classes = computed(() => [
typoClasses[props.size],
toVariants({
disabled: isDisabled.value,
accent: props.accent,
}),
])
const config = computed(() => {
if (isDisabled.value) {
return {
component: 'span',
attributes: {},
}
}
if (props.href) {
return {
component: 'a',
attributes: {
rel: 'noopener noreferrer',
target: props.target ?? '_blank',
href: props.href,
},
}
}
return {
component: 'RouterLink',
attributes: {
target: props.target,
to: props.to,
},
}
})
</script>

<style lang="postcss" scoped>
/*
ACCENT + STATE
--vts-link--color
*/
.vts-link {
&.accent--brand {
--vts-link--color: var(--color-normal-txt-base);
&:hover {
--vts-link--color: var(--color-normal-txt-hover);
}
&:active {
--vts-link--color: var(--color-normal-txt-active);
}
&.disabled {
--vts-link--color: var(--color-neutral-txt-secondary);
}
}
&.accent--success {
--vts-link--color: var(--color-success-txt-base);
&:hover {
--vts-link--color: var(--color-success-txt-hover);
}
&:active {
--vts-link--color: var(--color-success-txt-active);
}
&.disabled {
--vts-link--color: var(--color-neutral-txt-secondary);
}
}
&.accent--warning {
--vts-link--color: var(--color-warning-txt-base);
&:hover {
--vts-link--color: var(--color-warning-txt-hover);
}
&:active {
--vts-link--color: var(--color-warning-txt-active);
}
&.disabled {
--vts-link--color: var(--color-neutral-txt-secondary);
}
}
&.accent--danger {
--vts-link--color: var(--color-danger-txt-base);
&:hover {
--vts-link--color: var(--color-danger-txt-hover);
}
&:active {
--vts-link--color: var(--color-danger-txt-active);
}
&.disabled {
--vts-link--color: var(--color-neutral-txt-secondary);
}
}
}
/* IMPLEMENTATION */
.vts-link {
display: inline-flex;
align-items: center;
gap: 0.8rem;
color: var(--vts-link--color);
text-decoration: underline;
text-underline-offset: 0.2rem;
&.disabled {
cursor: not-allowed;
}
.external-icon {
font-size: 0.75em;
}
}
</style>

0 comments on commit eba6c5d

Please sign in to comment.