ajo-ui 0.1.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 (103) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +126 -0
  3. package/dist/accordion.js +130 -0
  4. package/dist/avatar.js +54 -0
  5. package/dist/calendar.js +2 -0
  6. package/dist/carousel.js +239 -0
  7. package/dist/chart.js +790 -0
  8. package/dist/checkbox-group.js +70 -0
  9. package/dist/checkbox.js +77 -0
  10. package/dist/chunks/bar-CVafh6C1.js +99 -0
  11. package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
  12. package/dist/chunks/collection-DtRB63U4.js +111 -0
  13. package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
  14. package/dist/chunks/menu-B45IyHHC.js +704 -0
  15. package/dist/chunks/native-BJdhd9XJ.js +20 -0
  16. package/dist/chunks/popup-C8Bb3l_g.js +459 -0
  17. package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
  18. package/dist/chunks/position-D6_i_SRn.js +434 -0
  19. package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
  20. package/dist/collapsible.js +106 -0
  21. package/dist/command.js +263 -0
  22. package/dist/context-menu.js +112 -0
  23. package/dist/data-table.js +5 -0
  24. package/dist/dialog.js +207 -0
  25. package/dist/direction.js +22 -0
  26. package/dist/drawer.js +139 -0
  27. package/dist/field.js +26 -0
  28. package/dist/index.js +38 -0
  29. package/dist/input-date.js +994 -0
  30. package/dist/input-group.js +52 -0
  31. package/dist/input-otp.js +179 -0
  32. package/dist/menu.js +2 -0
  33. package/dist/menubar.js +236 -0
  34. package/dist/message-scroller.js +446 -0
  35. package/dist/navigation-menu.js +330 -0
  36. package/dist/popover.js +307 -0
  37. package/dist/progress.js +39 -0
  38. package/dist/radio-group.js +107 -0
  39. package/dist/resizable.js +172 -0
  40. package/dist/select.js +961 -0
  41. package/dist/sidebar.js +343 -0
  42. package/dist/slider.js +259 -0
  43. package/dist/switch.js +53 -0
  44. package/dist/tabs.js +182 -0
  45. package/dist/toast.js +492 -0
  46. package/dist/toggle-group.js +111 -0
  47. package/dist/toggle.js +52 -0
  48. package/dist/toolbar.js +127 -0
  49. package/dist/tooltip.js +196 -0
  50. package/dist/utils.js +104 -0
  51. package/dist/virtual-list.js +2 -0
  52. package/package.json +250 -0
  53. package/src/accordion.tsx +261 -0
  54. package/src/availability.ts +261 -0
  55. package/src/avatar.tsx +99 -0
  56. package/src/bar.ts +156 -0
  57. package/src/calendar.tsx +1441 -0
  58. package/src/carousel.tsx +424 -0
  59. package/src/chart.tsx +1194 -0
  60. package/src/checkbox-group.tsx +132 -0
  61. package/src/checkbox.tsx +130 -0
  62. package/src/collapsible.tsx +188 -0
  63. package/src/collection.ts +154 -0
  64. package/src/command.tsx +511 -0
  65. package/src/context-menu.tsx +233 -0
  66. package/src/data-table-contract.ts +143 -0
  67. package/src/data-table-model.ts +760 -0
  68. package/src/data-table.tsx +475 -0
  69. package/src/dialog.tsx +393 -0
  70. package/src/direction.tsx +45 -0
  71. package/src/drawer.tsx +251 -0
  72. package/src/field.tsx +61 -0
  73. package/src/index.ts +37 -0
  74. package/src/input-date.tsx +1539 -0
  75. package/src/input-group.tsx +142 -0
  76. package/src/input-otp.tsx +324 -0
  77. package/src/menu-cluster.ts +124 -0
  78. package/src/menu.tsx +1095 -0
  79. package/src/menubar.tsx +459 -0
  80. package/src/message-scroller.tsx +732 -0
  81. package/src/native.ts +26 -0
  82. package/src/navigation-menu.tsx +578 -0
  83. package/src/popover.tsx +519 -0
  84. package/src/popup-surface.tsx +31 -0
  85. package/src/popup.ts +569 -0
  86. package/src/position.ts +523 -0
  87. package/src/progress.tsx +70 -0
  88. package/src/radio-group.tsx +186 -0
  89. package/src/resizable.tsx +310 -0
  90. package/src/segments.ts +922 -0
  91. package/src/select.tsx +1501 -0
  92. package/src/sidebar.tsx +683 -0
  93. package/src/slider.tsx +424 -0
  94. package/src/switch.tsx +104 -0
  95. package/src/tabs.tsx +314 -0
  96. package/src/toast.tsx +923 -0
  97. package/src/toggle-group.tsx +249 -0
  98. package/src/toggle.tsx +91 -0
  99. package/src/toolbar.tsx +212 -0
  100. package/src/tooltip.tsx +359 -0
  101. package/src/utils.ts +204 -0
  102. package/src/virtual-list.tsx +205 -0
  103. package/src/virtual.ts +385 -0
