chore: migrate components to typescript setup (#55)

Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
Sylvain Marroufin
2022-05-09 16:05:07 +02:00
committed by GitHub
parent 6c2f93f262
commit 39bf242f78
19 changed files with 1538 additions and 1684 deletions

View File

@@ -53,89 +53,81 @@
</TransitionRoot>
</template>
<script>
<script setup lang="ts">
import { computed } from 'vue'
import { Dialog, DialogPanel, TransitionRoot, TransitionChild } from '@headlessui/vue'
import { classNames } from '../../utils/'
import Card from '../layout/Card'
import $ui from '#build/ui'
export default {
components: {
Dialog,
DialogPanel,
TransitionRoot,
TransitionChild,
Card
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
inheritAttrs: false,
props: {
modelValue: {
type: Boolean,
default: false
},
appear: {
type: Boolean,
default: false
},
baseClass: {
type: String,
default: () => $ui.modal.base
},
backgroundClass: {
type: String,
default: () => $ui.modal.background
},
shadowClass: {
type: String,
default: () => $ui.modal.shadow
},
ringClass: {
type: String,
default: () => $ui.modal.ring
},
roundedClass: {
type: String,
default: () => $ui.modal.rounded
},
widthClass: {
type: String,
default: () => $ui.modal.width,
validator (value) {
return ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'].map(width => `max-w-${width}`).includes(value)
}
appear: {
type: Boolean,
default: false
},
baseClass: {
type: String,
default: () => $ui.modal.base
},
backgroundClass: {
type: String,
default: () => $ui.modal.background
},
shadowClass: {
type: String,
default: () => $ui.modal.shadow
},
ringClass: {
type: String,
default: () => $ui.modal.ring
},
roundedClass: {
type: String,
default: () => $ui.modal.rounded
},
widthClass: {
type: String,
default: () => $ui.modal.width,
validator (value: string) {
return ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl'].map(width => `max-w-${width}`).includes(value)
}
},
emits: ['update:modelValue', 'close'],
setup (props, { emit }) {
const isOpen = computed({
get () {
return props.modelValue
},
set (value) {
emit('update:modelValue', value)
}
})
}
})
const modalClass = computed(() => {
return classNames(
props.baseClass,
const emit = defineEmits(['update:modelValue', 'close'])
const isOpen = computed({
get () {
return props.modelValue
},
set (value) {
emit('update:modelValue', value)
}
})
const modalClass = computed(() => {
return classNames(
props.baseClass,
`sm:${props.widthClass}`,
props.backgroundClass,
props.shadowClass,
props.ringClass,
props.roundedClass
)
})
)
})
return {
isOpen,
modalClass,
close (value) {
isOpen.value = value
emit('close')
}
}
}
function close (value: boolean) {
isOpen.value = value
emit('close')
}
</script>
<script lang="ts">
export default {
inheritAttrs: false
}
</script>

View File

@@ -55,148 +55,131 @@
</transition>
</template>
<script>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watchEffect } from 'vue'
import Icon from '../elements/Icon'
import Button from '../elements/Button'
import { useTimer } from '../../composables/useTimer'
export default {
components: {
Icon,
Button
const props = defineProps({
id: {
type: String,
required: true
},
props: {
id: {
type: String,
required: true
},
type: {
type: String,
required: true,
default: 'info',
validator (value) {
return ['info', 'success', 'error', 'warning'].includes(value)
}
},
title: {
type: String,
required: true
},
description: {
type: String,
default: null
},
icon: {
type: String,
default: null
},
timeout: {
type: Number,
default: 5000
},
undo: {
type: Function,
default: null
},
callback: {
type: Function,
default: null
type: {
type: String,
required: true,
default: 'info',
validator (value: string) {
return ['info', 'success', 'error', 'warning'].includes(value)
}
},
emits: ['close'],
setup (props, { emit }) {
let timer = null
const remaining = ref(props.timeout)
title: {
type: String,
required: true
},
description: {
type: String,
default: null
},
icon: {
type: String,
default: null
},
timeout: {
type: Number,
default: 5000
},
undo: {
type: Function,
default: null
},
callback: {
type: Function,
default: null
}
})
const iconName = computed(() => {
return props.icon || ({
warning: 'heroicons-outline:exclamation-circle',
info: 'heroicons-outline:information-circle',
success: 'heroicons-outline:check-circle',
error: 'heroicons-outline:x-circle'
})[props.type]
})
const emit = defineEmits(['close'])
const iconClass = computed(() => {
return ({
warning: 'text-orange-400',
info: 'text-blue-400',
success: 'text-green-400',
error: 'text-red-400'
})[props.type] || 'u-text-gray-400'
})
let timer: any = null
const remaining = ref(props.timeout)
const progressBarStyle = computed(() => {
const remainingPercent = remaining.value / props.timeout * 100
return { width: `${remainingPercent || 0}%` }
})
const iconName = computed(() => {
return props.icon || ({
warning: 'heroicons-outline:exclamation-circle',
info: 'heroicons-outline:information-circle',
success: 'heroicons-outline:check-circle',
error: 'heroicons-outline:x-circle'
})[props.type]
})
function onMouseover () {
if (timer) {
timer.pause()
}
}
const iconClass = computed(() => {
return ({
warning: 'text-orange-400',
info: 'text-blue-400',
success: 'text-green-400',
error: 'text-red-400'
})[props.type] || 'u-text-gray-400'
})
function onMouseleave () {
if (timer) {
timer.resume()
}
}
const progressBarStyle = computed(() => {
const remainingPercent = remaining.value / props.timeout * 100
return { width: `${remainingPercent || 0}%` }
})
function onClose () {
if (timer) {
timer.stop()
}
if (props.callback) {
props.callback()
}
emit('close')
}
function onUndo () {
if (timer) {
timer.stop()
}
if (props.undo) {
props.undo()
}
emit('close')
}
onMounted(() => {
if (!props.timeout) {
return
}
timer = useTimer(() => {
onClose()
}, props.timeout)
watchEffect(() => {
remaining.value = timer.remaining.value
})
})
onUnmounted(() => {
timer.stop()
})
return {
timer,
iconName,
iconClass,
progressBarStyle,
onMouseover,
onMouseleave,
onClose,
onUndo
}
function onMouseover () {
if (timer) {
timer.pause()
}
}
function onMouseleave () {
if (timer) {
timer.resume()
}
}
function onClose () {
if (timer) {
timer.stop()
}
if (props.callback) {
props.callback()
}
emit('close')
}
function onUndo () {
if (timer) {
timer.stop()
}
if (props.undo) {
props.undo()
}
emit('close')
}
onMounted(() => {
if (!props.timeout) {
return
}
timer = useTimer(() => {
onClose()
}, props.timeout)
watchEffect(() => {
remaining.value = timer.remaining.value
})
})
onUnmounted(() => {
timer.stop()
})
</script>

View File

@@ -24,135 +24,121 @@
</Popover>
</template>
<script>
<script setup lang="ts">
import type { Ref } from 'vue'
import { ref, onMounted } from 'vue'
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
import { usePopper } from '../../utils'
export default {
components: {
Popover,
PopoverButton,
PopoverPanel
const props = defineProps({
placement: {
type: String,
default: 'bottom'
},
props: {
placement: {
type: String,
default: 'bottom'
},
strategy: {
type: String,
default: 'fixed'
},
mode: {
type: String,
default: 'click',
validator: (value) => {
return ['click', 'hover'].includes(value)
}
},
wrapperClass: {
type: String,
default: 'relative'
},
containerClass: {
type: String,
default: 'z-10 py-2'
},
panelClass: {
type: String,
default: ''
strategy: {
type: String,
default: 'fixed'
},
mode: {
type: String,
default: 'click',
validator: (value: string) => {
return ['click', 'hover'].includes(value)
}
},
setup (props) {
const [trigger, container] = usePopper({
placement: props.placement,
strategy: props.strategy,
modifiers: [{
name: 'offset',
options: {
offset: 0
}
},
{
name: 'computeStyles',
options: {
gpuAcceleration: false,
adaptive: false
}
},
{
name: 'preventOverflow',
options: {
padding: 8
}
}]
})
const popoverApi = ref(null)
let openTimeout = null
let closeTimeout = null
onMounted(() => {
setTimeout(() => {
const popoverProvides = trigger.value?.$.provides
const popoverProvidesSymbols = Object.getOwnPropertySymbols(popoverProvides)
popoverApi.value = popoverProvidesSymbols.length && popoverProvides[popoverProvidesSymbols[0]]
// stop trigger click propagation on hover
popoverApi.value.button.addEventListener('click', (e) => {
if (props.mode === 'hover') {
e.stopPropagation()
}
}, true)
}, 0)
})
function onMouseOver () {
if (props.mode !== 'hover' || !popoverApi.value) {
return
}
// cancel programmed closing
if (closeTimeout) {
clearTimeout(closeTimeout)
closeTimeout = null
}
// dropdown already open
if (popoverApi.value.popoverState === 0) {
return
}
openTimeout = openTimeout || setTimeout(() => {
popoverApi.value.togglePopover && popoverApi.value.togglePopover()
openTimeout = null
}, 50)
}
function onMouseLeave () {
if (props.mode !== 'hover' || !popoverApi.value) {
return
}
// cancel programmed opening
if (openTimeout) {
clearTimeout(openTimeout)
openTimeout = null
}
// dropdown already closed
if (popoverApi.value.popoverState === 1) {
return
}
closeTimeout = closeTimeout || setTimeout(() => {
popoverApi.value.closePopover && popoverApi.value.closePopover()
closeTimeout = null
}, 0)
}
return {
trigger,
container,
onMouseOver,
onMouseLeave
}
wrapperClass: {
type: String,
default: 'relative'
},
containerClass: {
type: String,
default: 'z-10 py-2'
},
panelClass: {
type: String,
default: ''
}
})
const [trigger, container] = usePopper({
placement: props.placement,
strategy: props.strategy,
modifiers: [{
name: 'offset',
options: {
offset: 0
}
},
{
name: 'computeStyles',
options: {
gpuAcceleration: false,
adaptive: false
}
},
{
name: 'preventOverflow',
options: {
padding: 8
}
}]
})
const popoverApi: Ref<any> = ref(null)
let openTimeout: NodeJS.Timeout | null = null
let closeTimeout: NodeJS.Timeout | null = null
onMounted(() => {
setTimeout(() => {
const popoverProvides = trigger.value?.$.provides
const popoverProvidesSymbols = Object.getOwnPropertySymbols(popoverProvides)
popoverApi.value = popoverProvidesSymbols.length && popoverProvides[popoverProvidesSymbols[0]]
// stop trigger click propagation on hover
popoverApi.value.button.addEventListener('click', (e: Event) => {
if (props.mode === 'hover') {
e.stopPropagation()
}
}, true)
}, 0)
})
function onMouseOver () {
if (props.mode !== 'hover' || !popoverApi.value) {
return
}
// cancel programmed closing
if (closeTimeout) {
clearTimeout(closeTimeout)
closeTimeout = null
}
// dropdown already open
if (popoverApi.value.popoverState === 0) {
return
}
openTimeout = openTimeout || setTimeout(() => {
popoverApi.value.togglePopover && popoverApi.value.togglePopover()
openTimeout = null
}, 50)
}
function onMouseLeave () {
if (props.mode !== 'hover' || !popoverApi.value) {
return
}
// cancel programmed opening
if (openTimeout) {
clearTimeout(openTimeout)
openTimeout = null
}
// dropdown already closed
if (popoverApi.value.popoverState === 1) {
return
}
closeTimeout = closeTimeout || setTimeout(() => {
popoverApi.value.closePopover && popoverApi.value.closePopover()
closeTimeout = null
}, 0)
}
</script>

View File

@@ -24,74 +24,65 @@
</div>
</template>
<script>
<script setup lang="ts">
import { ref } from 'vue'
import { usePopper } from '../../utils'
export default {
props: {
text: {
type: String,
default: null
},
placement: {
type: String,
default: 'bottom',
validator: (value) => {
return ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'].includes(value)
}
},
strategy: {
type: String,
default: 'fixed',
validator: (value) => {
return ['absolute', 'fixed'].includes(value)
}
},
wrapperClass: {
type: String,
default: 'relative inline-flex'
},
containerClass: {
type: String,
default: 'z-10 py-2'
},
tooltipClass: {
type: String,
default: 'flex items-center justify-center invisible w-auto h-6 max-w-xs px-2 space-x-1 truncate rounded shadow lg:visible u-bg-gray-800 truncate u-text-gray-50 text-xs'
const props = defineProps({
text: {
type: String,
default: null
},
placement: {
type: String,
default: 'bottom',
validator: (value: string) => {
return ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'].includes(value)
}
},
setup (props) {
const open = ref(false)
const [trigger, container] = usePopper({
placement: props.placement,
strategy: props.strategy,
modifiers: [{
name: 'offset',
options: {
offset: 0
}
},
{
name: 'computeStyles',
options: {
gpuAcceleration: false,
adaptive: false
}
},
{
name: 'preventOverflow',
options: {
padding: 8
}
}]
})
return {
open,
trigger,
container
strategy: {
type: String,
default: 'fixed',
validator: (value: string) => {
return ['absolute', 'fixed'].includes(value)
}
},
wrapperClass: {
type: String,
default: 'relative inline-flex'
},
containerClass: {
type: String,
default: 'z-10 py-2'
},
tooltipClass: {
type: String,
default: 'flex items-center justify-center invisible w-auto h-6 max-w-xs px-2 space-x-1 truncate rounded shadow lg:visible u-bg-gray-800 truncate u-text-gray-50 text-xs'
}
}
})
const open = ref(false)
const [trigger, container] = usePopper({
placement: props.placement,
strategy: props.strategy,
modifiers: [{
name: 'offset',
options: {
offset: 0
}
},
{
name: 'computeStyles',
options: {
gpuAcceleration: false,
adaptive: false
}
},
{
name: 'preventOverflow',
options: {
padding: 8
}
}]
})
</script>