@alikhalilll/ui 1.2.2 → 1.2.4

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 (47) hide show
  1. package/dist/entries/drawer/components/ADrawer.vue.d.ts +1 -5
  2. package/dist/entries/drawer/components/ADrawerContent.vue.d.ts +1 -5
  3. package/dist/entries/drawer/components/ADrawerTrigger.vue.d.ts +1 -5
  4. package/dist/entries/input/components/AInput.vue.d.ts +1 -5
  5. package/dist/entries/popover/components/APopover.vue.d.ts +1 -5
  6. package/dist/entries/popover/components/APopoverContent.vue.d.ts +1 -5
  7. package/dist/entries/popover/components/APopoverTrigger.vue.d.ts +1 -5
  8. package/dist/entries/responsive-popover/components/AResponsivePopover.vue.d.ts +1 -5
  9. package/dist/entries/responsive-popover/components/AResponsivePopoverContent.vue.d.ts +1 -5
  10. package/dist/entries/responsive-popover/components/AResponsivePopoverTrigger.vue.d.ts +1 -5
  11. package/dist/entries/tell-input/components/ACountryFlag.vue.d.ts +1 -5
  12. package/dist/entries/tell-input/components/ACountrySelect.vue.d.ts +1 -5
  13. package/dist/entries/tell-input/components/ATellInput.vue.d.ts +1 -5
  14. package/entries/drawer/components/ADrawer.vue +16 -0
  15. package/entries/drawer/components/ADrawerContent.vue +35 -0
  16. package/entries/drawer/components/ADrawerOverlay.vue +25 -0
  17. package/entries/drawer/components/ADrawerTrigger.vue +13 -0
  18. package/entries/drawer/index.ts +4 -0
  19. package/entries/input/components/AInput.vue +111 -0
  20. package/entries/input/index.ts +1 -0
  21. package/entries/popover/components/APopover.vue +19 -0
  22. package/entries/popover/components/APopoverContent.vue +65 -0
  23. package/entries/popover/components/APopoverOverlay.vue +69 -0
  24. package/entries/popover/components/APopoverTrigger.vue +13 -0
  25. package/entries/popover/composables/useEventScrollLock.ts +193 -0
  26. package/entries/popover/index.ts +8 -0
  27. package/entries/responsive-popover/components/AResponsivePopover.vue +67 -0
  28. package/entries/responsive-popover/components/AResponsivePopoverContent.vue +80 -0
  29. package/entries/responsive-popover/components/AResponsivePopoverTrigger.vue +23 -0
  30. package/entries/responsive-popover/composables/useResponsivePopoverContext.ts +20 -0
  31. package/entries/responsive-popover/index.ts +3 -0
  32. package/entries/tell-input/components/ACountryFlag.vue +68 -0
  33. package/entries/tell-input/components/ACountrySelect.vue +522 -0
  34. package/entries/tell-input/components/ATellInput.vue +616 -0
  35. package/entries/tell-input/composables/useCountryDetection.ts +247 -0
  36. package/entries/tell-input/composables/useCountryMatching.ts +213 -0
  37. package/entries/tell-input/composables/usePhoneValidation.ts +573 -0
  38. package/entries/tell-input/composables/useTellInputValidation.ts +136 -0
  39. package/entries/tell-input/composables/useTypingPhase.ts +88 -0
  40. package/entries/tell-input/index.ts +29 -0
  41. package/entries/tell-input/utils/digits.ts +42 -0
  42. package/entries/tell-input/utils/flag-url.ts +10 -0
  43. package/entries/tell-input/utils/types.ts +169 -0
  44. package/package.json +4 -1
  45. package/utils/cn.ts +6 -0
  46. package/utils/index.ts +10 -0
  47. package/utils/sizes.ts +48 -0
@@ -23,9 +23,5 @@ declare const __VLS_base: import("vue").DefineComponent<DrawerRootProps, {}, {},
23
23
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
24
24
  declare const _default: typeof __VLS_export;
25
25
  export default _default;
