Skip to content

Commit

Permalink
feat: add buttons controls (#312)
Browse files Browse the repository at this point in the history
Co-authored-by: Guillaume Chau <guillaume.b.chau@gmail.com>
  • Loading branch information
hugoattal and Akryum committed Oct 4, 2022
1 parent dde9249 commit 7bc1b60
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 7 deletions.
2 changes: 1 addition & 1 deletion packages/histoire-app/src/app/style/main.pcss
Expand Up @@ -24,7 +24,7 @@ a {
text-decoration: inherit;
}

input {
input, button {
font-family: inherit;
}

Expand Down
16 changes: 12 additions & 4 deletions packages/histoire-controls/src/components/HstWrapper.vue
Expand Up @@ -5,15 +5,23 @@ export default {
</script>

<script lang="ts" setup>
import { withDefaults, computed } from 'vue'
import { VTooltip as vTooltip } from 'floating-vue'
defineProps<{
const props = withDefaults(defineProps<{
title?: string
}>()
tag?: string
}>(), {
tag: 'label',
})
</script>

<template>
<label class="htw-p-2 hover:htw-bg-primary-100 dark:hover:htw-bg-primary-800 htw-flex htw-gap-2 htw-flex-wrap">
<component
:is="tag"
class="htw-p-2 hover:htw-bg-primary-100 dark:hover:htw-bg-primary-800 htw-flex htw-gap-2 htw-flex-wrap"
>
<span
v-tooltip="{
content: title,
Expand All @@ -30,5 +38,5 @@ defineProps<{
</span>
<slot name="actions" />
</span>
</label>
</component>
</template>
@@ -0,0 +1,30 @@
<script lang="ts" setup>
import HstButton from './HstButton.vue'
const variants: Array<{name: string, bind?: unknown}> = [
{ name: 'Default' },
{ name: 'Primary', bind: { color: 'primary' } },
{ name: 'Flat', bind: { color: 'flat' } },
]
</script>

<template>
<Story
title="HstButton"
group="controls"
:layout="{ type: 'grid', width: '200px', iframe: false }"
>
<Variant
v-for="(variant, key) of variants"
:key="key"
:title="variant.name"
>
<HstButton
v-bind="variant.bind"
class="htw-p-2"
>
Click me!
</HstButton>
</Variant>
</Story>
</template>
26 changes: 26 additions & 0 deletions packages/histoire-controls/src/components/button/HstButton.vue
@@ -0,0 +1,26 @@
<script lang="ts">
export default {
name: 'HstButton',
}
</script>

<script setup lang="ts">
const colors = {
default: 'htw-bg-gray-200 dark:htw-bg-gray-750 htw-text-gray-900 dark:htw-text-gray-100 hover:htw-bg-primary-200 dark:hover:htw-bg-primary-900',
primary: 'htw-bg-primary-500 hover:htw-bg-primary-600 htw-text-white dark:htw-text-black',
flat: 'htw-bg-transparent hover:htw-bg-gray-500/20 htw-text-gray-900 dark:htw-text-gray-100',
}
defineProps<{
color?: keyof typeof colors
}>()
</script>

<template>
<button
class="htw-cursor-pointer htw-rounded-sm"
:class="colors[color ?? 'default']"
>
<slot />
</button>
</template>
@@ -0,0 +1,51 @@
<script lang="ts" setup>
import HstButtonGroup from './HstButtonGroup.vue'
const options = {
slow: 'Slow',
fast: 'Fast',
max: 'Max',
}
const flatOptions = Object.keys(options)
const objectOptions = Object.keys(options).map(key => ({
label: options[key],
value: key,
}))
function initState () {
return {
speed: flatOptions[0],
}
}
</script>

<template>
<Story
title="HstButtonGroup"
group="controls"
:layout="{ type: 'single', iframe: false }"
>
<Variant
title="playground"
:init-state="initState"
>
<template #default="{ state }">
<HstButtonGroup
v-model="state.speed"
title="Label"
:options="objectOptions"
/>
</template>

<template #controls="{ state }">
<HstButtonGroup
v-model="state.speed"
title="Label"
:options="objectOptions"
/>
</template>
</Variant>
</Story>
</template>
@@ -0,0 +1,64 @@
<script lang="ts">
export default {
name: 'HstButtonGroup',
}
</script>

<script setup lang="ts">
import { computed, ComputedRef } from 'vue'
import HstWrapper from '../HstWrapper.vue'
import { HstControlOption } from '../../types'
import HstButton from './HstButton.vue'
const props = defineProps<{
title?: string
modelValue: string
options: HstControlOption[]
}>()
const formattedOptions: ComputedRef<Record<string, string>> = computed(() => {
if (Array.isArray(props.options)) {
return Object.fromEntries(props.options.map((value: string | HstControlOption) => {
if (typeof value === 'string') {
return [value, value]
} else {
return [value.value, value.label]
}
}))
}
return props.options
})
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()
function selectOption (value: string) {
emit('update:modelValue', value)
}
</script>

<template>
<HstWrapper
tag="div"
role="group"
:title="title"
class="htw-flex-nowrap htw-items-center"
>
<div class="htw-flex htw-gap-px htw-border htw-border-solid htw-border-black/25 dark:htw-border-white/25 htw-rounded-sm htw-p-px">
<HstButton
v-for="( label, value ) in formattedOptions"
:key="value"
class="htw-px-1 htw-h-[22px] htw-flex-1 !htw-rounded-[3px]"
:color="value === modelValue ? 'primary' : 'flat'"
:rounded="false"
@click="selectOption(value)"
>
{{ label }}
</HstButton>
</div>
<template #actions>
<slot name="actions" />
</template>
</HstWrapper>
</template>
@@ -1,7 +1,6 @@
<script lang="ts">
export default {
name: 'HstNumber',
inheritAttrs: false,
}
</script>
Expand Down Expand Up @@ -86,7 +85,7 @@ onUnmounted(() => {
:class="{
'htw-select-none': isDragging,
}"
class="htw-text-inherit htw-bg-transparent htw-w-full htw-outline-none htw-pl-2 htw-py-1 -htw-my-1 htw-border htw-border-solid htw-border-black/25 dark:htw-border-white/25 focus:htw-border-primary-500 dark:focus:htw-border-primary-500 htw-rounded-sm htw-cursor-ew-resize"
class="htw-text-inherit htw-bg-transparent htw-w-full htw-outline-none htw-pl-2 htw-py-1 -htw-my-1 htw-border htw-border-solid htw-border-black/25 dark:htw-border-white/25 focus:htw-border-primary-500 dark:focus:htw-border-primary-500 htw-rounded-sm htw-cursor-ew-resize htw-box-border"
>

<template #actions>
Expand Down
Expand Up @@ -14,6 +14,7 @@ const state = reactive({
<Story
title="HstSlider"
group="controls"
:layout="{ type: 'single', iframe: false }"
>
<HstSlider
v-model="state.value"
Expand Down
6 changes: 6 additions & 0 deletions packages/histoire-controls/src/index.ts
@@ -1,3 +1,5 @@
import HstButtonVue from './components/button/HstButton.vue'
import HstButtonGroupVue from './components/button/HstButtonGroup.vue'
import HstCheckboxVue from './components/checkbox/HstCheckbox.vue'
import HstCheckboxListVue from './components/checkbox/HstCheckboxList.vue'
import HstTextVue from './components/text/HstText.vue'
Expand All @@ -11,6 +13,8 @@ import HstTokenGridVue from './components/design-tokens/HstTokenGrid.vue'
import HstCopyIconVue from './components/HstCopyIcon.vue'
import HstRadioVue from './components/radio/HstRadio.vue'

export const HstButton = HstButtonVue
export const HstButtonGroup = HstButtonGroupVue
export const HstCheckbox = HstCheckboxVue
export const HstCheckboxList = HstCheckboxListVue
export const HstText = HstTextVue
Expand All @@ -25,6 +29,8 @@ export const HstCopyIcon = HstCopyIconVue
export const HstRadio = HstRadioVue

export const components = {
HstButton,
HstButtonGroup,
HstCheckbox,
HstCheckboxList,
HstText,
Expand Down
6 changes: 6 additions & 0 deletions packages/histoire-plugin-svelte/src/index.ts
Expand Up @@ -57,6 +57,12 @@ export interface Hst {
Story: typeof SvelteComponentTyped<StoryProps>
Variant: typeof SvelteComponentTyped<VariantProps>
// Controls
Button: typeof SvelteComponentTyped
ButtonGroup: typeof SvelteComponentTyped<{
value: string
options: HstControlOption[]
title?: string
}>
Checkbox: typeof SvelteComponentTyped<{
value: boolean
title: string
Expand Down

0 comments on commit 7bc1b60

Please sign in to comment.