fcb924d3a5
* refactor WIP * Button design changes * FE2 FormButton Updates * ts composition api * CommonTextLink Changes * CommonTextLink prop updates * Add disabled styles * WIP * Design system updates * Colour Updates * New Text Styles. Initial FE2 changes * More fe2 styling classes * Minor update * Minor update * Fix build * More updates for discussion * More styling updates * Minor updates to inputs * Revert change to size options * More text updates * More font class swapping * Revert dui3 changes * Confirmed Lineheights * Add story files for new text styles * Minor copy changes * Minor typo * Revert variant>color * New Colours WIP * andrew/web-1371-misalignment-in-account-dropdown * andrew/web-1374-settings-text-styles-are-not-right * andrew/web-1375-nav-texts-should-be-14px * andrew/web-1376-decrease-size-of-versions-header * andrew/web-1377-version-card-title * Updates * semibold>medium * Colour updates * Sizing updates * Colour updates * Colour updates * Measure mode * Updates * Fix build * Fix build * WIP Updates * Changes from PR * Updated login, registration and reset password styling * Make share dropdown bg white * Updated viewer titles * Fix: Resize panel highlight color in the viewer should be blue * Fix: Blue + Add link in Models. And other blue links in Viewer * Add labelPosition Prop. Fix Button stories * Updated CommonLink to remove default underline * Add Highlight Color * Card updates from Michal * Updated discussion icon on version card * Small tweaks to version card * Small tweaks to version card * Fix: Ghost button doesn't have padding * Fix: Write Delete... * Fix: Version hover border color * Updates to Project Card. Updates to PageTabs * Fix: Adjust title in announcement modal * Updates from Comments * Select Background Colour * Fix: Select dropdown color * Improve list view. Improve discussions * Fix: Minor tweaks to onboarding checklist * Fix: Clean up nav * Hide third item when not >md * Change project heading size * Add border to version card * Adjust spacing in dropdowns * Slight change * Update button style in Version card * Tweaked nav menu * Tweaked nav menu * Various styling tweaks * Fix settings modal subheader * Various styling tweaks and fixes * Tweak settings dialog styling * Tweak simple scrollbar * Minor tweaks to model page * Minor tweaks to model page * Minor tweak to login * Tweak discussion card * Tweak settings page * Tweak vertical tabs * Tweak Dialog alignment * Fix some paddings * Change IconVersions to ClockIcon * Tweak spacing between icons * Updates to Card Icons * Bold "connectors" in empty project message * Remove padding in Profile field * Update inline model create * Remove icons from share menu * Updated Delete dialog * Wrong text positioning in alert * Updated copy in dropdown * Change bg to bg-foundation in select dropdown component * Fix merge conflicy * Selection Info title colour * Wrong text class * Update card colours based on call * Update card colours * Update empty state * Input label font weight * Updates to Embed * Various styling fixes * Fix; Viewer panel header styling * Fix; Adjust BG in dev mode list items * Fix; Fix button placement in video modal * Fix: Share menu is not using LayoutMenu * Fix: Buttons clash under filters * Fix: Adjust spacing in selection info * Fix: Adjust gray BG behind model preview images * Fix: No hover cursor on model card * Fix: Align text styling in dev mode and selection info panel * Fix for menu width * Fix mobile problems * Fix Add spacing on new login screens * Revert prose change. Add prose-sm * Text - Use contain for bg image * Fix onboarding screens * Responsive fixes * Fix hydration errors * Added padding to Add Model Dialog * Fix versions buttons * Fix build problem * Changes PRE PR * Final Pre PR Changes * Remove DUI3 change * Fix small issue with dialog after merge conflict * Remove label classes from Visibility Select * Revert changes made in Controls.vue * Remove old-webhooks * Add highlight colours to Storybook * Add v-keyboard-clickable --------- Co-authored-by: Mike Tasset <mike.tasset@gmail.com>
300 lines
7.5 KiB
Vue
300 lines
7.5 KiB
Vue
<template>
|
|
<Component
|
|
:is="to ? linkComponent : 'button'"
|
|
:href="to"
|
|
:to="to"
|
|
:type="buttonType"
|
|
:external="external"
|
|
:class="buttonClasses"
|
|
:disabled="isDisabled"
|
|
role="button"
|
|
:style="
|
|
color !== 'subtle' && !text
|
|
? `box-shadow: -1px 1px 4px 0px #0000000a inset; box-shadow: 0px 2px 2px 0px #0000000d;`
|
|
: ''
|
|
"
|
|
@click="onClick"
|
|
>
|
|
<Component :is="finalLeftIcon" v-if="finalLeftIcon" :class="iconClasses" />
|
|
<slot v-if="!hideText">Button</slot>
|
|
<Component :is="iconRight" v-if="iconRight || !loading" :class="iconClasses" />
|
|
</Component>
|
|
</template>
|
|
<script setup lang="ts">
|
|
import { isObjectLike } from 'lodash'
|
|
import type { PropAnyComponent } from '~~/src/helpers/common/components'
|
|
import { computed, resolveDynamicComponent } from 'vue'
|
|
import type { Nullable } from '@speckle/shared'
|
|
import { ArrowPathIcon } from '@heroicons/vue/24/solid'
|
|
import type { FormButtonStyle, FormButtonSize } from '~~/src/helpers/form/button'
|
|
|
|
const emit = defineEmits<{
|
|
/**
|
|
* Emit MouseEvent on click
|
|
*/
|
|
(e: 'click', val: MouseEvent): void
|
|
}>()
|
|
|
|
const props = defineProps<{
|
|
/**
|
|
* URL to which to navigate - can be a relative (app) path or an absolute link for an external URL
|
|
*/
|
|
to?: string
|
|
/**
|
|
* Choose from one of 3 button sizes
|
|
*/
|
|
size?: FormButtonSize
|
|
/**
|
|
* If set, will make the button take up all available space horizontally
|
|
*/
|
|
fullWidth?: boolean
|
|
/**
|
|
* Similar to "link", but without an underline and possibly in different colors
|
|
*/
|
|
text?: boolean
|
|
/**
|
|
* Will remove paddings and background. Use for links.
|
|
*/
|
|
link?: boolean
|
|
/**
|
|
* color:
|
|
* primary: the default primary blue.
|
|
* outline: foundation background and outline
|
|
* subtle: no styling
|
|
*/
|
|
color?: FormButtonStyle
|
|
/**
|
|
* Should rounded-full be added?:
|
|
*/
|
|
rounded?: boolean
|
|
/**
|
|
* Whether the target location should be forcefully treated as an external URL
|
|
* (for relative paths this will likely cause a redirect)
|
|
*/
|
|
external?: boolean
|
|
/**
|
|
* Whether to disable the button so that it can't be pressed
|
|
*/
|
|
disabled?: boolean
|
|
/**
|
|
* If set, will have type set to "submit" to enable it to submit any parent forms
|
|
*/
|
|
submit?: boolean
|
|
/**
|
|
* Add icon to the left from the text
|
|
*/
|
|
iconLeft?: Nullable<PropAnyComponent>
|
|
/**
|
|
* Add icon to the right from the text
|
|
*/
|
|
iconRight?: Nullable<PropAnyComponent>
|
|
/**
|
|
* Hide default slot (when you want to show icons only)
|
|
*/
|
|
hideText?: boolean
|
|
/**
|
|
* Customize component to be used when rendering links.
|
|
*
|
|
* The component will try to dynamically resolve NuxtLink and RouterLink and use those, if this is set to null.
|
|
*/
|
|
linkComponent?: Nullable<PropAnyComponent>
|
|
/**
|
|
* Disables the button and shows a spinning loader
|
|
*/
|
|
loading?: boolean
|
|
}>()
|
|
|
|
const NuxtLink = resolveDynamicComponent('NuxtLink')
|
|
const RouterLink = resolveDynamicComponent('RouterLink')
|
|
|
|
const linkComponent = computed(() => {
|
|
if (props.linkComponent) return props.linkComponent
|
|
if (props.external) return 'a'
|
|
if (isObjectLike(NuxtLink)) return NuxtLink
|
|
if (isObjectLike(RouterLink)) return RouterLink
|
|
return 'a'
|
|
})
|
|
|
|
const buttonType = computed(() => {
|
|
if (props.to) return undefined
|
|
if (props.submit) return 'submit'
|
|
return 'button'
|
|
})
|
|
|
|
const isDisabled = computed(() => props.disabled || props.loading)
|
|
const finalLeftIcon = computed(() => (props.loading ? ArrowPathIcon : props.iconLeft))
|
|
|
|
const bgAndBorderClasses = computed(() => {
|
|
const classParts: string[] = []
|
|
|
|
const colorsBgBorder = {
|
|
subtle: [
|
|
'bg-transparent border-transparent text-foreground font-medium',
|
|
'hover:bg-primary-muted disabled:hover:bg-transparent focus-visible:border-foundation'
|
|
],
|
|
outline: [
|
|
'bg-foundation border-outline-2 text-foreground font-medium',
|
|
'hover:bg-primary-muted disabled:hover:bg-foundation focus-visible:border-foundation'
|
|
],
|
|
danger: [
|
|
'bg-danger border-danger-darker text-foundation font-medium',
|
|
'hover:bg-danger-darker disabled:hover:bg-danger focus-visible:border-foundation'
|
|
],
|
|
primary: [
|
|
'bg-primary border-outline-1 text-foreground-on-primary font-semibold',
|
|
'hover:bg-primary-focus disabled:hover:bg-primary focus-visible:border-foundation'
|
|
]
|
|
}
|
|
|
|
if (props.rounded) {
|
|
classParts.push('!rounded-full')
|
|
}
|
|
|
|
if (props.text || props.link) {
|
|
switch (props.color) {
|
|
case 'subtle':
|
|
classParts.push('text-foreground')
|
|
break
|
|
case 'outline':
|
|
classParts.push('text-foreground')
|
|
break
|
|
case 'danger':
|
|
classParts.push('text-danger')
|
|
break
|
|
case 'primary':
|
|
default:
|
|
classParts.push('text-primary')
|
|
break
|
|
}
|
|
} else {
|
|
switch (props.color) {
|
|
case 'subtle':
|
|
classParts.push(...colorsBgBorder.subtle)
|
|
break
|
|
case 'outline':
|
|
classParts.push(...colorsBgBorder.outline)
|
|
break
|
|
case 'danger':
|
|
classParts.push(...colorsBgBorder.danger)
|
|
break
|
|
case 'primary':
|
|
default:
|
|
classParts.push(...colorsBgBorder.primary)
|
|
break
|
|
}
|
|
}
|
|
|
|
return classParts.join(' ')
|
|
})
|
|
|
|
const sizeClasses = computed(() => {
|
|
switch (props.size) {
|
|
case 'sm':
|
|
return 'h-6 text-body-2xs'
|
|
case 'lg':
|
|
return 'h-10 text-body-sm'
|
|
default:
|
|
case 'base':
|
|
return 'h-8 text-body-xs'
|
|
}
|
|
})
|
|
|
|
const paddingClasses = computed(() => {
|
|
if (props.text || props.link) {
|
|
return 'p-0'
|
|
}
|
|
|
|
const hasIconLeft = !!props.iconLeft
|
|
const hasIconRight = !!props.iconRight
|
|
const hideText = props.hideText
|
|
|
|
switch (props.size) {
|
|
case 'sm':
|
|
if (hideText) return 'w-6'
|
|
if (hasIconLeft) return 'py-1 pr-2 pl-1'
|
|
if (hasIconRight) return 'py-1 pl-2 pr-1'
|
|
return 'px-2 py-1'
|
|
case 'lg':
|
|
if (hideText) return 'w-10'
|
|
if (hasIconLeft) return 'py-2 pr-6 pl-4'
|
|
if (hasIconRight) return 'py-2 pl-6 pr-4'
|
|
return 'px-6 py-2'
|
|
case 'base':
|
|
default:
|
|
if (hideText) return 'w-8'
|
|
if (hasIconLeft) return 'py-1 pr-4 pl-2'
|
|
if (hasIconRight) return 'py-1 pl-4 pr-2'
|
|
return 'px-4 py-1'
|
|
}
|
|
})
|
|
|
|
const generalClasses = computed(() => {
|
|
const baseClasses = [
|
|
'inline-flex justify-center items-center',
|
|
'text-center select-none whitespace-nowrap',
|
|
'outline outline-2 outline-transparent',
|
|
'transition duration-200 ease-in-out focus-visible:outline-outline-4'
|
|
]
|
|
|
|
const additionalClasses = []
|
|
|
|
if (!props.text && !props.link) {
|
|
additionalClasses.push('rounded-md border')
|
|
}
|
|
|
|
if (props.fullWidth) {
|
|
additionalClasses.push('w-full')
|
|
} else if (!props.hideText) {
|
|
additionalClasses.push('max-w-max')
|
|
}
|
|
if (isDisabled.value) {
|
|
additionalClasses.push('cursor-not-allowed opacity-60')
|
|
}
|
|
|
|
return [...baseClasses, ...additionalClasses].join(' ')
|
|
})
|
|
|
|
const buttonClasses = computed(() => {
|
|
return [
|
|
generalClasses.value,
|
|
sizeClasses.value,
|
|
bgAndBorderClasses.value,
|
|
paddingClasses.value
|
|
].join(' ')
|
|
})
|
|
|
|
const iconClasses = computed(() => {
|
|
const classParts: string[] = ['shrink-0']
|
|
|
|
if (props.loading) {
|
|
classParts.push('animate-spin')
|
|
}
|
|
|
|
switch (props.size) {
|
|
case 'sm':
|
|
classParts.push('h-5 w-5 p-0.5')
|
|
break
|
|
case 'lg':
|
|
classParts.push('h-6 w-6 p-1')
|
|
break
|
|
case 'base':
|
|
default:
|
|
classParts.push('h-6 w-6 p-1')
|
|
break
|
|
}
|
|
|
|
return classParts.join(' ')
|
|
})
|
|
|
|
const onClick = (e: MouseEvent) => {
|
|
if (isDisabled.value) {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
e.stopImmediatePropagation()
|
|
return
|
|
}
|
|
|
|
emit('click', e)
|
|
}
|
|
</script>
|