@antify/ui 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/components/AntAccordion.vue +15 -7
  2. package/dist/components/AntAccordionItem.vue +19 -5
  3. package/dist/components/AntAlert.vue +8 -6
  4. package/dist/components/AntDropdown.vue +50 -36
  5. package/dist/components/AntIcon.vue +8 -6
  6. package/dist/components/AntKeycap.vue +10 -10
  7. package/dist/components/AntListGroup.vue +5 -3
  8. package/dist/components/AntModal.vue +17 -7
  9. package/dist/components/AntPopover.vue +118 -42
  10. package/dist/components/AntSkeleton.vue +1 -1
  11. package/dist/components/AntTooltip.vue +127 -80
  12. package/dist/components/__stories/AntAccordion.stories.js +12 -3
  13. package/dist/components/__stories/AntAccordion.stories.mjs +12 -3
  14. package/dist/components/__stories/AntDropdown.stories.js +27 -23
  15. package/dist/components/__stories/AntDropdown.stories.mjs +26 -22
  16. package/dist/components/__stories/AntListGroup.stories.js +1 -1
  17. package/dist/components/__stories/AntListGroup.stories.mjs +1 -1
  18. package/dist/components/__stories/AntModal.stories.js +2 -1
  19. package/dist/components/__stories/AntModal.stories.mjs +2 -1
  20. package/dist/components/__stories/AntPopover.stories.js +22 -21
  21. package/dist/components/__stories/AntPopover.stories.mjs +22 -20
  22. package/dist/components/__stories/AntTooltip.stories.d.ts +0 -10
  23. package/dist/components/__stories/AntTooltip.stories.js +34 -212
  24. package/dist/components/__stories/AntTooltip.stories.mjs +29 -193
  25. package/dist/components/buttons/AntButton.vue +41 -44
  26. package/dist/components/crud/AntCrud.vue +1 -1
  27. package/dist/components/crud/AntCrudDetailActions.vue +1 -0
  28. package/dist/components/crud/AntCrudDetailNav.vue +1 -0
  29. package/dist/components/crud/AntCrudTableFilter.vue +20 -18
  30. package/dist/components/forms/AntField.vue +7 -2
  31. package/dist/components/forms/__stories/AntField.stories.js +0 -16
  32. package/dist/components/forms/__stories/AntField.stories.mjs +2 -16
  33. package/dist/components/index.d.ts +2 -2
  34. package/dist/components/index.js +7 -7
  35. package/dist/components/index.mjs +2 -2
  36. package/dist/components/inputs/AntCheckbox.vue +25 -6
  37. package/dist/components/inputs/AntDateInput.vue +1 -1
  38. package/dist/components/inputs/AntRadio.vue +2 -1
  39. package/dist/components/inputs/AntSelect.vue +25 -22
  40. package/dist/components/inputs/AntSwitch.vue +2 -7
  41. package/dist/components/inputs/AntTagInput.vue +91 -114
  42. package/dist/components/inputs/AntTextarea.vue +16 -4
  43. package/dist/components/inputs/Elements/AntBaseInput.vue +2 -2
  44. package/dist/components/inputs/Elements/{AntDropDown.vue → AntSelectMenu.vue} +84 -40
  45. package/dist/components/inputs/Elements/__stories/AntBaseInput.stories.d.ts +0 -1
  46. package/dist/components/inputs/Elements/__stories/AntBaseInput.stories.js +1 -29
  47. package/dist/components/inputs/Elements/__stories/AntBaseInput.stories.mjs +0 -22
  48. package/dist/components/inputs/Elements/index.d.ts +2 -1
  49. package/dist/components/inputs/Elements/index.js +7 -0
  50. package/dist/components/inputs/Elements/index.mjs +3 -1
  51. package/dist/components/inputs/__stories/AntCheckbox.stories.d.ts +0 -1
  52. package/dist/components/inputs/__stories/AntCheckbox.stories.js +1 -43
  53. package/dist/components/inputs/__stories/AntCheckbox.stories.mjs +0 -35
  54. package/dist/components/inputs/__stories/AntCheckboxGroup.stories.d.ts +0 -1
  55. package/dist/components/inputs/__stories/AntCheckboxGroup.stories.js +1 -31
  56. package/dist/components/inputs/__stories/AntCheckboxGroup.stories.mjs +0 -28
  57. package/dist/components/inputs/__stories/AntDateInput.stories.d.ts +0 -1
  58. package/dist/components/inputs/__stories/AntDateInput.stories.js +1 -32
  59. package/dist/components/inputs/__stories/AntDateInput.stories.mjs +0 -28
  60. package/dist/components/inputs/__stories/AntNumberInput.stories.d.ts +0 -2
  61. package/dist/components/inputs/__stories/AntNumberInput.stories.js +1 -65
  62. package/dist/components/inputs/__stories/AntNumberInput.stories.mjs +1 -54
  63. package/dist/components/inputs/__stories/AntPasswordInput.stories.d.ts +0 -1
  64. package/dist/components/inputs/__stories/AntPasswordInput.stories.js +1 -35
  65. package/dist/components/inputs/__stories/AntPasswordInput.stories.mjs +0 -25
  66. package/dist/components/inputs/__stories/AntRadioGroup.stories.d.ts +0 -1
  67. package/dist/components/inputs/__stories/AntRadioGroup.stories.js +1 -47
  68. package/dist/components/inputs/__stories/AntRadioGroup.stories.mjs +0 -46
  69. package/dist/components/inputs/__stories/AntRangeSlider.stories.d.ts +0 -1
  70. package/dist/components/inputs/__stories/AntRangeSlider.stories.js +1 -33
  71. package/dist/components/inputs/__stories/AntRangeSlider.stories.mjs +1 -28
  72. package/dist/components/inputs/__stories/AntSelect.stories.d.ts +0 -1
  73. package/dist/components/inputs/__stories/AntSelect.stories.js +18 -46
  74. package/dist/components/inputs/__stories/AntSelect.stories.mjs +16 -47
  75. package/dist/components/inputs/__stories/AntSwitch.stories.d.ts +0 -1
  76. package/dist/components/inputs/__stories/AntSwitch.stories.js +1 -42
  77. package/dist/components/inputs/__stories/AntSwitch.stories.mjs +1 -37
  78. package/dist/components/inputs/__stories/AntSwitcher.stories.d.ts +0 -1
  79. package/dist/components/inputs/__stories/AntSwitcher.stories.js +1 -51
  80. package/dist/components/inputs/__stories/AntSwitcher.stories.mjs +1 -51
  81. package/dist/components/inputs/__stories/AntTagInput.stories.d.ts +0 -1
  82. package/dist/components/inputs/__stories/AntTagInput.stories.js +1 -35
  83. package/dist/components/inputs/__stories/AntTagInput.stories.mjs +0 -33
  84. package/dist/components/inputs/__stories/AntTextInput.stories.d.ts +0 -2
  85. package/dist/components/inputs/__stories/AntTextInput.stories.js +2 -107
  86. package/dist/components/inputs/__stories/AntTextInput.stories.mjs +1 -104
  87. package/dist/components/inputs/__stories/AntTextarea.stories.d.ts +0 -2
  88. package/dist/components/inputs/__stories/AntTextarea.stories.js +7 -66
  89. package/dist/components/inputs/__stories/AntTextarea.stories.mjs +6 -55
  90. package/dist/components/inputs/__stories/AntUnitInput.stories.d.ts +0 -2
  91. package/dist/components/inputs/__stories/AntUnitInput.stories.js +1 -61
  92. package/dist/components/inputs/__stories/AntUnitInput.stories.mjs +0 -53
  93. package/dist/components/table/AntTable.vue +17 -15
  94. package/dist/components/table/AntTd.vue +1 -2
  95. package/dist/components/table/__stories/AntTable.stories.js +7 -14
  96. package/dist/components/table/__stories/AntTable.stories.mjs +7 -15
  97. package/dist/components/tabs/AntTabItem.vue +24 -7
  98. package/dist/components/tabs/AntTabs.vue +14 -2
  99. package/dist/components/tabs/__stories/AntTabs.stories.d.ts +1 -0
  100. package/dist/components/tabs/__stories/AntTabs.stories.js +112 -6
  101. package/dist/components/tabs/__stories/AntTabs.stories.mjs +120 -5
  102. package/dist/components/tabs/__types/AntTabItem.types.d.ts +2 -0
  103. package/dist/components/tabs/__types/AntTabItem.types.js +1 -0
  104. package/dist/components/tabs/__types/AntTabItem.types.mjs +1 -0
  105. package/package.json +2 -1