@@ -0,0 +1,519 @@
1
+ import type { IntrinsicElements, Stateful, Stateless, WithChildren } from 'ajo'
2
+ import type { Host } from 'ajo-cloves'
3
+ import { callHandler, callRef, statefulRootAttrs as rootAttrs } from 'ajo-cloves'
4
+ import { context } from 'ajo/context'
5
+ import { contentAttrs, popup, type PopupView } from './popup'
6
+ import { PopupSurface } from './popup-surface'
7
+ import type { ReservedPositionArg } from './position'
8
+ import { popupStyle, triggerAttrs, type FixedArgs, type OmitArg, type PopupPosition } from './utils'
9
+ export type { PopupPlacement, PopupPosition } from './utils'
10
+
11
+ /** Interaction that opens a popover. */
12
+ export type PopoverOpenOn = 'click' | 'hover'
13
+
14
+ /** Props for the popover root and its controlled open state. */
15
+ export type PopoverArgs = WithChildren<OmitArg<IntrinsicElements['div'], 'onchange' | ReservedPositionArg> & PopupPosition & {
16
+ /** Accessible and visible title owned by the popover surface. */
17
+ label: string
18
+ /** Optional visible description associated with the popover surface. */
19
+ description?: string
20
+ /** Controlled open state. */
21
+ open?: boolean
22
+ /** Initial open state for uncontrolled usage. */
23
+ defaultOpen?: boolean
24
+ /** Interaction mode that opens the popover. Fixed at mount. Defaults to click. */
25
+ openOn?: PopoverOpenOn
26
+ /** Hover-mode-only delay before opening, in milliseconds. */
27
+ openDelay?: number
28
+ /** Hover-mode-only delay before closing, in milliseconds. */
29
+ closeDelay?: number
30
+ /** Disable trigger activation. */
31
+ disabled?: boolean
32
+ /** Called whenever the popover opens or closes. */
33
+ onOpenChange?: (open: boolean, event?: Event) => void
34
+ /** Additional CSS classes. */
35
+ class?: string
36
+ }> & FixedArgs<'onchange' | ReservedPositionArg>
37
+
38
+ type PopoverTriggerSharedArgs = {
39
+ /** Render the trigger wrapper as an anchor, button, or span. */
40
+ as?: 'a' | 'button' | 'span'
41
+ /** Additional CSS classes. */
42
+ class?: string
43
+ }
44
+
45
+ /** Props for the element that opens a popover. */
46
+ export type PopoverTriggerArgs = WithChildren<
47
+ | (IntrinsicElements['button'] & PopoverTriggerSharedArgs & { as?: 'button' })
48
+ | (IntrinsicElements['a'] & PopoverTriggerSharedArgs & { as: 'a' })
49
+ | (IntrinsicElements['span'] & PopoverTriggerSharedArgs & { as: 'span' })
50
+ >
51
+
52
+ type PopoverTriggerAllArgs = WithChildren<(IntrinsicElements['a'] & IntrinsicElements['button'] & IntrinsicElements['span']) & PopoverTriggerSharedArgs>
53
+
54
+ /** Props for the positioned popover panel. Semantics come from the root label. */
55
+ export type PopoverContentArgs = WithChildren<OmitArg<IntrinsicElements['div'], 'align' | 'aria-describedby' | 'aria-label' | 'aria-labelledby' | 'aria-modal' | 'id' | 'popover' | 'role' | 'tabindex' | 'tabIndex' | ReservedPositionArg> & {
56
+ /** Extends the visual surface toward its positioning reference. */
57
+ arrow?: boolean
58
+ /** Additional CSS classes. */
59
+ class?: string
60
+ /** Inline CSS string. */
61
+ style?: string
62
+ }> & FixedArgs<'aria-describedby' | 'aria-label' | 'aria-labelledby' | 'aria-modal' | 'gap' | 'id' | 'placement' | 'popover' | 'role' | 'tabindex' | 'tabIndex' | ReservedPositionArg>
63
+
64
+ /** Props for an explicit positioning anchor. */
65
+ export type PopoverAnchorArgs = WithChildren<IntrinsicElements['div'] & {
66
+ /** Additional CSS classes. */
67
+ class?: string
68
+ }>
69
+
70
+ type PopoverContextValue = {
71
+ arrowAttrs: PopupView['arrowAttrs']
72
+ close: (event?: Event) => void
73
+ content: HTMLDivElement | null
74
+ contentId: string
75
+ contentStyle: PopupView['contentStyle']
76
+ description?: string
77
+ disabled: boolean
78
+ label: string
79
+ open: boolean
80
+ openOn: PopoverOpenOn
81
+ openWithDelay?: (event?: Event) => void
82
+ registerContentFocus?: (focused: boolean, event?: Event) => void
83
+ registerContentHover?: (hovering: boolean, event?: Event) => void
84
+ registerTriggerFocus?: (focused: boolean, event?: Event) => void
85
+ registerTriggerHover?: (hovering: boolean, event?: Event) => void
86
+ setReference: (element: HTMLElement | null) => void
87
+ setContent: (element: HTMLDivElement | null) => void
88
+ setOpen: (open: boolean, event?: Event) => void
89
+ setTrigger: (element: HTMLElement | null) => void
90
+ trigger: HTMLElement | null
91
+ triggerId: string
92
+ }
93
+
94
+ const PopoverContext = context<PopoverContextValue | null>(null)
95
+
96
+ /** Shared root body for both interaction modes; the mode is fixed per host. */
97
+ function* popoverEngine(
98
+ this: Host<HTMLDivElement, PopoverArgs>,
99
+ initial: PopoverArgs,
100
+ mode: PopoverOpenOn,
101
+ ) {
102
+ let closeDelay = 300
103
+ let disabled = false
104
+ let onOpenChange: PopoverArgs['onOpenChange']
105
+ let openDelay = 700
106
+ let popover: PopupView<HTMLElement, HTMLDivElement>
107
+
108
+ popover = popup<HTMLElement, HTMLDivElement>(this, {
109
+ prefix: 'popover',
110
+ profile: 'popover',
111
+ initialOpen: Boolean(initial.open ?? initial.defaultOpen),
112
+ disabled: () => disabled,
113
+ hover: mode === 'hover' ? {
114
+ openDelay: () => openDelay,
115
+ closeDelay: () => closeDelay,
116
+ } : undefined,
117
+ onOpenChange: (next, event) => onOpenChange?.(next, event),
118
+ reference: view => view.reference ?? view.trigger,
119
+ referenceHidden: 'close',
120
+ dismiss: {
121
+ prevent: mode === 'hover',
122
+ outside: true,
123
+ onDismiss: (event, view) => {
124
+ view.cancelHover()
125
+ view.setOpen(false, event)
126
+ if (mode === 'click') queueMicrotask(() => view.trigger?.focus())
127
+ },
128
+ },
129
+ })
130
+
131
+ const close = (event?: Event) => {
132
+ popover.cancelHover()
133
+ popover.close(event)
134
+ }
135
+
136
+ const openWithDelay = (event?: Event) => popover.hold('trigger', event as Event)
137
+
138
+ const registerTriggerHover = (hovering: boolean, event?: Event) =>
139
+ hovering ? popover.hold('trigger', event as Event) : popover.release('trigger', event as Event)
140
+
141
+ const registerContentHover = (hovering: boolean, event?: Event) =>
142
+ hovering ? popover.hold('content', event as Event) : popover.release('content', event as Event)
143
+
144
+ const registerTriggerFocus = (focused: boolean, event?: Event) =>
145
+ focused ? popover.hold('focus-trigger', event as Event) : popover.release('focus-trigger', event as Event)
146
+
147
+ const registerContentFocus = (focused: boolean, event?: Event) =>
148
+ focused ? popover.hold('focus-content', event as Event) : popover.release('focus-content', event as Event)
149
+
150
+ for (const args of this) {
151
+ closeDelay = Math.max(0, Number(args.closeDelay ?? 300))
152
+ disabled = Boolean(args.disabled)
153
+ onOpenChange = args.onOpenChange
154
+ openDelay = Math.max(0, Number(args.openDelay ?? 700))
155
+ const opened = popover.sync(args.open == null ? null : Boolean(args.open), {
156
+ placement: args.placement,
157
+ gap: args.gap,
158
+ })
159
+
160
+ PopoverContext({
161
+ ...popover,
162
+ close: mode === 'hover' ? close : popover.close,
163
+ description: args.description,
164
+ disabled,
165
+ label: args.label,
166
+ open: opened,
167
+ openOn: mode,
168
+ ...(mode === 'hover' ? {
169
+ openWithDelay,
170
+ registerContentFocus,
171
+ registerContentHover,
172
+ registerTriggerFocus,
173
+ registerTriggerHover,
174
+ } : {}),
175
+ })
176
+
177
+ yield <>{args.children}</>
178
+ }
179
+ }
180
+
181
+ const PopoverClickRoot: Stateful<PopoverArgs> = function* (args) {
182
+ yield* popoverEngine.call(this, args, 'click')
183
+ }
184
+
185
+
186
+ const PopoverHoverRoot: Stateful<PopoverArgs> = function* (args) {
187
+ yield* popoverEngine.call(this, args, 'hover')
188
+ }
189
+
190
+
191
+ /** Unstyled root provider for a popover. */
192
+ const Popover: Stateless<PopoverArgs> = ({
193
+ children,
194
+ class: classes,
195
+ 'data-slot': slot = 'popover',
196
+ closeDelay,
197
+ defaultOpen,
198
+ description,
199
+ disabled,
200
+ gap,
201
+ label,
202
+ onOpenChange,
203
+ open,
204
+ openDelay,
205
+ openOn = 'click',
206
+ placement,
207
+ ...attrs
208
+ }) => openOn === 'hover' ? (
209
+ <PopoverHoverRoot
210
+ {...rootAttrs(attrs)}
211
+ closeDelay={closeDelay}
212
+ defaultOpen={defaultOpen}
213
+ description={description}
214
+ disabled={disabled}
215
+ gap={gap}
216
+ label={label}
217
+ onOpenChange={onOpenChange}
218
+ open={open}
219
+ openDelay={openDelay}
220
+ placement={placement}
221
+ attr:class={classes}
222
+ attr:data-slot={slot}
223
+ >
224
+ {children}
225
+ </PopoverHoverRoot>
226
+ ) : (
227
+ <PopoverClickRoot
228
+ {...rootAttrs(attrs)}
229
+ defaultOpen={defaultOpen}
230
+ description={description}
231
+ disabled={disabled}
232
+ gap={gap}
233
+ label={label}
234
+ onOpenChange={onOpenChange}
235
+ open={open}
236
+ placement={placement}
237
+ attr:class={classes}
238
+ attr:data-slot={slot}
239
+ >
240
+ {children}
241
+ </PopoverClickRoot>
242
+ )
243
+
244
+ /** Unstyled button that opens a Popover. */
245
+ const PopoverTrigger: Stateless<PopoverTriggerArgs> = args => {
246
+ const popover = PopoverContext()
247
+ const all = args as PopoverTriggerAllArgs
248
+
249
+ if (popover?.openOn === 'hover') {
250
+ const {
251
+ as = 'button',
252
+ children,
253
+ class: classes,
254
+ 'data-slot': slot = 'popover-trigger',
255
+ disabled,
256
+ id,
257
+ ref,
258
+ type = 'button',
259
+ 'set:onblur': onBlur,
260
+ 'set:onfocus': onFocus,
261
+ 'set:onkeydown': onKeydown,
262
+ 'set:onmouseleave': onMouseLeave,
263
+ 'set:onmouseenter': onMouseEnter,
264
+ ...attrs
265
+ } = all
266
+ const disabledFlag = Boolean(disabled ?? popover.disabled)
267
+
268
+ const common = {
269
+ ...attrs,
270
+ ...triggerAttrs({
271
+ controls: popover.contentId,
272
+ expanded: popover.open,
273
+ haspopup: 'dialog',
274
+ id,
275
+ open: popover.open,
276
+ ref,
277
+ setTrigger: popover.setTrigger,
278
+ triggerId: popover.triggerId,
279
+ }),
280
+ class: classes,
281
+ 'data-slot': slot,
282
+ 'set:onblur': (event: FocusEvent) => {
283
+ callHandler(onBlur, event)
284
+ popover.registerTriggerFocus?.(false, event)
285
+ },
286
+ 'set:onfocus': (event: FocusEvent) => {
287
+ callHandler(onFocus, event)
288
+ if (event.defaultPrevented || disabledFlag) return
289
+ popover.registerTriggerFocus?.(true, event)
290
+ },
291
+ 'set:onkeydown': (event: KeyboardEvent) => {
292
+ callHandler(onKeydown, event)
293
+ },
294
+ 'set:onmouseleave': (event: MouseEvent) => {
295
+ callHandler(onMouseLeave, event)
296
+ popover.registerTriggerHover?.(false, event)
297
+ },
298
+ 'set:onmouseenter': (event: MouseEvent) => {
299
+ callHandler(onMouseEnter, event)
300
+ if (event.defaultPrevented || disabledFlag) return
301
+ popover.registerTriggerHover?.(true, event)
302
+ },
303
+ }
304
+
305
+ if (as === 'a') {
306
+ const anchor = attrs as IntrinsicElements['a']
307
+
308
+ return (
309
+ <a
310
+ {...common}
311
+ aria-disabled={disabledFlag ? 'true' : undefined}
312
+ href={disabledFlag ? undefined : anchor.href}
313
+ tabIndex={disabledFlag ? -1 : anchor.tabIndex}
314
+ >
315
+ {children}
316
+ </a>
317
+ )
318
+ }
319
+
320
+ if (as === 'span') {
321
+ return (
322
+ <span
323
+ {...common}
324
+ aria-disabled={disabledFlag ? 'true' : undefined}
325
+ >
326
+ {children}
327
+ </span>
328
+ )
329
+ }
330
+
331
+ return (
332
+ <button
333
+ {...common}
334
+ disabled={disabledFlag}
335
+ type={type}
336
+ >
337
+ {children}
338
+ </button>
339
+ )
340
+ }
341
+
342
+ const {
343
+ children,
344
+ 'data-slot': slot = 'popover-trigger',
345
+ disabled,
346
+ id,
347
+ ref,
348
+ type = 'button',
349
+ 'set:onclick': onClick,
350
+ ...attrs
351
+ } = all
352
+ const disabledFlag = Boolean(disabled ?? popover?.disabled)
353
+
354
+ return (
355
+ <button
356
+ {...attrs}
357
+ {...triggerAttrs({
358
+ controls: popover?.contentId,
359
+ expanded: Boolean(popover?.open),
360
+ haspopup: 'dialog',
361
+ id,
362
+ open: Boolean(popover?.open),
363
+ ref,
364
+ setTrigger: popover?.setTrigger,
365
+ triggerId: popover?.triggerId,
366
+ })}
367
+ data-slot={slot}
368
+ disabled={disabledFlag}
369
+ set:onclick={(event: Event) => {
370
+ callHandler(onClick, event)
371
+ if (event.defaultPrevented) return
372
+ popover?.setOpen(!popover.open, event)
373
+ }}
374
+ type={type}
375
+ >
376
+ {children}
377
+ </button>
378
+ )
379
+ }
380
+
381
+ /** Optional explicit anchor used to position PopoverContent independently of the trigger. */
382
+ const PopoverAnchor: Stateless<PopoverAnchorArgs> = ({
383
+ children,
384
+ class: classes,
385
+ 'data-slot': slot = 'popover-anchor',
386
+ ref,
387
+ ...attrs
388
+ }) => {
389
+ const popover = PopoverContext()
390
+ const reference = (element: HTMLDivElement | null) => {
391
+ popover?.setReference(element)
392
+ callRef(ref, element)
393
+ }
394
+
395
+ return (
396
+ <div
397
+ {...attrs}
398
+ class={classes}
399
+ data-slot={slot}
400
+ ref={reference}
401
+ >
402
+ {children}
403
+ </div>
404
+ )
405
+ }
406
+
407
+ /** Unstyled floating panel for a Popover. */
408
+ const PopoverContent: Stateless<PopoverContentArgs> = args => {
409
+ const popover = PopoverContext()
410
+ const titleId = popover ? `${popover.contentId}-title` : undefined
411
+ const descriptionId = popover?.description ? `${popover.contentId}-description` : undefined
412
+ const heading = popover ? (
413
+ <div data-slot="popover-header">
414
+ <h2 data-slot="popover-title" id={titleId}>{popover.label}</h2>
415
+ {popover.description ? (
416
+ <p data-slot="popover-description" id={descriptionId}>{popover.description}</p>
417
+ ) : null}
418
+ </div>
419
+ ) : null
420
+
421
+ if (popover?.openOn === 'hover') {
422
+ const {
423
+ arrow = false,
424
+ children,
425
+ class: classes,
426
+ 'data-slot': slot = 'popover-content',
427
+ ref,
428
+ style,
429
+ 'set:onfocusin': onFocusIn,
430
+ 'set:onfocusout': onFocusOut,
431
+ 'set:onmouseleave': onMouseLeave,
432
+ 'set:onmouseenter': onMouseEnter,
433
+ ...attrs
434
+ } = args
435
+
436
+ return (
437
+ <div
438
+ {...attrs}
439
+ {...contentAttrs({
440
+ id: popover.contentId,
441
+ open: popover.open,
442
+ ref,
443
+ setContent: popover.setContent,
444
+ style: popover.contentStyle(style),
445
+ tabindex: '-1',
446
+ })}
447
+ aria-describedby={descriptionId}
448
+ aria-labelledby={titleId}
449
+ class={classes}
450
+ data-arrow={arrow ? 'true' : undefined}
451
+ data-slot={slot}
452
+ role="dialog"
453
+ set:onfocusout={(event: FocusEvent) => {
454
+ callHandler(onFocusOut, event)
455
+ const next = event.relatedTarget as Node | null
456
+ if (next && popover.content?.contains(next)) return
457
+ popover.registerContentFocus?.(false, event)
458
+ }}
459
+ set:onfocusin={(event: FocusEvent) => {
460
+ callHandler(onFocusIn, event)
461
+ popover.registerContentFocus?.(true, event)
462
+ }}
463
+ set:onmouseleave={(event: MouseEvent) => {
464
+ callHandler(onMouseLeave, event)
465
+ popover.registerContentHover?.(false, event)
466
+ }}
467
+ set:onmouseenter={(event: MouseEvent) => {
468
+ callHandler(onMouseEnter, event)
469
+ popover.registerContentHover?.(true, event)
470
+ }}
471
+ >
472
+ <PopupSurface arrow={arrow} popup={popover} />
473
+ {heading}
474
+ {children}
475
+ </div>
476
+ )
477
+ }
478
+
479
+ const {
480
+ arrow = false,
481
+ children,
482
+ class: classes,
483
+ 'data-slot': slot = 'popover-content',
484
+ ref,
485
+ style,
486
+ ...attrs
487
+ } = args
488
+
489
+ return (
490
+ <div
491
+ {...attrs}
492
+ {...contentAttrs({
493
+ id: popover?.contentId,
494
+ open: Boolean(popover?.open),
495
+ ref,
496
+ setContent: popover?.setContent,
497
+ style: popover?.contentStyle(style) ?? popupStyle(style),
498
+ tabindex: '-1',
499
+ })}
500
+ aria-describedby={descriptionId}
501
+ aria-labelledby={titleId}
502
+ class={classes}
503
+ data-arrow={arrow ? 'true' : undefined}
504
+ data-slot={slot}
505
+ role="dialog"
506
+ >
507
+ <PopupSurface arrow={arrow} popup={popover} />
508
+ {heading}
509
+ {children}
510
+ </div>
511
+ )
512
+ }
513
+
514
+ export {
515
+ Popover,
516
+ PopoverAnchor,
517
+ PopoverContent,
518
+ PopoverTrigger,
519
+ }
@@ -0,0 +1,31 @@
1
+ import type { Stateless } from 'ajo'
2
+ import type { PopupView } from './popup'
3
+
4
+ type PopupSurfaceArgs = {
5
+ /** Whether the surface extends toward its positioning reference. */
6
+ arrow?: boolean
7
+ popup?: Pick<PopupView, 'arrowAttrs'> | null
8
+ }
9
+
10
+ const probeStyle = 'position:absolute;width:14px;height:14px;pointer-events:none;background:transparent;border:0;box-shadow:none;color:transparent;opacity:0'
11
+
12
+ /** Internal visual surface and optional transparent Floating UI arrow probe. */
13
+ const PopupSurface: Stateless<PopupSurfaceArgs> = ({ arrow = false, popup }) => {
14
+ const probe = arrow ? popup?.arrowAttrs() : undefined
15
+
16
+ return (
17
+ <>
18
+ <span aria-hidden="true" data-slot="popup-surface" />
19
+ {arrow ? (
20
+ <span
21
+ aria-hidden="true"
22
+ data-slot="popup-arrow"
23
+ ref={probe?.ref}
24
+ style={[probeStyle, probe?.style].filter(Boolean).join(';')}
25
+ />
26
+ ) : null}
27
+ </>
28
+ )
29
+ }
30
+
31
+ export { PopupSurface }