26
- type __VLS_WithSlots<T, S> = T & {
27
- new (): {
28
- $slots: S;
29
- };
30
- };
26
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
31
27
  //# sourceMappingURL=ADrawer.vue.d.ts.map
@@ -25,9 +25,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
25
25
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
26
26
  declare const _default: typeof __VLS_export;
27
27
  export default _default;
28
- type __VLS_WithSlots<T, S> = T & {
29
- new (): {
30
- $slots: S;
31
- };
32
- };
28
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
33
29
  //# sourceMappingURL=ADrawerContent.vue.d.ts.map
@@ -7,9 +7,5 @@ declare const __VLS_base: import("vue").DefineComponent<DrawerTriggerProps, {},
7
7
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
8
8
  declare const _default: typeof __VLS_export;
9
9
  export default _default;
10
- type __VLS_WithSlots<T, S> = T & {
11
- new (): {
12
- $slots: S;
13
- };
14
- };
10
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
15
11
  //# sourceMappingURL=ADrawerTrigger.vue.d.ts.map
@@ -28,9 +28,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
28
28
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
29
29
  declare const _default: typeof __VLS_export;
30
30
  export default _default;
31
- type __VLS_WithSlots<T, S> = T & {
32
- new (): {
33
- $slots: S;
34
- };
35
- };
31
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
36
32
  //# sourceMappingURL=AInput.vue.d.ts.map
@@ -16,9 +16,5 @@ declare const __VLS_base: import("vue").DefineComponent<PopoverRootProps, {}, {}
16
16
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
17
17
  declare const _default: typeof __VLS_export;
18
18
  export default _default;
19
- type __VLS_WithSlots<T, S> = T & {
20
- new (): {
21
- $slots: S;
22
- };
23
- };
19
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
24
20
  //# sourceMappingURL=APopover.vue.d.ts.map
@@ -40,9 +40,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
40
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
41
41
  declare const _default: typeof __VLS_export;
42
42
  export default _default;
43
- type __VLS_WithSlots<T, S> = T & {
44
- new (): {
45
- $slots: S;
46
- };
47
- };
43
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
48
44
  //# sourceMappingURL=APopoverContent.vue.d.ts.map
@@ -7,9 +7,5 @@ declare const __VLS_base: import("vue").DefineComponent<PopoverTriggerProps, {},
7
7
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
8
8
  declare const _default: typeof __VLS_export;
9
9
  export default _default;
10
- type __VLS_WithSlots<T, S> = T & {
11
- new (): {
12
- $slots: S;
13
- };
14
- };
10
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
15
11
  //# sourceMappingURL=APopoverTrigger.vue.d.ts.map
@@ -42,9 +42,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
42
42
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
43
43
  declare const _default: typeof __VLS_export;
44
44
  export default _default;
45
- type __VLS_WithSlots<T, S> = T & {
46
- new (): {
47
- $slots: S;
48
- };
49
- };
45
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
50
46
  //# sourceMappingURL=AResponsivePopover.vue.d.ts.map
@@ -31,9 +31,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
31
31
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
32
32
  declare const _default: typeof __VLS_export;
33
33
  export default _default;
34
- type __VLS_WithSlots<T, S> = T & {
35
- new (): {
36
- $slots: S;
37
- };
38
- };
34
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
39
35
  //# sourceMappingURL=AResponsivePopoverContent.vue.d.ts.map
@@ -12,9 +12,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
12
12
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
13
13
  declare const _default: typeof __VLS_export;
14
14
  export default _default;
15
- type __VLS_WithSlots<T, S> = T & {
16
- new (): {
17
- $slots: S;
18
- };
19
- };
15
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
20
16
  //# sourceMappingURL=AResponsivePopoverTrigger.vue.d.ts.map
@@ -22,9 +22,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
22
22
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
23
23
  declare const _default: typeof __VLS_export;
24
24
  export default _default;
25
- type __VLS_WithSlots<T, S> = T & {
26
- new (): {
27
- $slots: S;
28
- };
29
- };
25
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
30
26
  //# sourceMappingURL=ACountryFlag.vue.d.ts.map
