chore(example): dynamic editor

This commit is contained in:
Benjamin Canac
2021-11-18 18:50:44 +01:00
parent 0de12aac20
commit a5a78f9b94
3 changed files with 252 additions and 27 deletions

View File

@@ -1,36 +1,53 @@
<template>
<div class="mx-auto max-w-7xl flex flex-col">
Welcome
<UButton class="ml-3" variant="primary" icon="mdi:bed-single">
toto
</UButton>
<div class="h-full bg-tw-gray-50 min-h-screen">
<UContainer class="py-8">
<div class="lg:grid lg:grid-cols-12 lg:gap-10 lg:relative">
<aside class="pb-8 lg:pb-0 lg:sticky lg:top-0 px-4 sm:px-6 lg:px-0 lg:pt-8 lg:-mt-8 lg:self-start lg:col-span-3" style="position: sticky;">
<nav class="space-y-3">
<div class="font-bold text-lg pb-6">
@nuxthq/ui
</div>
<UIcon name="mdi:bullhorn" class="w-4 h-4 text-black" />
<ULink
v-for="component in components"
:key="component.name"
:to="component.to"
class="text-sm font-medium block w-full"
active-class="text-primary-600"
inactive-class="text-tw-gray-900"
>
{{ component.name }}
</ULink>
</nav>
</aside>
<UAvatar class="ml-3" src="https://picsum.photos/200/300" />
<UButton @click="toggleModalIsOpen()">
Toggle modal!
</UButton>
{{ isModalOpen }}
<UModal v-model="isModalOpen" title="Modal">
Body
</UModal>
<!-- <UPopover v-slot="{ open }">
<UButton trailing variant="white" :icon="open ? 'heroicons-outline:chevron-up' : 'heroicons-outline:chevron-down'">
toto
</UButton>
</UPopover> -->
<div class="space-y-6 sm:px-6 lg:px-0 lg:col-span-9">
<NuxtPage />
</div>
</div>
</UContainer>
</div>
</template>
<script setup>
const isModalOpen = ref(false)
useMeta({
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
],
htmlAttrs: {
class: 'bg-tw-gray-50'
}
})
function toggleModalIsOpen () {
isModalOpen.value = !isModalOpen.value
}
const components = [
{ name: 'Avatar', to: '/Avatar' },
{ name: 'Button', to: '/Button' },
{ name: 'Badge', to: '/Badge' },
{ name: 'Dropdown', to: '/Dropdown' },
{ name: 'Icon', to: '/Icon' },
{ name: 'Toggle', to: '/Toggle' },
{ name: 'Card', to: '/Card' },
{ name: 'Modal', to: '/Modal' },
{ name: 'Select', to: '/Select' }
]
</script>

View File

@@ -0,0 +1,102 @@
<template>
<UCard background-class="bg-tw-gray-100">
<div class="flex justify-center">
<component :is="is" v-bind="boundProps" />
</div>
<template v-if="props.length" #footer>
<div class="space-y-3">
<UInputGroup
v-for="prop of props"
:key="prop.key"
:name="prop.key"
:label="prop.key"
>
<UToggle
v-if="prop.type === 'Boolean'"
v-model="prop.value"
:name="prop.key"
:label="prop.key"
/>
<USelect
v-else-if="prop.values"
v-model="prop.value"
:name="prop.key"
:options="prop.values"
size="sm"
/>
<UInput
v-else-if="prop.type === 'String'"
v-model="prop.value"
:name="prop.key"
size="sm"
autocomplete="off"
/>
<UInput
v-else-if="prop.type === 'Number'"
type="number"
:value="prop.value"
:name="prop.key"
size="sm"
autocomplete="off"
@input="(value) => inputProp(prop.key, value)"
/>
<p v-else class="text-sm text-tw-gray-400">
This prop of type <span class="p-0.5 rounded bg-gray-100 dark:bg-gray-900">{{ prop.type }}</span> is not yet supported.
</p>
</UInputGroup>
</div>
</template>
</UCard>
</template>
<script setup>
const nuxtApp = useNuxtApp()
const { params } = useRoute()
const is = `U${params.component}`
const component = nuxtApp.vueApp.component(is)
const { props: componentProps } = await component.__asyncLoader()
const refProps = Object.entries(componentProps).map(([key, prop]) => {
let value = prop.default
let type = prop.type
if (Array.isArray(type)) {
type = type[0].name
} else {
type = type.name
}
let values
if (prop.validator) {
const result = prop.validator.toString().match(/\[.*\]/g, '')[0]
values = JSON.parse(result.replace(/'/g, '"'))
}
if (type === 'Boolean') {
value = value === 'true'
} else if (type === 'String') {
value = value === 'undefined' ? '' : value
value = value === 'null' ? '' : value
value = (value || '').replace(/^'(.*)'$/, '$1')
}
return {
key,
type,
value,
values
}
})
const props = ref(refProps)
const boundProps = computed(() => {
const bound = {}
for (const prop of props.value) {
bound[prop.key] = prop.value
}
return bound
})
</script>

106
example/pages/index.vue Normal file
View File

@@ -0,0 +1,106 @@
<template>
<div class="mx-auto max-w-5xl py-12 space-y-4">
<div>
<UButton variant="primary" icon="heroicons-outline:bell">
toto
</UButton>
</div>
<div>
<UAvatar src="https://picsum.photos/200/300" />
</div>
<div>
<UButton @click="toggleModalIsOpen()">
Toggle modal!
</UButton>
<UModal v-model="isModalOpen" title="Modal">
Body
</UModal>
</div>
<div>
<UDropdown v-slot="{ open }" :items="dropdownItems" placement="bottom-start">
<UButton variant="white" :icon="open ? 'heroicons-solid:chevron-up' : 'heroicons-solid:chevron-down'" trailing icon-class="transition">
Open menu!
</UButton>
</UDropdown>
</div>
<div>
<UDropdown :items="customDropdownItems" placement="bottom-end">
<button>
<UAvatar src="https://picsum.photos/200/300" />
</button>
<template #item-with-avatar="{ item }">
<UAvatar v-if="item.avatar" :src="item.avatar" size="xs" class="mr-3" />
{{ item.label }}
</template>
</UDropdown>
</div>
<div>
<UToggle v-model="isSwitchEnabled" />
</div>
<!-- <UPopover v-slot="{ open }">
<UButton trailing variant="white" :icon="open ? 'heroicons-outline:chevron-up' : 'heroicons-outline:chevron-down'">
toto
</UButton>
</UPopover> -->
</div>
</template>
<script setup>
const isModalOpen = ref(false)
const isSwitchEnabled = ref(false)
function toggleModalIsOpen () {
isModalOpen.value = !isModalOpen.value
}
function clickItem () {
// eslint-disable-next-line no-console
console.warn('clicked')
}
const dropdownItems = [
[{
label: 'Edit',
icon: 'heroicons-solid:pencil',
click: () => clickItem()
}, {
label: 'Duplicate',
icon: 'heroicons-solid:duplicate'
}],
[{
label: 'Archive',
icon: 'heroicons-solid:archive'
}, {
label: 'Move',
icon: 'heroicons-solid:external-link'
}],
[{
label: 'Delete',
icon: 'heroicons-solid:trash'
}]
]
const customDropdownItems = [
[{
label: 'benjamincanac',
avatar: 'https://picsum.photos/200/300',
href: 'https://google.fr',
target: '_blank',
slot: 'item-with-avatar'
}],
[{
label: 'About',
icon: 'heroicons-solid:plus',
to: '/about'
}]
]
</script>