@@ -2,6 +2,7 @@
2
2
  import AntAccordionItem from './AntAccordionItem.vue';
3
3
  import {onMounted, ref} from 'vue';
4
4
  import {CollapseStrategy} from './__types/AntAccordion.types';
5
+ import AntSkeleton from "./AntSkeleton.vue";
5
6
 
6
7
  const props = withDefaults(defineProps<{
7
8
  items: {
@@ -16,8 +17,10 @@ const props = withDefaults(defineProps<{
16
17
  inactiveIconClasses?: string;
17
18
  }[];
18
19
  collapseStrategy?: CollapseStrategy;
20
+ skeleton?: boolean;
19
21
  }>(), {
20
- collapseStrategy: CollapseStrategy.single
22
+ collapseStrategy: CollapseStrategy.single,
23
+ skeleton: false
21
24
  });
22
25
 
23
26
  const openItems = ref<number[]>([]);
@@ -59,6 +62,7 @@ function onClose(index: number) {
59
62
  :activeIconClasses="item.activeIconClasses"
60
63
  :inactiveLabelClasses="item.inactiveLabelClasses"
61
64
  :inactiveIconClasses="item.inactiveIconClasses"
65
+ :skeleton="skeleton"
62
66
  @open="() => onOpen(index)"
63
67
  @close="() => onClose(index)"
64
68
  >
@@ -69,12 +73,16 @@ function onClose(index: number) {
69
73
  />
70
74
  </template>
71
75
 
72
- <slot
73
- name="item-content"
74
- v-bind="{item, index}"
75
- >
76
- <div v-html="item.content"/>
77
- </slot>
76
+ <div class="relative">
77
+ <slot
78
+ name="item-content"
79
+ v-bind="{item, index}"
80
+ >
81
+ <div v-html="item.content"/>
82
+
83
+ <AntSkeleton v-if="skeleton" absolute rounded/>
84
+ </slot>
85
+ </div>
78
86
  </AntAccordionItem>
79
87
  </slot>
80
88
  </div>
@@ -4,6 +4,7 @@ import AntIcon from './AntIcon.vue';
4
4
  import AntTransitionCollapseHeight from './transitions/AntTransitionCollapseHeight.vue';
5
5
  import {IconSize} from './__types/AntIcon.types';
6
6
  import {computed} from 'vue';
7
+ import AntSkeleton from "../components/AntSkeleton.vue";
7
8
 
8
9
  const props = withDefaults(defineProps<{
9
10
  isOpen: boolean;
@@ -15,10 +16,12 @@ const props = withDefaults(defineProps<{
15
16
  activeIconClasses?: string;
16
17
  inactiveLabelClasses?: string;
17
18
  inactiveIconClasses?: string;
19
+ skeleton?: boolean;
18
20
  }>(), {
19
21
  collapseTransition: 'slide',
20
22
  iconLeft: false,
21
23
  contentPadding: true,
24
+ skeleton: false,
22
25
  activeLabelClasses: 'bg-primary-500 text-primary-500-font',
23
26
  activeIconClasses: 'text-primary-500-font',
24
27
  inactiveLabelClasses: 'bg-white text-for-white-bg-font',
@@ -28,6 +31,10 @@ const emit = defineEmits(['close', 'open']);
28
31
 
29
32
  // TODO:: Stehengeblieben: delays machen
30
33
  function onClick() {
34
+ if (props.skeleton) {
35
+ return
36
+ }
37
+
31
38
  if (props.isOpen) {
32
39
  emit('close');
33
40
  } else {
@@ -37,13 +44,14 @@ function onClick() {
37
44
 
38
45
  const labelClasses = computed(() => ({
39
46
  [props.activeLabelClasses]: props.isOpen,
40
- [props.inactiveLabelClasses]: !props.isOpen
47
+ [props.inactiveLabelClasses]: !props.isOpen,
48
+ 'cursor-pointer': !props.skeleton
41
49
  }))
42
50
  </script>
43
51
 
44
52
  <template>
45
53
  <div
46
- class="p-2 select-none cursor-pointer transition-colors"
54
+ class="p-2 select-none transition-colors"
47
55
  :class="labelClasses"
48
56
  @click="onClick"
49
57
  >
@@ -61,17 +69,23 @@ const labelClasses = computed(() => ({
61
69
  :size="IconSize.sm"
62
70
  :icon="faQuestionCircle"
63
71
  :color="isOpen ? activeIconClasses : inactiveIconClasses"
72
+ :skeleton="skeleton"
64
73
  />
65
74
  </slot>
66
75
 
67
- <span class="text-sm font-semibold">
68
- {{ label }}
69
- </span>
76
+
77
+ <div class="relative">
78
+ <div class="text-sm font-semibold">
79
+ {{ label }}
80
+ </div>
81
+ <AntSkeleton v-if="skeleton" absolute rounded/>
82
+ </div>
70
83
  </div>
71
84
 
72
85
  <AntIcon
73
86
  :icon="isOpen ? faAngleUp : faAngleDown"
74
87
  :color="isOpen ? activeIconClasses : inactiveIconClasses"
88
+ :skeleton="skeleton"
75
89
  />
76
90
  </div>
77
91
  </slot>
@@ -114,12 +114,14 @@ onMounted(() => {
114
114
  <div class="flex gap-2">
115
115
  <div v-if="hasQuestionMark">
116
116
  <slot name="questionMarkText">
117
- <AntTooltip :position="Position.bottom">
118
- <AntIcon
119
- :icon="faCircleQuestion"
120
- :color="iconColor"
121
- :size="IconSize.sm"
122
- />
117
+ <AntTooltip >
118
+ <template #reference>
119
+ <AntIcon
120
+ :icon="faCircleQuestion"
121
+ :color="iconColor"
122
+ :size="IconSize.sm"
123
+ />
124
+ </template>
123
125
 
124
126
  <template #content>
125
127
  {{ questionMarkText }}
@@ -1,43 +1,43 @@
1
1
  <script lang="ts" setup>
2
- /**
3
- * TODO:: Emit if there's enough space for the dropdown content. If not, reposition it automatically like the
4
- * browsers select dropdown content does.
5
- * TODO:: Known issue: open dropdown, click between button and dropdown. Dropdown will not click.
6
- * The v-on-click-outside does not trigger, because it think it's the dropdown itself.
7
- * Solution is using margin only (calc(50% + 0.625rem)) and not padding.
8
- */
9
- import {computed, onMounted} from 'vue';
10
- import {handleEnumValidation} from '../handler';
11
- import {Position} from '../enums';
2
+ import {computed, ref} from 'vue';
12
3
  import {classesToObjectSyntax} from '../utils';
13
4
  import {vOnClickOutside} from '@vueuse/components';
14
5
  import {onKeyStroke} from '@vueuse/core';
6
+ import {autoUpdate, flip, offset, useFloating, shift} from "@floating-ui/vue";
15
7
 
16
8
  const props = withDefaults(defineProps<{
17
9
  showDropdown: boolean,
18
- position?: Position,
19
10
  dropdownClasses?: string | Record<string, boolean>,
20
- contentPadding?: boolean
11
+ contentPadding?: boolean,
12
+ isClickable?: boolean,
21
13
  }>(), {
22
- showDropdown: false,
23
14
  contentPadding: true,
24
- position: Position.bottom,
25
15
  dropdownClasses: '',
16
+ isClickable: true,
26
17
  });
27
18
  const emit = defineEmits(['update:showDropdown']);
28
19
 
20
+ const reference = ref<HTMLElement | null>(null)
21
+ const floating = ref<HTMLElement | null>(null)
22
+
23
+ const {floatingStyles} = useFloating(reference, floating, {
24
+ transform: false,
25
+ placement: 'bottom-start',
26
+ whileElementsMounted: autoUpdate,
27
+ middleware: [
28
+ offset(8),
29
+ shift(),
30
+ flip({
31
+ fallbackPlacements: ['top-start'],
32
+ }),
33
+ ]
34
+ });
35
+
29
36
  const _dropdownClasses = computed(() => ({
30
- 'absolute min-w-[10rem]': true,
31
- 'bottom-0 left-0 mb-[50%] pb-2.5': props.position === Position.top,
32
- 'top-0 left-0 mt-[50%] pt-2.5': props.position === Position.bottom,
33
- 'right-0 top-0 mr-[100%] pr-2.5': props.position === Position.left,
34
- 'left-0 top-0 ml-[100%] pl-2.5': props.position === Position.right,
37
+ 'min-w-[10rem] z-[90]': true,
35
38
  ...classesToObjectSyntax(props.dropdownClasses)
36
39
  }));
37
40
 
38
- onMounted(() => {
39
- handleEnumValidation(props.position, Position, 'Position');
40
- });
41
41
  onKeyStroke('Escape', (e: KeyboardEvent) => {
42
42
  if (props.showDropdown) {
43
43
  e.preventDefault();
@@ -45,31 +45,45 @@ onKeyStroke('Escape', (e: KeyboardEvent) => {
45
45
  }
46
46
  });
47
47
 
48
- function onClickOutside() {
49
- emit('update:showDropdown', false);
50
- }
48
+ const onClickOutside = [
49
+ (ev) => {
50
+ emit('update:showDropdown', false);
51
+ },
52
+ {
53
+ ignore: [floating]
54
+ }
55
+ ]
51
56
  </script>
52
57
 
53
58
  <template>
54
59
  <div
55
- v-on-click-outside="onClickOutside"
56
- class="relative inline-flex justify-center items-end z-40"
60
+ class="relative flex"
57
61
  data-e2e="dropdown"
58
62
  >
59
- <slot/>
63
+ <div
64
+ ref="reference"
65
+ class="h-full w-full"
66
+ v-on-click-outside="onClickOutside"
67
+ >
68
+ <slot/>
69
+ </div>
60
70
 
61
71
  <Transition name="bounce">
62
- <div
63
- v-if="showDropdown"
64
- :class="_dropdownClasses"
65
- >
72
+ <teleport to="body">
66
73
  <div
67
- class="shadow-lg border border-neutral-300 rounded-md text-sm relative inline-flex flex-col relative bg-white text-for-white-bg-font w-full"
68
- :class="{'p-2': contentPadding}"
74
+ v-if="showDropdown"
75
+ :class="_dropdownClasses"
76
+ ref="floating"
77
+ :style="floatingStyles"
69
78
  >
70
- <slot name="content"/>
79
+ <div
80
+ class="shadow-lg border border-neutral-300 rounded-md text-sm relative inline-flex flex-col bg-white text-for-white-bg-font w-full overflow-hidden"
81
+ :class="{'p-2': contentPadding}"
82
+ >
83
+ <slot name="content"/>
84
+ </div>
71
85
  </div>
72
- </div>
86
+ </teleport>
73
87
  </Transition>
74
88
  </div>
75
89
  </template>
@@ -3,10 +3,12 @@ import {computed, onMounted} from 'vue';
3
3
  import {handleEnumValidation} from '../handler';
4
4
  import {type IconDefinition} from '@fortawesome/free-solid-svg-icons';
5
5
  import {IconSize} from './__types/AntIcon.types';
6
+ import AntSkeleton from "./AntSkeleton.vue";
6
7
 
7
8
  const props = withDefaults(defineProps<{
8
9
  icon: IconDefinition;
9
10
  size?: IconSize;
11
+ skeleton?: boolean;
10
12
 
11
13
  /**
12
14
  * A css text-* class
@@ -14,11 +16,12 @@ const props = withDefaults(defineProps<{
14
16
  color?: string;
15
17
  }>(), {
16
18
  size: IconSize.sm,
17
- color: 'text-for-white-bg-font'
19
+ color: 'text-for-white-bg-font',
20
+ skeleton: false,
18
21
  });
19
22
 
20
23
  const containerClasses = computed(() => ({
21
- 'inline-flex items-center justify-center': true,
24
+ 'inline-flex items-center justify-center relative': true,
22
25
  'w-3 h-3': props.size === IconSize.xs2,
23
26
  'w-4 h-4': props.size === IconSize.xs,
24
27
  'w-5 h-5': props.size === IconSize.sm,
@@ -44,13 +47,12 @@ onMounted(() => {
44
47
  </script>
45
48
 
46
49
  <template>
47
- <span
48
- v-if="icon"
49
- :class="containerClasses"
50
- >
50
+ <span :class="containerClasses">
51
51
  <FaIcon
52
+ v-if="icon"
52
53
  :icon="icon"
53
54
  :class="iconClasses"
54
55
  />
56
+ <AntSkeleton v-if="skeleton" absolute rounded/>
55
57
  </span>
56
58
  </template>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  // TODO:: Fix typo KeyCap with upperchar C
3
3
  import AntIcon from './AntIcon.vue';
4
+ import AntSkeleton from "./AntSkeleton.vue";
4
5
  import {type IconDefinition} from '@fortawesome/free-solid-svg-icons';
5
6
  import {IconSize} from './__types';
6
7
  import {AntKeycapSize} from './__types/AntKeycap.types';
@@ -9,9 +10,10 @@ import {computed} from 'vue';
9
10
  const props = withDefaults(defineProps<{
10
11
  icon?: IconDefinition
11
12
  size?: AntKeycapSize
12
-
13
+ skeleton?: boolean
13
14
  }>(), {
14
- size: AntKeycapSize.sm
15
+ size: AntKeycapSize.sm,
16
+ skeleton: false
15
17
  });
16
18
 
17
19
  const classes = computed(() => {
@@ -35,16 +37,14 @@ const iconClasses = computed(() => {
35
37
  </script>
36
38
 
37
39
  <template>
38
- <span
39
- class="inline-flex justify-center items-center bg-neutral-300 rounded-sm text-center text-neutral-300-font font-medium"
40
- :class="classes"
41
- >
42
- <AntIcon
40
+ <div class="inline-flex relative justify-center items-center bg-neutral-300 rounded-md text-center text-neutral-300-font font-medium" :class="classes">
41
+ <AntIcon
43
42
  v-if="icon"
44
43
  :icon="icon"
45
44
  :size="iconClasses"
46
- />
45
+ />
47
46
 
48
- <slot v-else/>
49
- </span>
47
+ <slot v-else/>
48
+ <AntSkeleton v-if="skeleton" absolute rounded/>
49
+ </div>
50
50
  </template>
@@ -3,9 +3,11 @@ import AntSkeleton from './AntSkeleton.vue';
3
3
 
4
4
  withDefaults(
5
5
  defineProps<{
6
- skeleton?: boolean
6
+ skeleton?: boolean,
7
+ shadow?: boolean
7
8
  }>(), {
8
- skeleton: false
9
+ skeleton: false,
10
+ shadow: false,
9
11
  }
10
12
  )
11
13
  </script>
@@ -16,7 +18,7 @@ withDefaults(
16
18
 
17
19
  <div
18
20
  class="flex flex-col rounded-md bg-neutral-300 gap-px relative border border-neutral-300 overflow-hidden"
19
- :class="{'invisible': skeleton}"
21
+ :class="{'invisible': skeleton, 'shadow-md': shadow}"
20
22
  >
21
23
  <slot/>
22
24
  </div>
@@ -2,14 +2,19 @@
2
2
  import {ref, useSlots, watch} from 'vue';
3
3
  import {faXmark} from '@fortawesome/free-solid-svg-icons';
4
4
  import AntButton from './buttons/AntButton.vue';
5
+ import AntSkeleton from "./AntSkeleton.vue";
5
6
 
6
7
  const emit = defineEmits(['update:open', 'close']);
7
8
  const props = withDefaults(defineProps<{
8
9
  title: string,
9
10
  open: boolean,
10
- fullscreen?: boolean
11
+ fullscreen?: boolean,
12
+ padding?: boolean
13
+ skeleton?: boolean
11
14
  }>(), {
12
- fullscreen: false
15
+ fullscreen: false,
16
+ padding: false,
17
+ skeleton: false
13
18
  });
14
19
  const openModal = ref(props.open);
15
20
  const openBackground = ref(props.open);
@@ -53,21 +58,26 @@ function closeModal() {
53
58
  :class="{'w-full h-full': fullscreen, 'border border-neutral-300 rounded-md shadow-md': !fullscreen}"
54
59
  >
55
60
  <div
56
- class="bg-white p-2 flex items-center justify-between text-for-white-bg-font text-lg font-medium"
61
+ class="bg-white p-2 flex items-center justify-between"
57
62
  >
58
63
  <slot name="title">
59
- {{ title }}
64
+ <div class="relative text-for-white-bg-font text-lg font-medium">
65
+ {{ title }}
66
+ <AntSkeleton v-if="skeleton" absolute rounded/>
67
+ </div>
60
68
  </slot>
61
69
 
62
70
  <AntButton
71
+ :skeleton="skeleton"
63
72
  :icon-left="faXmark"
64
- :outlined="false"
65
- filled
66
73
  @click="closeModal"
67
74
  />
68
75
  </div>
69
76
 
70
- <div class="bg-white p-2 text-for-white-bg-font grow overflow-y-auto">
77
+ <div
78
+ class="bg-white text-for-white-bg-font grow overflow-y-auto"
79
+ :class="{'p-2': padding}"
80
+ >
71
81
  <slot/>
72
82
  </div>
73
83
 
@@ -1,57 +1,145 @@
1
1
  <script lang="ts" setup>
2
- import {computed, onMounted} from 'vue';
3
- import {handleEnumValidation} from '../handler';
2
+ import {computed, ref} from 'vue';
4
3
  import {Position} from '../enums/Position.enum';
5
4
  import {classesToObjectSyntax} from '../utils';
5
+ import {arrow, autoUpdate, flip, offset, useFloating, shift, limitShift} from "@floating-ui/vue";
6
+ import {vOnClickOutside} from '@vueuse/components';
7
+ import {onKeyStroke} from "@vueuse/core";
6
8
 
7
9
  const props = withDefaults(defineProps<{
8
10
  showPopover: boolean,
9
11
  title?: string,
10
- position?: Position,
12
+ isClickable?: boolean,
11
13
  popoverClasses?: string | Record<string, boolean>
12
14
  }>(), {
15
+ isClickable: true,
13
16
  showPopover: false,
14
- position: Position.left,
17
+ preferredPosition: Position.top,
15
18
  popoverClasses: ''
16
19
  });
20
+
21
+ const emit = defineEmits(['update:showPopover']);
22
+
23
+ const reference = ref<HTMLElement | null>(null)
24
+ const floating = ref<HTMLElement | null>(null)
25
+ const floatingArrow = ref<HTMLElement | null>(null);
26
+
27
+ const {floatingStyles, middlewareData, placement} = useFloating(reference, floating, {
28
+ transform: false,
29
+ placement: 'right',
30
+ whileElementsMounted: autoUpdate,
31
+ middleware: [
32
+ offset(() => 16),
33
+ shift({
34
+ limiter: limitShift({
35
+ offset: {
36
+ mainAxis: 62,
37
+ }
38
+ }),
39
+ }),
40
+ flip({
41
+ fallbackPlacements: ['top', 'bottom', 'left'],
42
+ }),
43
+ arrow({element: floatingArrow})
44
+ ]
45
+ });
46
+
47
+ const side = computed(() => placement.value.split('-')[0]);
48
+ const staticSide = computed(() => {
49
+ return {
50
+ top: "bottom",
51
+ right: "left",
52
+ bottom: "top",
53
+ left: "right"
54
+ }[side.value] as string
55
+ })
56
+ const arrowTransform = computed(() => {
57
+ if (placement.value === 'bottom') {
58
+ return 'rotate(0deg)'
59
+ } else if (placement.value === 'top') {
60
+ return 'rotate(180deg)'
61
+ } else if (placement.value === 'left') {
62
+ return 'rotate(90deg)'
63
+ } else {
64
+ return 'rotate(270deg)'
65
+ }
66
+ });
67
+
17
68
  const _popoverClasses = computed(() => ({
18
- 'absolute min-w-[10rem]': true,
19
- 'bottom-0 mb-[50%] pb-3.5': props.position === Position.top,
20
- 'top-0 mt-[50%] pt-3.5': props.position === Position.bottom,
21
- 'right-0 mr-[100%] pr-3.5': props.position === Position.left,
22
- 'left-0 ml-[100%] pl-3.5': props.position === Position.right,
69
+ 'z-[90] min-w-[10rem]': true,
23
70
  ...classesToObjectSyntax(props.popoverClasses)
24
71
  }));
25
- const classes = computed(() => ({
26
- 'z-10 absolute flex': true,
27
- 'top-0 left-0 right-0 -m-[2.5px] justify-center': props.position === Position.bottom,
28
- 'bottom-0 left-0 right-0 -m-[2.5px] justify-center': props.position === Position.top,
29
- 'top-0 left-0 bottom-0 -ml-[2.5px] items-end pb-1': props.position === Position.right,
30
- 'top-0 right-0 bottom-0 -mr-[2.5px] items-end pb-1': props.position === Position.left,
31
- }));
32
72
  const itemContainerClasses = computed(() => ({
33
- 'relative flex items-center': true,
34
- 'justify-center': props.position === Position.bottom,
35
- 'justify-center rotate-180': props.position === Position.top,
36
- 'justify-start -rotate-90': props.position === Position.right,
37
- 'justify-end rotate-90': props.position === Position.left,
73
+ 'relative flex items-center justify-center': true,
38
74
  }));
39
75
 
40
- onMounted(() => {
41
- handleEnumValidation(props.position, Position, 'Position');
76
+ onKeyStroke('Escape', (e: KeyboardEvent) => {
77
+ if (props.showPopover) {
78
+ e.preventDefault();
79
+ emit('update:showPopover', false);
80
+ }
42
81
  });
82
+
83
+ const onClickOutside = [
84
+ (ev) => {
85
+ emit('update:showPopover', false);
86
+ },
87
+ {
88
+ ignore: [floating]
89
+ }
90
+ ]
43
91
  </script>
44
92
 
45
93
  <template>
46
94
  <div class="relative inline-flex justify-center items-end">
47
- <slot/>
95
+ <div
96
+ v-on-click-outside="onClickOutside"
97
+ ref="reference"
98
+ >
99
+ <slot/>
100
+ </div>
48
101
 
49
102
  <Transition name="bounce">
50
103
  <template v-if="showPopover">
51
- <div :class="_popoverClasses">
52
- <div class="shadow-lg border-neutral-300 rounded-md text-sm relative inline-flex flex-col relative">
104
+ <teleport to="body">
105
+ <div
106
+ :class="_popoverClasses"
107
+ ref="floating"
108
+ :style="floatingStyles"
109
+ >
110
+ <div class="shadow-lg border-neutral-300 rounded-md text-sm relative inline-flex flex-col">
111
+
112
+ <div
113
+ class="border-neutral-300 border-b p-2 bg-neutral-100 rounded-t-md border-t border-l border-r text-neutral-100-font font-semibold"
114
+ >
115
+ <slot name="title">
116
+ {{ title }}
117
+ </slot>
118
+ </div>
119
+
120
+ <div
121
+ class="p-2 rounded-b-md text-for-white-bg-font border-neutral-300 border-l border-b border-r bg-white"
122
+ >
123
+ <slot name="content"/>
124
+ </div>
125
+ </div>
126
+
53
127
  <div
54
- :class="classes"
128
+ class="flex items-center justify-center"
129
+ ref="floatingArrow"
130
+ :style="{
131
+ position: 'absolute',
132
+ left:
133
+ middlewareData.arrow?.x != null
134
+ ? `${middlewareData.arrow.x}px`
135
+ : '',
136
+ top:
137
+ middlewareData.arrow?.y != null
138
+ ? `${middlewareData.arrow.y}px`
139
+ : '',
140
+ [staticSide]: '-2.5px',
141
+ transform: arrowTransform
142
+ }"
55
143
  >
56
144
  <div
57
145
  :class="itemContainerClasses"
@@ -66,12 +154,12 @@ onMounted(() => {
66
154
  >
67
155
  <path
68
156
  d="M20.3284 1.82843L23.1569 4.65685C24.6571 6.15715 26.692 7 28.8137 7L6.18629 7C8.30802 7 10.3429 6.15715 11.8431 4.65686L14.6716 1.82843C16.2337 0.266331 18.7663 0.266328 20.3284 1.82843Z"
69
- :class="{'fill-neutral-100': position === Position.bottom, 'fill-white': position === Position.top || position === Position.right || position === Position.left}"
157
+ :class="{'fill-neutral-100': placement === Position.bottom, 'fill-white': placement === Position.top || placement === Position.right || placement === Position.left}"
70
158
  />
71
159
 
72
160
  <path
73
161
  d="M34.5 7L28.8137 7C26.692 7 24.6571 6.15715 23.1569 4.65685L20.3284 1.82843C18.7663 0.266328 16.2337 0.266331 14.6716 1.82843L11.8431 4.65686C10.3429 6.15715 8.30802 7 6.18629 7L0.5 7L34.5 7Z"
74
- :class="{'stroke-neutral-100': position === Position.bottom, 'stroke-white': position === Position.top || position === Position.right || position === Position.left}"
162
+ :class="{'stroke-neutral-100': placement === Position.bottom, 'stroke-white': placement === Position.top || placement === Position.right || placement === Position.left}"
75
163
  />
76
164
  </svg>
77
165
 
@@ -90,20 +178,8 @@ onMounted(() => {
90
178
  </svg>
91
179
  </div>
92
180
  </div>
93
-
94
- <div
95
- class="border-neutral-300 border-b p-2 bg-neutral-100 rounded-t-md border-t border-l border-r text-neutral-100-font font-semibold"
96
- >
97
- {{ title }}
98
- </div>
99
-
100
- <div
101
- class="p-2 rounded-b-md text-for-white-bg-font border-neutral-300 border-l border-b border-r bg-white"
102
- >
103
- <slot name="content"/>
104
- </div>
105
181
  </div>
106
- </div>
182
+ </teleport>
107
183
  </template>
108
184
  </Transition>
109
185
  </div>
@@ -25,7 +25,7 @@ const classList = computed(() => ({
25
25
  'animate-skeleton min-h-[1px] min-w-[1px] inline-block': true,
26
26
  'absolute inset-0 w-full h-full z-40': props.absolute,
27
27
  'rounded-md': props.rounded && props.grouped === Grouped.none,
28
- 'rounded-xl': props.roundedFull && props.grouped === Grouped.none,
28
+ 'rounded-full': props.roundedFull && props.grouped === Grouped.none,
29
29
  ...groupedClassList.value
30
30
  }));
31
31
  </script>