frappe-ui 1.0.0-beta.13 → 1.0.0-beta.14

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 (40) hide show
  1. package/package.json +1 -1
  2. package/src/components/CodeEditor/CodeEditor.cy.ts +5 -1
  3. package/src/components/Combobox/Combobox.vue +9 -11
  4. package/src/components/DatePicker/DatePicker.vue +3 -5
  5. package/src/components/DatePicker/DateRangePicker.vue +1 -1
  6. package/src/components/DatePicker/DateTimePicker.vue +3 -5
  7. package/src/components/HoverCard/HoverCard.api.md +101 -0
  8. package/src/components/HoverCard/HoverCard.cy.ts +103 -0
  9. package/src/components/HoverCard/HoverCard.md +52 -0
  10. package/src/components/HoverCard/HoverCard.vue +91 -0
  11. package/src/components/HoverCard/index.ts +2 -0
  12. package/src/components/HoverCard/stories/Arrow.vue +28 -0
  13. package/src/components/HoverCard/stories/Delays.vue +16 -0
  14. package/src/components/HoverCard/stories/Example.vue +28 -0
  15. package/src/components/HoverCard/types.ts +47 -0
  16. package/src/components/Popover/Popover.api.md +126 -41
  17. package/src/components/Popover/Popover.cy.ts +373 -41
  18. package/src/components/Popover/Popover.md +99 -4
  19. package/src/components/Popover/Popover.vue +271 -209
  20. package/src/components/Popover/index.ts +8 -1
  21. package/src/components/Popover/stories/Arrow.vue +16 -0
  22. package/src/components/Popover/stories/Bare.vue +20 -0
  23. package/src/components/Popover/stories/Click.vue +7 -4
  24. package/src/components/Popover/stories/Controlled.vue +22 -0
  25. package/src/components/Popover/stories/Dismissible.vue +19 -0
  26. package/src/components/Popover/stories/MatchTriggerWidth.vue +16 -0
  27. package/src/components/Popover/stories/SideAlign.vue +17 -0
  28. package/src/components/Popover/types.ts +98 -23
  29. package/src/components/Select/Select.vue +207 -192
  30. package/src/components/Tooltip/Tooltip.api.md +41 -3
  31. package/src/components/Tooltip/types.ts +1 -1
  32. package/src/components/shared/picker/PickerShell.vue +17 -14
  33. package/src/components/shared/popover/PopoverPanel.vue +67 -0
  34. package/src/components/shared/popover/popoverPanel.css +102 -0
  35. package/src/index.ts +1 -0
  36. package/src/molecules/editor/EditorFixedMenu.vue +2 -1
  37. package/src/molecules/editor/MenuItems.vue +26 -33
  38. package/tailwind/colorPalette.js +20 -2
  39. package/tailwind/colors.json +313 -313
  40. package/src/components/Popover/stories/Hover.vue +0 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frappe-ui",
3
- "version": "1.0.0-beta.13",
3
+ "version": "1.0.0-beta.14",
4
4
  "description": "A set of components and utilities for rapid UI development",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -75,8 +75,12 @@ describe('CodeEditor', () => {
75
75
  cy.mount(CodeEditor, {
76
76
  props: { modelValue: 'frozen', disabled: true },
77
77
  }).then(({ wrapper }) => {
78
+ // `setProps` is synchronous JS, so it must be wrapped in `cy.then` to join
79
+ // the command queue *after* the first assertion — otherwise it flips the
80
+ // prop before that assertion runs and the initial `false` state is never
81
+ // observed (the flake this guards against).
78
82
  cy.get('.cm-content').should('have.attr', 'contenteditable', 'false')
79
- wrapper.setProps({ disabled: false })
83
+ cy.then(() => wrapper.setProps({ disabled: false }))
80
84
  cy.get('.cm-content').should('have.attr', 'contenteditable', 'true')
81
85
  })
82
86
  })
@@ -18,7 +18,7 @@ import {
18
18
  FocusScope,
19
19
  } from 'reka-ui'
20
20
  import OptionIcon from '../shared/selection/OptionIcon.vue'
21
- import '../shared/selection/popoverMotion.css'
21
+ import PopoverPanel from '../shared/popover/PopoverPanel.vue'
22
22
  import ComboboxResults from './ComboboxResults.vue'
