All files / src/FormItem FormItem.vue

96.33% Statements 105/109
44.44% Branches 4/9
100% Functions 0/0
96.33% Lines 105/109

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110  1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 6x 6x   6x   6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 6x 6x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
<template>
  <slot v-if="labelPosition === 'sticky'" name="label">{{ label }} </slot>
  <div
    ref="elRef"
    class="form-item"
    :class="labelClass"
    :style="state.item.itemStyle"
    :id="state.item.key"
  >
    <div
      v-if="labelPosition !== 'sticky'"
      class="label-wrapper"
      :style="{ width: __labelWith }"
    >
      <slot name="label">{{ label }} </slot>
    </div>
    <div class="content-wrapper">
      <slot />
      <div v-if="data?.$anyInvalid && data.$dirty" class="error-msg">
        {{ data.validate.$message }}
      </div>
    </div>
  </div>
</template>
 
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { LabelPoistion } from '../inject'
import { useFormItemState } from './state'
import { ValidateModel } from './type'
 
defineOptions({
  name: 'FormItem',
})
 
const props = withDefaults(
  defineProps<{
    data: ValidateModel
    label?: string
    labelWidth?: string | number
    labelPosition?: LabelPoistion
    classList?: string
  }>(),
  {
    labelPosition: 'left',
    classList: '',
  }
)
 
const __labelWith = computed(() => {
  const { labelWidth } = props
  switch (typeof labelWidth) {
    case 'string':
      return labelWidth
    case 'number':
      return labelWidth + 'px'
    default:
      return '20px'
  }
})
 
const elRef = ref<HTMLElement | null>(null)
 
const LABEL_CLASS = {
  left: '',
  top: 'label-top',
  sticky: '',
  none: 'hide-label',
}
 
const state = useFormItemState()
 
const labelClass = computed(() => {
  return [
    LABEL_CLASS[state.item.labelPosition ?? props.labelPosition],
    props.classList,
  ].flatMap((e) => e.split(' '))
})
 
defineExpose({
  el: elRef,
})
</script>
<style scoped lang="scss">
.label-wrapper {
  @apply flex-shrink-0 whitespace-nowrap relative z-1 mr-3;
}
.form-item {
  @apply flex text-sm  relative;
 
  &.label-top {
    @apply block;
  }
 
  &.hide-label {
    & > .label-wrapper {
      display: none;
    }
  }
}
 
.error-msg {
  @apply text-red-400 text-xs;
  @apply absolute -bottom-1.3em left-0;
}
.content-wrapper {
  @apply flex-1 relative;
}
</style>