Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add presets (fix #53) #88

Merged
merged 12 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions examples/vue3/src/components/Controls.story.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script lang="ts" setup>
const options = [
{ label: 'Crash Bandicoot', value: 'crash-bandicoot' },
{ label: 'The Last of Us', value: 'the-last-of-us' },
{ label: 'Ghost of Tsushima', value: 'ghost-of-tsushima' },
]
const options = {
'crash-bandicoot': 'Crash Bandicoot',
'the-last-of-us': 'The Last of Us',
'ghost-of-tsushima': 'Ghost of Tsushima',
}

function initState () {
return {
text: 'Hello',
Expand Down
2 changes: 1 addition & 1 deletion packages/histoire-controls/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
},
"devDependencies": {
"@peeky/test": "^0.13.5",
"@types/node": "^17.0.23",
"@types/node": "^17.0.32",
"@vitejs/plugin-vue": "^2.3.1",
"@vue/test-utils": "^2.0.0-rc.19",
"@vueuse/core": "^8.2.5",
Expand Down
119 changes: 119 additions & 0 deletions packages/histoire-controls/src/components/select/CustomSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<script lang="ts">
export default {
name: 'CustomSelect',
}
</script>

<script lang="ts" setup>
import { Dropdown as VDropdown } from 'floating-vue'
import { computed, ComputedRef } from 'vue'
import { Icon } from '@iconify/vue'

export interface SelectOption {
label: string
value: string
}

const props = defineProps<{
modelValue: string
options: Record<string, string> | string[] | SelectOption[]
}>()

const emits = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()

const formattedOptions: ComputedRef<Record<string, string>> = computed(() => {
if (Array.isArray(props.options)) {
return Object.fromEntries(props.options.map((value: string | SelectOption) => {
if (typeof value === 'string') {
return [value, value]
} else {
return [value.value, value.label]
}
}))
}
return props.options
})

const selectedLabel = computed(() => formattedOptions.value[props.modelValue])

function selectValue (value: string, hide: () => void) {
emits('update:modelValue', value)
hide()
}
</script>

<template>
<VDropdown
auto-size
>
<div
class="htw-cursor-pointer htw-w-full htw-outline-none htw-px-2 htw-h-[27px] -htw-my-1 htw-border htw-border-solid htw-border-black/25 dark:htw-border-white/25 hover:htw-border-primary-500 dark:hover:htw-border-primary-500 htw-rounded-sm htw-flex htw-gap-2 htw-items-center htw-leading-normal"
>
<div class="htw-flex-1 htw-truncate">
<slot :label="selectedLabel">
{{ selectedLabel }}
</slot>
</div>
<Icon
icon="carbon:chevron-sort"
class="htw-w-4 htw-h-4 htw-flex-none htw-ml-auto"
/>
</div>
<template #popper="{ hide }">
<div class="htw-flex htw-flex-col htw-bg-gray-50 dark:htw-bg-gray-700">
<div
v-for="( label, value ) in formattedOptions"
v-bind="{ ...$attrs, class: null, style: null }"
:key="label"
class="htw-px-2 htw-py-1 htw-cursor-pointer hover:htw-bg-primary-100 dark:hover:htw-bg-primary-700"
:class="{
'htw-bg-primary-200 dark:htw-bg-primary-800': props.modelValue === value,
}"
@click="selectValue(value, hide)"
>
{{ label }}
</div>
</div>
</template>
</VDropdown>
</template>

<style lang="postcss">
/* @TODO custom themes */

.v-popper {
line-height: 0;
}

.v-popper--theme-dropdown {
.htw-dark & {
.v-popper__inner {
@apply htw-bg-gray-700 htw-border-gray-850 htw-text-gray-100;
}

.v-popper__arrow-inner {
@apply htw-border-gray-700;
}

.v-popper__arrow-outer {
@apply htw-border-gray-850;
}
}

&.v-popper__popper--show-from .v-popper__wrapper {
transform: scale(.75);
}

&.v-popper__popper--show-to .v-popper__wrapper {
transform: none;
transition: transform .15s cubic-bezier(0, 1, .5, 1);
}
}

.v-popper__popper:focus-visible {
outline: none;
}

</style>
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
<script lang="ts" setup>
import HstSelect from './HstSelect.vue'

const options = [
{ label: 'Crash Bandicoot', value: 'crash-bandicoot' },
{ label: 'The Last of Us', value: 'the-last-of-us' },
{ label: 'Ghost of Tsushima', value: 'ghost-of-tsushima' },
]
const options = {
'crash-bandicoot': 'Crash Bandicoot',
'the-last-of-us': 'The Last of Us',
'ghost-of-tsushima': 'Ghost of Tsushima',
}

const flatOptions = Object.keys(options)

const flatOptions = options.map(option => option.label)
const objectOptions = Object.keys(options).map(key => ({
label: options[key],
value: key,
}))

function initState () {
return {
Expand Down Expand Up @@ -66,7 +71,7 @@ function initState () {
</Variant>

<Variant
title="options-as-array-of-objects"
title="options-as-object"
:init-state="initState"
>
<template #default="{ state }">
Expand All @@ -86,6 +91,27 @@ function initState () {
</template>
</Variant>

<Variant
title="options-as-array-of-objects"
:init-state="initState"
>
<template #default="{ state }">
<pre class="htw-text-xs htw-bg-gray-50 dark:htw-bg-gray-600 htw-rounded htw-p-4">{{ objectOptions }}</pre>
<HstSelect
v-model="state.select"
title="Games"
:options="objectOptions"
/>
</template>
<template #controls="{ state }">
<HstSelect
v-model="state.select"
title="Games"
:options="objectOptions"
/>
</template>
</Variant>

<Variant
title="options-as-array-of-strings"
:init-state="initState"
Expand Down
61 changes: 17 additions & 44 deletions packages/histoire-controls/src/components/select/HstSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,24 @@
export default {
name: 'HstSelect',
}

export type { SelectOption as HstSelectOption } from './CustomSelect.vue'
</script>

<script lang="ts" setup>
import { ref, computed, ComputedRef, PropType } from 'vue'
import { ref } from 'vue'
import HstWrapper from '../HstWrapper.vue'
import CustomSelect, { SelectOption } from './CustomSelect.vue'

export interface HstSelectOptions {
label: string
value: string
}

const props = defineProps({
title: {
type: String,
default: '',
},
modelValue: {
type: String,
default: null,
},
options: {
type: Array as PropType<HstSelectOptions[] | string[]>,
required: true,
default: () => [],
},
})

const formattedOtions: ComputedRef<HstSelectOptions[]> =
computed(() => props.options.map(option => typeof option === 'string' ? { label: option, value: option } as HstSelectOptions : option as HstSelectOptions))
const props = defineProps<{
title?: string
modelValue: string
options: Record<string, string> | string[] | SelectOption[]
}>()

const emit = defineEmits({
'update:modelValue': (newValue: string) => true,
})
const emits = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()

const select = ref<HTMLInputElement>()
</script>
Expand All @@ -45,23 +30,11 @@ const select = ref<HTMLInputElement>()
class="htw-cursor-text htw-items-center"
:class="$attrs.class"
:style="$attrs.style"
@click="select.focus()"
>
<select
ref="select"
class="htw-text-inherit htw-bg-transparent htw-w-full htw-outline-none htw-px-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"
:value="modelValue"
@input="emit('update:modelValue', ($event.target as HTMLInputElement).value)"
>
<option
v-for="{ label, value } in formattedOtions"
v-bind="{ ...$attrs, class: null, style: null }"
:key="label"
class="dark:htw-bg-gray-600"
:value="value"
>
{{ label }}
</option>
</select>
<CustomSelect
:options="options"
:model-value="modelValue"
@update:model-value="emits('update:modelValue', $event)"
/>
</HstWrapper>
</template>
2 changes: 1 addition & 1 deletion packages/histoire/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"@types/flexsearch": "^0.7.3",
"@types/fs-extra": "^9.0.13",
"@types/markdown-it": "^12.2.3",
"@types/node": "^17.0.23",
"@types/node": "^17.0.32",
"@vitejs/plugin-vue": "^2.3.1",
"autoprefixer": "^10.4.4",
"concurrently": "^7.1.0",
Expand Down
72 changes: 72 additions & 0 deletions packages/histoire/src/client/app/components/base/BaseSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<script lang="ts" setup>
import { Dropdown as VDropdown } from 'floating-vue'
import { computed, ComputedRef } from 'vue'
import { Icon } from '@iconify/vue'

const props = defineProps<{
modelValue: string
options: Record<string, string> | Array<string>
}>()

const emits = defineEmits<{
(e: 'update:modelValue', value: string): void
(e: 'select', value: string): void
}>()

const formattedOptions: ComputedRef<Record<string, string>> = computed(() => {
if (Array.isArray(props.options)) {
return Object.fromEntries(props.options.map((value) => [value, value]))
}
return props.options
})

const selectedLabel = computed(() => formattedOptions.value[props.modelValue])

function selectValue (value: string, hide: () => void) {
emits('update:modelValue', value)
emits('select', value)
hide()
}
</script>

<template>
<VDropdown
auto-size
>
<div
class="htw-cursor-pointer htw-w-full htw-outline-none htw-px-2 htw-h-[27px] -htw-my-1 htw-border htw-border-solid htw-border-black/25 dark:htw-border-white/25 hover:htw-border-primary-500 dark:hover:htw-border-primary-500 htw-rounded-sm htw-flex htw-gap-2 htw-items-center htw-leading-normal"
>
<div class="htw-flex-1 htw-truncate">
<slot :label="selectedLabel">
{{ selectedLabel }}
</slot>
</div>
<Icon
icon="carbon:chevron-sort"
class="htw-w-4 htw-h-4 htw-flex-none htw-ml-auto"
/>
</div>
<template #popper="{ hide }">
<div class="htw-flex htw-flex-col htw-bg-gray-50 dark:htw-bg-gray-700">
<div
v-for="( label, value ) in formattedOptions"
v-bind="{ ...$attrs, class: null, style: null }"
:key="label"
class="htw-px-2 htw-py-1 htw-cursor-pointer hover:htw-bg-primary-100 dark:hover:htw-bg-primary-700"
:class="{
'htw-bg-primary-200 dark:htw-bg-primary-800': props.modelValue === value,
}"
@click="selectValue(value, hide)"
>
<slot
name="option"
:label="label"
:value="value"
>
{{ label }}
</slot>
</div>
</div>
</template>
</VDropdown>
</template>
Loading