Skip to content

Commit

Permalink
feat: added NeButton
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaile committed Dec 11, 2023
1 parent bfcb948 commit 8949ce1
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 1 deletion.
102 changes: 102 additions & 0 deletions src/components/NeButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<!--
Copyright (C) 2023 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->

<script lang="ts" setup>
import { computed, type PropType } from 'vue'
import NeSpinner, { type SpinnerColor, type SpinnerSize } from './NeSpinner.vue'

export type ButtonKind = 'primary' | 'secondary' | 'tertiary' | 'danger'
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'

const props = defineProps({
kind: {
type: String as PropType<ButtonKind>,
default: 'secondary'
},
size: {
type: String as PropType<ButtonSize>,
default: 'md'
},
loading: {
type: Boolean,
default: false
},
loadingPosition: {
type: String as PropType<'prefix' | 'suffix'>,
default: 'prefix'
},
disabled: {
type: Boolean,
default: false
}
})

const sizeStyle: { [index: string]: string } = {
xs: 'rounded px-2 py-1 text-xs',
sm: 'rounded px-2 py-1 text-sm',
md: 'rounded-md px-2.5 py-1.5 text-sm',
lg: 'rounded-md px-3 py-2 text-sm',
xl: 'rounded-md px-3.5 py-2.5 text-sm'
}
const kindStyle: { [index: string]: string } = {
primary:
'shadow-sm bg-primary-700 text-white hover:bg-primary-800 focus:ring-offset-white dark:bg-primary-500 dark:text-gray-950 dark:hover:bg-primary-300 dark:focus:ring-offset-primary-950',
secondary:
'shadow-sm ring-1 text-primary-700 ring-gray-300 hover:bg-gray-200/70 hover:text-primary-800 focus:ring-offset-white dark:text-primary-500 dark:ring-gray-500 dark:hover:bg-gray-600/30 dark:hover:text-primary-500 dark:focus:ring-offset-primary-950',
tertiary:
'text-primary-700 hover:text-primary-800 hover:bg-gray-200/70 focus:ring-offset-white dark:text-primary-500 dark:hover:text-primary-500 dark:hover:bg-gray-600/30 dark:focus:ring-offset-primary-950',
danger:
'shadow-sm bg-rose-700 text-white hover:bg-rose-800 focus:ring-offset-white dark:bg-rose-600 dark:text-white dark:hover:bg-rose-500 dark:focus:ring-offset-primary-950'
}

const spinnerColorStyle: { [index: string]: SpinnerColor } = {
primary: 'white',
secondary: 'primary',
tertiary: 'primary',
danger: 'white'
}

const spinnerSizeStyle: { [index: string]: SpinnerSize } = {
xs: '2.5',
sm: '3',
md: '4',
lg: '4',
xl: '5'
}

const loadingPrefix = computed(() => props.loading && props.loadingPosition === 'prefix')
const loadingSuffix = computed(() => props.loading && props.loadingPosition === 'suffix')
</script>

<template>
<button
class="font-semibold disabled:cursor-not-allowed disabled:opacity-50 transition-colors duration-200 focus:ring-2 focus:outline-none focus:ring-offset-2 focus:ring-primary-500 dark:focus:ring-primary-300"
:class="[kindStyle[props.kind], sizeStyle[props.size]]"
:disabled="disabled"
type="button"
>
<div class="flex items-center justify-center">
<!-- prefix -->
<div v-if="$slots.prefix || loadingPrefix" class="mr-2">
<NeSpinner
v-if="loadingPrefix"
:color="spinnerColorStyle[kind]"
:size="spinnerSizeStyle[size]"
/>
<slot v-else name="prefix" />
</div>
<slot />
<!-- suffix -->
<div v-if="$slots.suffix || loadingSuffix" class="ml-2">
<NeSpinner
v-if="loadingSuffix"
:color="spinnerColorStyle[kind]"
:size="spinnerSizeStyle[size]"
/>
<slot v-else name="suffix" />
</div>
</div>
</button>
</template>
4 changes: 3 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import NeSideDrawer from '@/components/NeSideDrawer.vue'
import NeTitle from '@/components/NeTitle.vue'
import NeTooltip from '@/components/NeTooltip.vue'
import NeBadge from '@/components/NeBadge.vue'
import NeButton from '@/components/NeButton.vue'
// style import
import '@/main.css'

