UiPopover.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <template>
  2. <div class="ui-popover inline-block" :class="{ hidden: to }">
  3. <div
  4. ref="targetEl"
  5. :class="triggerClass"
  6. class="ui-popover__trigger h-full inline-block"
  7. >
  8. <slot name="trigger" v-bind="{ isShow }"></slot>
  9. </div>
  10. <div
  11. ref="content"
  12. class="ui-popover__content bg-white dark:bg-gray-800 rounded-lg shadow-xl border"
  13. :class="[padding]"
  14. >
  15. <slot v-bind="{ isShow }"></slot>
  16. </div>
  17. </div>
  18. </template>
  19. <script>
  20. import { ref, onMounted, watch, shallowRef, onUnmounted } from 'vue';
  21. import createTippy from '@/lib/tippy';
  22. export default {
  23. props: {
  24. placement: {
  25. type: String,
  26. default: 'bottom',
  27. },
  28. trigger: {
  29. type: String,
  30. default: 'click',
  31. },
  32. padding: {
  33. type: String,
  34. default: 'p-4',
  35. },
  36. to: {
  37. type: [String, Object, HTMLElement],
  38. default: '',
  39. },
  40. options: {
  41. type: Object,
  42. default: () => ({}),
  43. },
  44. disabled: {
  45. type: Boolean,
  46. default: false,
  47. },
  48. triggerWidth: {
  49. type: Boolean,
  50. default: false,
  51. },
  52. modelValue: {
  53. type: Boolean,
  54. default: false,
  55. },
  56. triggerClass: {
  57. type: String,
  58. default: null,
  59. },
  60. },
  61. emits: ['show', 'trigger', 'close', 'update:modelValue'],
  62. setup(props, { emit }) {
  63. const targetEl = ref(null);
  64. const content = ref(null);
  65. const isShow = ref(false);
  66. const instance = shallowRef(null);
  67. watch(
  68. () => props.options,
  69. (value) => {
  70. instance.value.setProps(value);
  71. },
  72. { deep: true }
  73. );
  74. watch(
  75. () => props.disabled,
  76. (value) => {
  77. if (value) {
  78. instance.value.enable();
  79. } else {
  80. instance.value.hide();
  81. instance.value.disable();
  82. }
  83. }
  84. );
  85. watch(
  86. () => props.modelValue,
  87. (value) => {
  88. if (value === isShow.value) return;
  89. isShow.value = value;
  90. /* eslint-disable-next-line */
  91. value ? instance.value.show() : instance.value.hide();
  92. }
  93. );
  94. onMounted(() => {
  95. /* eslint-disable-next-line */
  96. const target = props.to
  97. ? typeof to === 'string'
  98. ? document.querySelector(props.to)
  99. : props.to
  100. : targetEl.value;
  101. instance.value = createTippy(target, {
  102. role: 'popover',
  103. theme: null,
  104. content: content.value,
  105. placement: props.placement,
  106. trigger: props.trigger,
  107. interactive: true,
  108. appendTo: () => document.body,
  109. onShow: (event) => {
  110. if (props.triggerWidth) {
  111. event.popper.style.width = `${
  112. event.reference.getBoundingClientRect().width
  113. }px`;
  114. }
  115. emit('show', event);
  116. isShow.value = true;
  117. },
  118. onHide: () => {
  119. emit('close');
  120. emit('update:modelValue', false);
  121. isShow.value = false;
  122. },
  123. onTrigger: () => emit('trigger'),
  124. ...props.options,
  125. });
  126. });
  127. onUnmounted(() => {
  128. instance.value.destroy();
  129. });
  130. return {
  131. isShow,
  132. content,
  133. targetEl,
  134. };
  135. },
  136. };
  137. </script>