@@ -132,9 +132,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
132
132
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
133
133
  declare const _default: typeof __VLS_export;
134
134
  export default _default;
135
- type __VLS_WithSlots<T, S> = T & {
136
- new (): {
137
- $slots: S;
138
- };
139
- };
135
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
140
136
  //# sourceMappingURL=ACountrySelect.vue.d.ts.map
@@ -137,9 +137,5 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
137
137
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
138
138
  declare const _default: typeof __VLS_export;
139
139
  export default _default;
140
- type __VLS_WithSlots<T, S> = T & {
141
- new (): {
142
- $slots: S;
143
- };
144
- };
140
+ type __VLS_WithSlots<T, S> = T & (new () => (T extends new (...args: any) => infer R ? R : {}) & { $slots: S });
145
141
  //# sourceMappingURL=ATellInput.vue.d.ts.map
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ import { DrawerRoot, type DrawerRootEmits, type DrawerRootProps } from 'vaul-vue';
3
+ import { useForwardPropsEmits } from 'reka-ui';
4
+
5
+ const props = withDefaults(defineProps<DrawerRootProps>(), {
6
+ shouldScaleBackground: true,
7
+ });
8
+ const emits = defineEmits<DrawerRootEmits>();
9
+ const forwarded = useForwardPropsEmits(props, emits);
10
+ </script>
11
+
12
+ <template>
13
+ <DrawerRoot data-slot="drawer" v-bind="forwarded">
14
+ <slot />
15
+ </DrawerRoot>
16
+ </template>
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { DrawerContent, DrawerPortal } from 'vaul-vue';
4
+ import type { DialogContentEmits, DialogContentProps } from 'reka-ui';
5
+ import { reactiveOmit } from '@vueuse/core';
6
+ import { useForwardPropsEmits } from 'reka-ui';
7
+ import { cn } from '@/utils';
8
+ import ADrawerOverlay from './ADrawerOverlay.vue';
9
+
10
+ defineOptions({ inheritAttrs: false });
11
+
12
+ const props = defineProps<DialogContentProps & { class?: HTMLAttributes['class'] }>();
13
+ const emits = defineEmits<DialogContentEmits>();
14
+ const delegated = reactiveOmit(props, 'class');
15
+ const forwarded = useForwardPropsEmits(delegated, emits);
16
+ </script>
17
+
18
+ <template>
19
+ <DrawerPortal>
20
+ <ADrawerOverlay />
21
+ <DrawerContent
22
+ data-slot="drawer-content"
23
+ v-bind="{ ...$attrs, ...forwarded }"
24
+ :class="
25
+ cn(
26
+ 'bg-background fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] shadow-2xl shadow-black/30 outline-none',
27
+ props.class
28
+ )
29
+ "
30
+ >
31
+ <div class="bg-muted mx-auto mt-4 h-2 w-[100px] rounded-full" />
32
+ <slot />
33
+ </DrawerContent>
34
+ </DrawerPortal>
35
+ </template>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { DrawerOverlay } from 'vaul-vue';
4
+ import type { DialogOverlayProps } from 'reka-ui';
5
+ import { reactiveOmit } from '@vueuse/core';
6
+ import { useForwardProps } from 'reka-ui';
7
+ import { cn } from '@/utils';
8
+
9
+ const props = defineProps<DialogOverlayProps & { class?: HTMLAttributes['class'] }>();
10
+ const delegated = reactiveOmit(props, 'class');
11
+ const forwarded = useForwardProps(delegated);
12
+ </script>
13
+
14
+ <template>
15
+ <DrawerOverlay
16
+ data-slot="drawer-overlay"
17
+ v-bind="forwarded"
18
+ :class="
19
+ cn(
20
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-40 bg-black/70',
21
+ props.class
22
+ )
23
+ "
24
+ />
25
+ </template>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ import { DrawerTrigger, type DrawerTriggerProps } from 'vaul-vue';
3
+ import { useForwardProps } from 'reka-ui';
4
+
5
+ const props = defineProps<DrawerTriggerProps>();
6
+ const forwarded = useForwardProps(props);
7
+ </script>
8
+
9
+ <template>
10
+ <DrawerTrigger data-slot="drawer-trigger" v-bind="forwarded">
11
+ <slot />
12
+ </DrawerTrigger>
13
+ </template>
@@ -0,0 +1,4 @@
1
+ export { default as ADrawer } from './components/ADrawer.vue';
2
+ export { default as ADrawerTrigger } from './components/ADrawerTrigger.vue';
3
+ export { default as ADrawerContent } from './components/ADrawerContent.vue';
4
+ export { default as ADrawerOverlay } from './components/ADrawerOverlay.vue';
@@ -0,0 +1,111 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { computed, useSlots } from 'vue';
4
+ import { useVModel } from '@vueuse/core';
5
+ import { cn } from '@/utils';
6
+ import { controlHeight, controlPaddingX, controlTextSize, DEFAULT_SIZE, type Size } from '@/utils';
7
+
8
+ const props = withDefaults(
9
+ defineProps<{
10
+ defaultValue?: string | number;
11
+ modelValue?: string | number;
12
+ class?: HTMLAttributes['class'];
13
+ /** Classes for the inner &lt;input&gt; element (useful when prefix/suffix are present). */
14
+ inputClass?: HTMLAttributes['class'];
15
+ /** Classes for the prefix wrapper. */
16
+ prefixClass?: HTMLAttributes['class'];
17
+ /** Classes for the suffix wrapper. */
18
+ suffixClass?: HTMLAttributes['class'];
19
+ size?: Size;
20
+ }>(),
21
+ { size: DEFAULT_SIZE }
22
+ );
23
+
24
+ const emits = defineEmits<{
25
+ (e: 'update:modelValue', payload: string | number): void;
26
+ }>();
27
+
28
+ defineSlots<{
29
+ /** Content rendered inside the input's border, left of the field. */
30
+ prefix?: () => unknown;
31
+ /** Content rendered inside the input's border, right of the field. */
32
+ suffix?: () => unknown;
33
+ }>();
34
+
35
+ const slots = useSlots();
36
+ const hasPrefix = computed(() => !!slots.prefix);
37
+ const hasSuffix = computed(() => !!slots.suffix);
38
+ const hasAdornment = computed(() => hasPrefix.value || hasSuffix.value);
39
+
40
+ const modelValue = useVModel(props, 'modelValue', emits, {
41
+ passive: true,
42
+ defaultValue: props.defaultValue,
43
+ });
44
+
45
+ const sizeHeight = computed(() => controlHeight[props.size]);
46
+ const sizePaddingX = computed(() => controlPaddingX[props.size]);
47
+ const sizeText = computed(() => controlTextSize[props.size]);
48
+ </script>
49
+
50
+ <template>
51
+ <!--
52
+ When prefix or suffix slots are filled we render a wrapper that owns the border,
53
+ background and focus ring — so the visible "input" is the whole bar, not just the
54
+ native element. Otherwise we render the plain native input directly so consumers
55
+ can use AInput as a drop-in for <input>.
56
+ -->
57
+ <div
58
+ v-if="hasAdornment"
59
+ :data-size="props.size"
60
+ :class="
61
+ cn(
62
+ 'border-input bg-background ring-offset-background focus-within:ring-ring inline-flex w-full items-center rounded-md border shadow-sm transition-colors focus-within:ring-1',
63
+ sizeHeight,
64
+ sizePaddingX,
65
+ sizeText,
66
+ props.class
67
+ )
68
+ "
69
+ >
70
+ <span
71
+ v-if="hasPrefix"
72
+ :class="cn('text-muted-foreground flex shrink-0 items-center pr-2', props.prefixClass)"
73
+ >
74
+ <slot name="prefix" />
75
+ </span>
76
+
77
+ <input
78
+ v-model="modelValue"
79
+ data-slot="input"
80
+ :class="
81
+ cn(
82
+ 'placeholder:text-muted-foreground h-full min-w-0 flex-1 bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-50',
83
+ props.inputClass
84
+ )
85
+ "
86
+ />
87
+
88
+ <span
89
+ v-if="hasSuffix"
90
+ :class="cn('text-muted-foreground flex shrink-0 items-center pl-2', props.suffixClass)"
91
+ >
92
+ <slot name="suffix" />
93
+ </span>
94
+ </div>
95
+
96
+ <input
97
+ v-else
98
+ v-model="modelValue"
99
+ data-slot="input"
100
+ :data-size="props.size"
101
+ :class="
102
+ cn(
103
+ 'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border py-1 shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-1 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
104
+ sizeHeight,
105
+ sizePaddingX,
106
+ sizeText,
107
+ props.class
108
+ )
109
+ "
110
+ />
111
+ </template>
@@ -0,0 +1 @@
1
+ export { default as AInput } from './components/AInput.vue';
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import type { PopoverRootEmits, PopoverRootProps } from 'reka-ui';
3
+ import { PopoverRoot, useForwardPropsEmits } from 'reka-ui';
4
+
5
+ /**
6
+ * Defaults `modal` to `true` so the popover locks page scroll, traps focus, and an overlay
7
+ * (rendered by APopoverContent when `overlay` is set) actually dims the page.
8
+ * Pass `:modal="false"` for tooltip-style non-modal popovers.
9
+ */
10
+ const props = withDefaults(defineProps<PopoverRootProps>(), { modal: true });
11
+ const emits = defineEmits<PopoverRootEmits>();
12
+ const forwarded = useForwardPropsEmits(props, emits);
13
+ </script>
14
+
15
+ <template>
16
+ <PopoverRoot v-slot="slotProps" data-slot="popover" v-bind="forwarded">
17
+ <slot v-bind="slotProps" />
18
+ </PopoverRoot>
19
+ </template>
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { reactiveOmit } from '@vueuse/core';
4
+ import {
5
+ PopoverContent,
6
+ type PopoverContentEmits,
7
+ type PopoverContentProps,
8
+ PopoverPortal,
9
+ useForwardPropsEmits,
10
+ } from 'reka-ui';
11
+ import { cn } from '@/utils';
12
+ import APopoverOverlay from './APopoverOverlay.vue';
13
+
14
+ defineOptions({ inheritAttrs: false });
15
+
16
+ const props = withDefaults(
17
+ defineProps<
18
+ PopoverContentProps & {
19
+ class?: HTMLAttributes['class'];
20
+ /** Dim the entire viewport behind the popover and block all interaction with the
21
+ * page (clicks are captured by the overlay). Pair with `<APopover :modal="true">`
22
+ * (the default) for the full modal behaviour. */
23
+ overlay?: boolean;
24
+ overlayClass?: HTMLAttributes['class'];
25
+ /**
26
+ * When true, the overlay also locks page scroll via `body { overflow: hidden }`.
27
+ * Off by default — `AResponsivePopover` opts in to this when `scrollLock="body"`.
28
+ */
29
+ overlayLockScroll?: boolean;
30
+ }
31
+ >(),
32
+ { align: 'center', sideOffset: 4, overlay: false, overlayLockScroll: false }
33
+ );
34
+ const emits = defineEmits<PopoverContentEmits>();
35
+ const delegated = reactiveOmit(props, 'class', 'overlay', 'overlayClass', 'overlayLockScroll');
36
+ const forwarded = useForwardPropsEmits(delegated, emits);
37
+ </script>
38
+
39
+ <template>
40
+ <PopoverPortal>
41
+ <!--
42
+ Overlay is a sibling of PopoverContent inside the same portal. Reka-ui's
43
+ DismissableLayer treats any pointer-down outside the content as a dismiss,
44
+ so clicking the overlay closes the popover for free. The overlay component
45
+ locks body scroll on mount and restores it on unmount.
46
+ -->
47
+ <APopoverOverlay
48
+ v-if="props.overlay"
49
+ :class="props.overlayClass"
50
+ :lock-scroll="props.overlayLockScroll"
51
+ />
52
+ <PopoverContent
53
+ data-slot="popover-content"
54
+ v-bind="{ ...$attrs, ...forwarded }"
55
+ :class="
56
+ cn(
57
+ 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-[60] w-72 rounded-md border border-border/70 p-4 shadow-xl shadow-black/15 outline-none',
58
+ props.class
59
+ )
60
+ "
61
+ >
62
+ <slot />
63
+ </PopoverContent>
64
+ </PopoverPortal>
65
+ </template>
@@ -0,0 +1,69 @@
1
+ <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue';
3
+ import { onBeforeUnmount, onMounted } from 'vue';
4
+ import { cn } from '@/utils';
5
+
6
+ defineOptions({ inheritAttrs: false });
7
+
8
+ const props = withDefaults(
9
+ defineProps<{
10
+ class?: HTMLAttributes['class'];
11
+ /**
12
+ * When true, set `body { overflow: hidden; touchAction: none }` for the lifetime of
13
+ * the overlay. Off by default because it breaks `position: sticky` on the host page.
14
+ * Prefer the event-based lock (see `AResponsivePopover`'s `scrollLock` prop) which
15
+ * keeps the page scrollbar in place.
16
+ */
17
+ lockScroll?: boolean;
18
+ }>(),
19
+ { lockScroll: false }
20
+ );
21
+
22
+ let prevBodyOverflow = '';
23
+ let prevBodyTouchAction = '';
24
+ let prevPaddingRight = '';
25
+
26
+ function getScrollbarWidth() {
27
+ if (typeof window === 'undefined') return 0;
28
+ return window.innerWidth - document.documentElement.clientWidth;
29
+ }
30
+
31
+ onMounted(() => {
32
+ if (!props.lockScroll) return;
33
+ if (typeof document === 'undefined') return;
34
+ const body = document.body;
35
+ const sbw = getScrollbarWidth();
36
+ prevBodyOverflow = body.style.overflow;
37
+ prevBodyTouchAction = body.style.touchAction;
38
+ prevPaddingRight = body.style.paddingRight;
39
+ body.style.overflow = 'hidden';
40
+ body.style.touchAction = 'none';
41
+ // Compensate for the missing scrollbar so the layout doesn't jump.
42
+ if (sbw > 0) body.style.paddingRight = `${sbw}px`;
43
+ });
44
+
45
+ onBeforeUnmount(() => {
46
+ if (!props.lockScroll) return;
47
+ if (typeof document === 'undefined') return;
48
+ const body = document.body;
49
+ body.style.overflow = prevBodyOverflow;
50
+ body.style.touchAction = prevBodyTouchAction;
51
+ body.style.paddingRight = prevPaddingRight;
52
+ });
53
+ </script>
54
+
55
+ <template>
56
+ <div
57
+ data-slot="popover-overlay"
58
+ aria-hidden="true"
59
+ :class="
60
+ cn(
61
+ // `fixed inset-0` covers the entire viewport; `pointer-events-auto` captures every
62
+ // click so it can never reach the page underneath. `z-50` keeps us above any normal
63
+ // page chrome; the popover content sits at `z-[60]`.
64
+ 'fixed inset-0 z-50 bg-black/70 pointer-events-auto data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
65
+ props.class
66
+ )
67
+ "
68
+ />
69
+ </template>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ import type { PopoverTriggerProps } from 'reka-ui';
3
+ import { PopoverTrigger, useForwardProps } from 'reka-ui';
4
+
5
+ const props = defineProps<PopoverTriggerProps>();
6
+ const forwarded = useForwardProps(props);
7
+ </script>
8
+
9
+ <template>
10
+ <PopoverTrigger data-slot="popover-trigger" v-bind="forwarded">
11
+ <slot />
12
+ </PopoverTrigger>
13
+ </template>