woodpecker/web/src/components/atomic/Button.vue
Divya Jain e2ab8a46ed
Header and Tabs UI Improvements (#1290)
Some improvements to the Page Header and Tab UI.

Original |  New
:--------:|:-------:

![image](https://user-images.githubusercontent.com/62170586/197360886-046f1016-ca39-4b69-8134-99ba88e3a0c2.png)
|
![image](https://user-images.githubusercontent.com/62170586/197360819-7efd0d82-1412-465d-aefa-039164f97465.png)

![image](https://user-images.githubusercontent.com/62170586/197360872-f2ece5fd-7c0b-4e2c-8629-31524a412af5.png)
|
![image](https://user-images.githubusercontent.com/62170586/197360830-49f09e0d-619e-4fa9-8e38-8d05d9404185.png)

![image](https://user-images.githubusercontent.com/62170586/197281776-e3de6441-9417-4614-8b25-1aaef0b8da61.png)
|
![image](https://user-images.githubusercontent.com/62170586/197281698-40c66d34-76f3-4fd5-97e3-1c422b74844c.png)

![image](https://user-images.githubusercontent.com/62170586/196609248-ff150c6e-2995-4bcc-8573-49ffaf388446.png)
|
![image](https://user-images.githubusercontent.com/62170586/197323734-7c1a1b79-0f41-4bf2-96a3-dd38df9e1415.png)

![image](https://user-images.githubusercontent.com/62170586/196609329-b7a6f37e-e8c2-4004-a98b-73f837122ff8.png)
|
![image](https://user-images.githubusercontent.com/62170586/197323882-10141ffd-7411-4493-8291-b8000adc3cc5.png)


What?
- Create a new Scaffold component, which includes the header and tabs
required for a page.
- Use this component to wrap all the views that have a header.
- Ensures consistency in headers between different pages.
- [x] Add support to use custom html/component in place of title (for
repo page, pipeline page, etc)
- [x] Add support of right icon buttons (for repo page, pipeline page,
etc)
- [x] Refactor tabs handling using compositions (useTabsProvider, useTabsClient)
- [x] Make new header ui resposive
2022-10-28 00:55:07 +02:00

126 lines
3.3 KiB
Vue

<template>
<button
type="button"
class="relative flex items-center py-1 px-2 rounded-md border shadow-sm cursor-pointer transition-all duration-150 focus:outline-none overflow-hidden disabled:opacity-50 disabled:cursor-not-allowed"
:class="{
'bg-white hover:bg-gray-200 border-gray-300 text-color dark:bg-dark-gray-600 dark:border-dark-400 dark:hover:bg-dark-gray-800':
color === 'gray',
'bg-lime-600 hover:bg-lime-700 border-lime-800 text-white dark:text-gray-400 dark:bg-lime-900 dark:hover:bg-lime-800':
color === 'green',
'bg-cyan-600 hover:bg-cyan-700 border-cyan-800 text-white dark:text-gray-400 dark:bg-cyan-900 dark:hover:bg-cyan-800':
color === 'blue',
'bg-red-500 hover:bg-red-600 border-red-700 text-white dark:text-gray-400 dark:bg-red-900 dark:hover:bg-red-800':
color === 'red',
...passedClasses,
}"
:title="title"
:disabled="disabled"
@click="doClick"
>
<slot>
<Icon v-if="startIcon" :name="startIcon" class="mr-1 !w-6 !h-6" :class="{ invisible: isLoading }" />
<span :class="{ invisible: isLoading }">{{ text }}</span>
<Icon v-if="endIcon" :name="endIcon" class="ml-2 w-6 h-6" :class="{ invisible: isLoading }" />
<div
class="absolute left-0 top-0 right-0 bottom-0 flex items-center justify-center"
:class="{
'opacity-100': isLoading,
'opacity-0': !isLoading,
'bg-white dark:bg-dark-gray-700': color === 'gray',
'bg-lime-700': color === 'green',
'bg-cyan-700': color === 'blue',
'bg-red-600': color === 'red',
}"
>
<Icon name="loading" class="animate-spin" />
</div>
</slot>
</button>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import { RouteLocationRaw, useRouter } from 'vue-router';
import Icon, { IconNames } from '~/components/atomic/Icon.vue';
export default defineComponent({
name: 'Button',
components: { Icon },
props: {
text: {
type: String,
default: null,
},
title: {
type: String,
default: null,
},
disabled: {
type: Boolean,
required: false,
},
to: {
type: [String, Object, null] as PropType<RouteLocationRaw | null>,
default: null,
},
color: {
type: String as PropType<'blue' | 'green' | 'red' | 'gray'>,
default: 'gray',
},
startIcon: {
type: String as PropType<IconNames | null>,
default: null,
},
endIcon: {
type: String as PropType<IconNames | null>,
default: null,
},
isLoading: {
type: Boolean,
},
},
setup(props, { attrs }) {
const router = useRouter();
async function doClick() {
if (props.isLoading) {
return;
}
if (!props.to) {
return;
}
if (typeof props.to === 'string' && props.to.startsWith('http')) {
window.location.href = props.to;
return;
}
await router.push(props.to);
}
const passedClasses = computed(() => {
const classes: Record<string, boolean> = {};
const origClass = (attrs.class as string) || '';
origClass.split(' ').forEach((c) => {
classes[c] = true;
});
return classes;
});
return { doClick, passedClasses };
},
});
</script>