23
23
  import { usePopoverMotion } from '../../composables/usePopoverMotion'
24
24
  import { useInputLabeling } from '../../composables/useInputLabeling'
@@ -637,7 +637,6 @@ defineSlots<ComboboxSlots>()
637
637
  <ComboboxPortal :to="portalTo">
638
638
  <ComboboxContent
639
639
  data-slot="content"
640
- data-selection
641
640
  :data-variant="variant"
642
641
  :data-size="size"
643
642
  :class="[
@@ -675,10 +674,9 @@ defineSlots<ComboboxSlots>()
675
674
  @mount-auto-focus="handleFocusScopeMountAutoFocus"
676
675
  @unmount-auto-focus.prevent
677
676
  >
678
- <div
679
- data-slot="content-body"
680
- :data-motion="contentMotion"
681
- class="overflow-hidden rounded-lg bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5"
677
+ <PopoverPanel
678
+ :motion="contentMotion"
679
+ class="origin-[var(--reka-combobox-content-transform-origin)]"
682
680
  >
683
681
  <div
684
682
  v-if="isButtonMode"
@@ -718,7 +716,7 @@ defineSlots<ComboboxSlots>()
718
716
  <div v-if="$slots.footer" data-slot="footer">
719
717
  <slot name="footer" v-bind="controlSlotProps" />
720
718
  </div>
721
- </div>
719
+ </PopoverPanel>
722
720
  </FocusScope>
723
721
  </ComboboxContent>
724
722
  </ComboboxPortal>
@@ -741,10 +739,10 @@ defineSlots<ComboboxSlots>()
741
739
  outline: none !important;
742
740
  }
743
741
 