Expand All @@ -24,5 +25,6 @@ export {
NeSideDrawer,
NeTitle,
NeTooltip,
NeBadge
NeBadge,
NeButton
}
155 changes: 155 additions & 0 deletions stories/NeButton.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright (C) 2023 Nethesis S.r.l.
// SPDX-License-Identifier: GPL-3.0-or-later

import type { Meta, StoryObj } from '@storybook/vue3'
import { NeButton } from '../src/main'

const meta = {
title: 'Control/NeButton',
component: NeButton,
tags: ['autodocs'],
argTypes: {
size: { control: 'inline-radio', options: ['xs', 'sm', 'md', 'lg', 'xl'] },
kind: { control: 'inline-radio', options: ['primary', 'secondary', 'tertiary', 'danger'] },
loadingPosition: { control: 'inline-radio', options: ['prefix', 'suffix'] }
},
args: {
kind: 'secondary',
size: 'md',
disabled: false,
loading: false,
loadingPosition: 'prefix'
} // default values
} satisfies Meta<typeof NeButton>

export default meta
type Story = StoryObj<typeof meta>

const template = '<NeButton v-bind="args">Button</NeButton>'
export const Secondary: Story = {
render: (args) => ({
components: { NeButton },
setup() {
return { args }
},
template: template
}),
args: {
kind: 'secondary'
}
}

//// sample story with no need to specify the template:
// export const Secondary: Story = {
// args: {
// kind: 'secondary'
// }
// }

export const Primary: Story = {
render: (args) => ({
components: { NeButton },
setup() {
return { args }
},
template: template
}),
args: {
// ...Secondary.args, ////
kind: 'primary'
}
}

export const Tertiary: Story = {
render: (args) => ({
components: { NeButton },
setup() {
return { args }
},
template: template
}),
args: {
kind: 'tertiary'
}
}

export const Danger: Story = {
render: (args) => ({
components: { NeButton },
setup() {
return { args }
},
template: template
}),
args: {
kind: 'danger'
}
}

export const Loading: Story = {
render: (args) => ({
components: { NeButton },
setup() {
return { args }
},
template: template
}),
args: {
loading: true
}
}

const templateWithPrefix =
'<NeButton v-bind="args">\
<template #prefix>\
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">\
<path d="M3 1a1 1 0 000 2h1.22l.305 1.222a.997.997 0 00.01.042l1.358 5.43-.893.892C3.74 11.846 4.632 14 6.414 14H15a1 1 0 000-2H6.414l1-1H14a1 1 0 00.894-.553l3-6A1 1 0 0017 3H6.28l-.31-1.243A1 1 0 005 1H3zM16 16.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM6.5 18a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path>\
</svg>\
</template>\
Button\
</NeButton>'

export const WithPrefix: Story = {
render: (args) => ({
components: { NeButton },
setup() {
return { args }
},
template: templateWithPrefix
}),
args: {}
}

const templateWithSuffix =
'<NeButton v-bind="args">\
<template #suffix>\
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">\
<path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path>\
</svg>\
</template>\
Button\
</NeButton>'

export const WithSuffix: Story = {
render: (args) => ({
components: { NeButton },
setup() {
return { args }
},
template: templateWithSuffix
}),
args: { loadingPosition: 'suffix' }
}

export const Disabled: Story = {
render: (args) => ({
components: { NeButton },
setup() {
return { args }
},
template: template
}),
args: {
disabled: true
}
}

0 comments on commit 8949ce1

Please sign in to comment.