744
- /* Component-specific transform-origin; the rest of the motion lives in
745
- shared/selection/popoverMotion.css. */
746
- [data-slot='content-body'][data-motion='animated'] {
747
- transform-origin: var(--reka-combobox-content-transform-origin);
742
+ /* Block clicks while the panel plays its exit animation (the shell + open/close
743
+ motion now come from PopoverPanel; only this component-specific rule remains). */
744
+ [data-slot='content'][data-state='closed'] {
745
+ pointer-events: none;
748
746
  }
749
747
 
750
748
  @media (prefers-reduced-motion: reduce) {
@@ -120,11 +120,9 @@ const emit = defineEmits<DatePickerEmits>()
120
120
 
121
121
  const slots = defineSlots<DatePickerSlots>()
122
122
 
123
- const POPOVER_CLASSES =
124
- 'rounded-lg bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5'
125
- const contentClass = computed(() =>
126
- slots.actions ? `w-fit ${POPOVER_CLASSES}` : `w-56 ${POPOVER_CLASSES}`,
127
- )
123
+ // Layout only — the elevated shell (rounded/bg/shadow/ring) is owned by
124
+ // PopoverPanel inside PickerShell.
125
+ const contentClass = computed(() => (slots.actions ? 'w-fit' : 'w-56'))
128
126
 
129
127
  // Cast strips @deprecated markers so internal back-compat reads don't trigger TS6385.
130
128
  const dp = props as unknown as LegacyDatePickerProps
@@ -21,7 +21,7 @@
21
21
  :readonly="inputReadonly"
22
22
  :input-class="dp.inputClass"
23
23
  :display-label="displayLabel"
24
- content-class="w-fit rounded-lg bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5"
24
+ content-class="w-fit"
25
25
  @blur="commitInput()"
26
26
  @enter="commitInput(true)"
27
27
  @open="onShellOpen"
@@ -137,11 +137,9 @@ const emit = defineEmits<DateTimePickerEmits>()
137
137
 
138
138
  const slots = defineSlots<DateTimePickerSlots>()
139
139
 
140
- const POPOVER_CLASSES =
141
- 'rounded-lg bg-surface-elevation-2 shadow-2xl ring-1 ring-black ring-opacity-5'
142
- const contentClass = computed(() =>
143
- slots.actions ? `w-fit ${POPOVER_CLASSES}` : `w-56 ${POPOVER_CLASSES}`,
144
- )
140
+ // Layout only — the elevated shell (rounded/bg/shadow/ring) is owned by
141
+ // PopoverPanel inside PickerShell.
142
+ const contentClass = computed(() => (slots.actions ? 'w-fit' : 'w-56'))
145
143
 
146
144
  const dp = props as unknown as LegacyDatePickerProps
147
145
 
@@ -0,0 +1,101 @@
1
+ <!-- Auto Generated by scripts/propsgen.ts -->
2
+ <script setup>
3
+ import PropsTable from '@/components/Docs/PropsTable.vue'
4
+ import SlotsTable from '@/components/Docs/SlotsTable.vue'
5
+ import EmitsTable from '@/components/Docs/EmitsTable.vue'
6
+
7
+ const propsData = [
8
+ {
9
+ name: 'side',
10
+ description: 'Side of the trigger the card is placed on.',
11
+ required: false,
12
+ type: '"bottom" | "top" | "right" | "left"',
13
+ default: '"bottom"'
14
+ },
15
+ {
16
+ name: 'align',
17
+ description: 'Alignment of the card relative to the trigger.',
18
+ required: false,
19
+ type: '"start" | "center" | "end"',
20
+ default: '"start"'
21
+ },
22
+ {
23
+ name: 'offset',
24
+ description: 'Distance in pixels between the card and the trigger.',
25
+ required: false,
26
+ type: 'number',
27
+ default: '4'
28
+ },
29
+ {
30
+ name: 'portalTo',
31
+ description: 'Where the card is teleported to in the DOM.',
32
+ required: false,
33
+ type: 'string | HTMLElement',
34
+ default: '"body"'
35
+ },
36
+ {
37
+ name: 'collisionPadding',
38
+ description: 'Padding (in pixels) kept between the card and the viewport edges when\nrepositioning to avoid collisions.',
39
+ required: false,
40
+ type: 'number',
41
+ default: '10'
42
+ },
43
+ {
44
+ name: 'hoverDelay',
45
+ description: 'Delay (in seconds) from when the pointer enters the trigger until the card\nopens. Matches the Tooltip convention of using seconds.',
46
+ required: false,
47
+ type: 'number',
48
+ default: '0.3'
49
+ },
50
+ {
51
+ name: 'leaveDelay',
52
+ description: 'Delay (in seconds) from when the pointer leaves the trigger or card until\nthe card closes.',
53
+ required: false,
54
+ type: 'number',
55
+ default: '0.3'
56
+ },
57
+ {
58
+ name: 'arrow',
59
+ description: 'Render a small arrow pointing at the trigger. Styled to match the panel\nsurface.',
60
+ required: false,
61
+ type: 'boolean',
62
+ default: 'false'
63
+ },
64
+ {
65
+ name: 'open',
66
+ description: '',
67
+ required: false,
68
+ type: 'boolean',
69
+ default: 'false'
70
+ }
71
+ ]
72
+
73
+ const slotsData = [
74
+ {
75
+ name: 'trigger',
76
+ description: 'Trigger element. Rendered as-child so hover/focus a11y is auto-wired.',
77
+ type: '{ open: boolean; }'
78
+ },
79
+ {
80
+ name: 'default',
81
+ description: 'Card contents, rendered inside the standard PopoverPanel shell.',
82
+ type: 'any'
83
+ }
84
+ ]
85
+
86
+ const emitsData = [
87
+ {
88
+ name: 'update:open',
89
+ description: 'Fired when the open state changes.',
90
+ type: 'unknown[]'
91
+ }
92
+ ]
93
+ </script>
94
+ ## API Reference
95
+
96
+ <PropsTable name="HoverCard" :data="propsData"/>
97
+
98
+ <SlotsTable :data="slotsData"/>
99
+
100
+ <EmitsTable :data="emitsData"/>
101
+
@@ -0,0 +1,103 @@
1
+ import { defineComponent, h, ref } from 'vue'
2
+ import HoverCard from './HoverCard.vue'
3
+ import Button from '../Button/Button.vue'
4
+
5
+ const Slots = {
6
+ trigger: () => h(Button, { 'data-cy': 'trigger' }, () => 'Hover me'),
7
+ default: () => h('div', { 'data-cy': 'content' }, 'Card content'),
8
+ }
9
+
10
+ describe('HoverCard', () => {
11
+ it('opens on hover after the hoverDelay and renders inside the panel shell', () => {
12
+ cy.clock()
13
+ cy.mount(HoverCard, {
14
+ slots: Slots,
15
+ props: { hoverDelay: 0.5, leaveDelay: 0.3 },
16
+ })
17
+
18
+ cy.get('[data-slot="content"]').should('not.exist')
19
+ cy.get('[data-cy="trigger"]').trigger('pointerenter')
20
+
21
+ // Still closed before the delay elapses.
22
+ cy.get('[data-slot="content"]').should('not.exist')
23
+ cy.tick(500)
24
+
25
+ cy.get('[data-slot="content"]').should('exist')
26
+ cy.get('[data-slot="content"]')
27
+ .find('[data-slot="content-body"]')
28
+ .find('[data-cy="content"]')
29
+ .should('have.text', 'Card content')
30
+ })
31
+
32
+ it('closes when the open model is set back to false', () => {
33
+ // reka owns the leave-delay timing internally; here we verify the wrapper's
34
+ // two-way open binding tears the card down when the parent closes it.
35
+ const Harness = defineComponent({
36
+ setup() {
37
+ const open = ref(false)
38
+ return () =>
39
+ h('div', [
40
+ h(
41
+ Button,
42
+ {
43
+ 'data-cy': 'toggle',
44
+ onClick: () => (open.value = !open.value),
45
+ },
46
+ () => 'Toggle',
47
+ ),
48
+ h(
49
+ HoverCard,
50
+ {
51
+ open: open.value,
52
+ 'onUpdate:open': (value: boolean) => (open.value = value),
53
+ hoverDelay: 0,
54
+ },
55
+ Slots,
56
+ ),
57
+ ])
58
+ },
59
+ })
60
+
61
+ cy.mount(Harness)
62
+ cy.get('[data-cy="toggle"]').click()
63
+ cy.get('[data-slot="content"]').should('exist')
64
+ cy.get('[data-cy="toggle"]').click()
65
+ cy.get('[data-slot="content"]').should('not.exist')
66
+ })
67
+
68
+ it('positions the card via side + align', () => {
69
+ cy.clock()
70
+ cy.mount(HoverCard, {
71
+ slots: Slots,
72
+ props: { hoverDelay: 0, side: 'right', align: 'end' },
73
+ })
74
+
75
+ cy.get('[data-cy="trigger"]').trigger('pointerenter')
76
+ cy.tick(0)
77
+ cy.get('[data-slot="content"]')
78
+ .should('have.attr', 'data-side', 'right')
79
+ .and('have.attr', 'data-align', 'end')
80
+ })
81
+
82
+ it('renders an arrow when arrow is set', () => {
83
+ cy.clock()
84
+ cy.mount(HoverCard, { slots: Slots, props: { hoverDelay: 0, arrow: true } })
85
+
86
+ cy.get('[data-cy="trigger"]').trigger('pointerenter')
87
+ cy.tick(0)
88
+ cy.get('[data-slot="content"]').find('[data-slot="arrow"]').should('exist')
89
+ })
90
+
91
+ it('always uses animated motion for hover opens', () => {
92
+ cy.clock()
93
+ cy.mount(HoverCard, { slots: Slots, props: { hoverDelay: 0 } })
94
+
95
+ cy.get('[data-cy="trigger"]').trigger('pointerenter')
96
+ cy.tick(0)
97
+ cy.get('[data-slot="content-body"]').should(
98
+ 'have.attr',
99
+ 'data-motion',
100
+ 'animated',
101
+ )
102
+ })
103
+ })
@@ -0,0 +1,52 @@
1
+ # HoverCard
2
+
3
+ Shows a floating panel when the pointer rests on (or the keyboard focuses) a
4
+ trigger — for profile previews, link previews, and other sighted-only context.
5
+ Built on [reka-ui](https://reka-ui.com/)'s `HoverCard` primitives.
6
+
7
+ Use `HoverCard` for hover-driven panels and [`Popover`](./popover) for
8
+ click-driven ones. For short text hints prefer [`Tooltip`](./tooltip) instead.
9
+
10
+ ## Example
11
+
12
+ `#trigger` is rendered as-child, so hover and focus a11y are wired
13
+ automatically. `#default` renders inside the standard panel shell.
14
+
15
+ <ComponentPreview name="HoverCard-Example" />
16
+
17
+ ## Delays
18
+
19
+ `hoverDelay` and `leaveDelay` are in **seconds** (consistent with `Tooltip`).
20
+ `hoverDelay` is how long the pointer must rest before the card opens;
21
+ `leaveDelay` is how long after the pointer leaves before it closes.
22
+
23
+ <ComponentPreview name="HoverCard-Delays" />
24
+
25
+ ## Arrow
26
+
27
+ Set `arrow` to render a small arrow pointing at the trigger, styled to match the
28
+ panel surface.
29
+
30
+ <ComponentPreview name="HoverCard-Arrow" />
31
+
32
+ ## Styling
33
+
34
+ Like `Popover`, the card owns its panel shell and exposes the same stable
35
+ `data-slot` hooks — `[data-slot="content"]` for the portaled content and
36
+ `[data-slot="content-body"]` for the shell that owns the visuals. There are no
37
+ class-injection props.
38
+
39
+ ## Motion
40
+
41
+ A hover open always uses the animated scale-from-trigger entrance (180ms in /
42
+ 140ms out), and `prefers-reduced-motion` is respected. No configuration is
43
+ required.
44
+
45
+ ## Notes
46
+
47
+ - `HoverCard` is for sighted-only enhancement — don't put essential actions or
48
+ information that can only be reached by hovering.
49
+ - The card stays open while the pointer is over either the trigger or the card,
50
+ so users can move into it to interact.
51
+
52
+ <!-- @include: ./HoverCard.api.md -->
@@ -0,0 +1,91 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ HoverCardArrow,
4
+ HoverCardContent,
5
+ HoverCardPortal,
6
+ HoverCardRoot,
7
+ HoverCardTrigger,
8
+ } from 'reka-ui'
9
+ import { computed } from 'vue'
10
+ import PopoverPanel from '../shared/popover/PopoverPanel.vue'
11
+ import type { HoverCardProps } from './types'
12
+
13
+ defineOptions({
14
+ inheritAttrs: false,
15
+ })
16
+
17
+ const props = withDefaults(defineProps<HoverCardProps>(), {
18
+ side: 'bottom',
19
+ align: 'start',
20
+ offset: 4,
21
+ portalTo: 'body',
22
+ collisionPadding: 10,
23
+ hoverDelay: 0.3,
24
+ leaveDelay: 0.3,
25
+ arrow: false,
26
+ })
27
+
28
+ const open = defineModel<boolean>('open', { default: false })
29
+
30
+ // reka HoverCard delays are in milliseconds; the public API uses seconds to
31
+ // stay consistent with Tooltip.
32
+ const openDelay = computed(() => props.hoverDelay * 1000)
33
+ const closeDelay = computed(() => props.leaveDelay * 1000)
34
+
35
+ defineExpose({
36
+ open: () => {
37
+ open.value = true
38
+ },
39
+ close: () => {
40
+ open.value = false
41
+ },
42
+ })
43
+
44
+ defineSlots<{
45
+ /** Trigger element. Rendered as-child so hover/focus a11y is auto-wired. */
46
+ trigger?: (props: { open: boolean }) => any
47
+ /** Card contents, rendered inside the standard PopoverPanel shell. */
48
+ default?: () => any
49
+ }>()
50
+ </script>
51
+
52
+ <template>
53
+ <HoverCardRoot
54
+ v-model:open="open"
55
+ :open-delay="openDelay"
56
+ :close-delay="closeDelay"
57
+ >
58
+ <HoverCardTrigger as-child>
59
+ <slot name="trigger" :open="open" />
60
+ </HoverCardTrigger>
61
+ <HoverCardPortal :to="portalTo">
62
+ <HoverCardContent
63
+ data-slot="content"
64
+ class="z-[100]"
65
+ :side="side"
66
+ :align="align"
67
+ :side-offset="offset"
68
+ :collision-padding="collisionPadding"
69
+ v-bind="$attrs"
70
+ >
71
+ <!--
72
+ Hover opens always want the scale-from-trigger entrance, so motion is
73
+ fixed to 'animated' rather than driven by usePopoverMotion (which keys
74
+ on pointerdown and would classify a hover open as 'instant'). The
75
+ panel relies on the ancestor HoverCardContent's data-state for motion.
76
+ -->
77
+ <PopoverPanel
78
+ motion="animated"
79
+ class="origin-[var(--reka-hover-card-content-transform-origin)]"
80
+ >
81
+ <slot />
82
+ </PopoverPanel>
83
+ <HoverCardArrow
84
+ v-if="arrow"
85
+ data-slot="arrow"
86
+ class="fill-surface-elevation-2"
87
+ />
88
+ </HoverCardContent>
89
+ </HoverCardPortal>
90
+ </HoverCardRoot>
91
+ </template>
@@ -0,0 +1,2 @@
1
+ export { default as HoverCard } from './HoverCard.vue'
2
+ export type { HoverCardProps } from './types'
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ import { Avatar, HoverCard } from 'frappe-ui'
3
+ </script>
4
+
5
+ <template>
6
+ <HoverCard side="top" arrow>
7
+ <template #trigger>
8
+ <a href="#" class="text-ink-gray-9 underline underline-offset-2">
9
+ @jane
10
+ </a>
11
+ </template>
12
+ <template #default>
13
+ <div class="flex w-64 gap-3 p-3">
14
+ <Avatar
15
+ label="Jane Doe"
16
+ image="https://avatars.githubusercontent.com/u/499550?v=4"
17
+ size="2xl"
18
+ />
19
+ <div>
20
+ <p class="text-p-base font-medium text-ink-gray-9">Jane Doe</p>
21
+ <p class="mt-1 text-p-sm text-ink-gray-6">
22
+ Designer. Likes hover cards.
23
+ </p>
24
+ </div>
25
+ </div>
26
+ </template>
27
+ </HoverCard>
28
+ </template>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ import { Button, HoverCard } from 'frappe-ui'
3
+ </script>
4
+
5
+ <template>
6
+ <HoverCard :hover-delay="0.1" :leave-delay="0.5">
7
+ <template #trigger>
8
+ <Button>Hover me</Button>
9
+ </template>
10
+ <template #default>
11
+ <div class="p-3 text-p-sm text-ink-gray-7">
12
+ Opens after 0.1s, closes 0.5s after the pointer leaves.
13
+ </div>
14
+ </template>
15
+ </HoverCard>
16
+ </template>
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ import { Avatar, HoverCard } from 'frappe-ui'
3
+ </script>
4
+
5
+ <template>
6
+ <HoverCard>
7
+ <template #trigger>
8
+ <a href="#" class="text-ink-gray-9 underline underline-offset-2">
9
+ @jane
10
+ </a>
11
+ </template>
12
+ <template #default>
13
+ <div class="flex w-64 gap-3 p-3">
14
+ <Avatar
15
+ label="Jane Doe"
16
+ image="https://avatars.githubusercontent.com/u/499550?v=4"
17
+ size="2xl"
18
+ />
19
+ <div>
20
+ <p class="text-p-base font-medium text-ink-gray-9">Jane Doe</p>
21
+ <p class="mt-1 text-p-sm text-ink-gray-6">
22
+ Designer. Likes hover cards.
23
+ </p>
24
+ </div>
25
+ </div>
26
+ </template>
27
+ </HoverCard>
28
+ </template>
@@ -0,0 +1,47 @@
1
+ import { type HoverCardContentProps, type HoverCardPortalProps } from 'reka-ui'
2
+
3
+ export interface HoverCardProps {
4
+ /**
5
+ * Side of the trigger the card is placed on.
6
+ */
7
+ side?: HoverCardContentProps['side']
8
+
9
+ /**
10
+ * Alignment of the card relative to the trigger.
11
+ */
12
+ align?: HoverCardContentProps['align']
13
+
14
+ /**
15
+ * Distance in pixels between the card and the trigger.
16
+ */
17
+ offset?: number
18
+
19
+ /**
20
+ * Where the card is teleported to in the DOM.
21
+ */
22
+ portalTo?: HoverCardPortalProps['to']
23
+
24
+ /**
25
+ * Padding (in pixels) kept between the card and the viewport edges when
26
+ * repositioning to avoid collisions.
27
+ */
28
+ collisionPadding?: number
29
+
30
+ /**
31
+ * Delay (in seconds) from when the pointer enters the trigger until the card
32
+ * opens. Matches the Tooltip convention of using seconds.
33
+ */
34
+ hoverDelay?: number
35
+
36
+ /**
37
+ * Delay (in seconds) from when the pointer leaves the trigger or card until
38
+ * the card closes.
39
+ */
40
+ leaveDelay?: number
41
+
42
+ /**
43
+ * Render a small arrow pointing at the trigger. Styled to match the panel
44
+ * surface.
45
+ */
46
+ arrow?: boolean
47
+ }