@svadmin/ui 0.38.2 → 0.38.7

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 (144) hide show
  1. package/dist/app.css +8 -4
  2. package/dist/app.theme.css +8 -4
  3. package/dist/components/AICommandBar.svelte +16 -15
  4. package/dist/components/AboutSettings.svelte +12 -11
  5. package/dist/components/AdminApp.svelte +144 -70
  6. package/dist/components/AdminApp.svelte.d.ts +6 -3
  7. package/dist/components/ApiSettings.svelte +19 -16
  8. package/dist/components/AppearanceSettings.svelte +18 -16
  9. package/dist/components/AuditLogDrawer.svelte +6 -3
  10. package/dist/components/AuditLogViewer.svelte +18 -15
  11. package/dist/components/Authenticated.svelte +4 -4
  12. package/dist/components/AutoForm.svelte +19 -16
  13. package/dist/components/AutoSaveIndicator.svelte +7 -5
  14. package/dist/components/AutoTable.svelte +185 -159
  15. package/dist/components/AutoTable.svelte.d.ts +1 -1
  16. package/dist/components/Breadcrumbs.svelte +17 -13
  17. package/dist/components/CancelTaskButton.svelte +5 -2
  18. package/dist/components/CatchAllNavigate.svelte +3 -3
  19. package/dist/components/ChatDialog.svelte +22 -19
  20. package/dist/components/ComboboxField.svelte +6 -4
  21. package/dist/components/CommandPalette.svelte +20 -20
  22. package/dist/components/ConfigErrorScreen.svelte +12 -10
  23. package/dist/components/ConfirmDialog.svelte +6 -4
  24. package/dist/components/CreatePage.svelte +4 -2
  25. package/dist/components/DevTools.svelte +11 -14
  26. package/dist/components/DrawerForm.svelte +5 -2
  27. package/dist/components/EditPage.svelte +8 -5
  28. package/dist/components/EmptyState.svelte +5 -3
  29. package/dist/components/ErrorBoundary.svelte +5 -3
  30. package/dist/components/ErrorComponent.svelte +6 -4
  31. package/dist/components/ErrorPage.svelte +12 -10
  32. package/dist/components/FieldRenderer.svelte +17 -15
  33. package/dist/components/ForgotPasswordPage.svelte +22 -19
  34. package/dist/components/Header.svelte +7 -4
  35. package/dist/components/InfiniteList.svelte +6 -3
  36. package/dist/components/IntegrationsSettings.svelte +9 -6
  37. package/dist/components/KeyboardShortcuts.svelte +4 -2
  38. package/dist/components/Layout.svelte +12 -13
  39. package/dist/components/LazyPage.svelte +47 -0
  40. package/dist/components/LazyPage.svelte.d.ts +14 -0
  41. package/dist/components/ListPage.svelte +8 -5
  42. package/dist/components/LoginPage.svelte +24 -22
  43. package/dist/components/MarkdownRenderer.svelte +89 -35
  44. package/dist/components/ModalForm.svelte +4 -2
  45. package/dist/components/NavigateToResource.svelte +3 -3
  46. package/dist/components/NotificationsSettings.svelte +26 -24
  47. package/dist/components/PasswordInput.svelte +4 -2
  48. package/dist/components/PermissionMatrix.svelte +15 -12
  49. package/dist/components/ProfilePage.svelte +23 -21
  50. package/dist/components/RegisterPage.svelte +21 -19
  51. package/dist/components/ResourceOperationsPage.svelte +3 -2
  52. package/dist/components/RetryTaskButton.svelte +5 -2
  53. package/dist/components/RolesSettings.svelte +10 -8
  54. package/dist/components/SecuritySettings.svelte +26 -23
  55. package/dist/components/SettingsPage.svelte +14 -11
  56. package/dist/components/ShowPage.svelte +9 -7
  57. package/dist/components/Sidebar.svelte +36 -28
  58. package/dist/components/SidebarItem.svelte +4 -4
  59. package/dist/components/SidebarItem.svelte.d.ts +1 -1
  60. package/dist/components/StepsForm.svelte +11 -8
  61. package/dist/components/TaskDetails.svelte +14 -11
  62. package/dist/components/TaskList.svelte +14 -11
  63. package/dist/components/TaskQueueDrawer.svelte +34 -32
  64. package/dist/components/TaskStatusBadge.svelte +5 -2
  65. package/dist/components/Toast.svelte +18 -4
  66. package/dist/components/TooltipButton.svelte +25 -3
  67. package/dist/components/TwoFactorAuthPage.svelte +26 -24
  68. package/dist/components/UndoableNotification.svelte +5 -2
  69. package/dist/components/UpdatePasswordPage.svelte +18 -15
  70. package/dist/components/VirtualTable.svelte +0 -4
  71. package/dist/components/account/CompanyProfilePage.svelte +13 -11
  72. package/dist/components/account/GetStartedPage.svelte +15 -13
  73. package/dist/components/account/ImportMembersPage.svelte +14 -12
  74. package/dist/components/account/MembersStarterPage.svelte +10 -8
  75. package/dist/components/account/SecurityLogPage.svelte +24 -22
  76. package/dist/components/account/SettingsEnterprisePage.svelte +6 -4
  77. package/dist/components/account/SettingsPlainPage.svelte +14 -12
  78. package/dist/components/account/SettingsSidebarPage.svelte +11 -9
  79. package/dist/components/account/TeamMembersPage.svelte +10 -8
  80. package/dist/components/buttons/CloneButton.svelte +4 -2
  81. package/dist/components/buttons/CreateButton.svelte +4 -2
  82. package/dist/components/buttons/DeleteButton.svelte +6 -4
  83. package/dist/components/buttons/EditButton.svelte +4 -2
  84. package/dist/components/buttons/ExportButton.svelte +4 -2
  85. package/dist/components/buttons/ImportButton.svelte +4 -2
  86. package/dist/components/buttons/RefreshButton.svelte +4 -2
  87. package/dist/components/buttons/SaveButton.svelte +4 -2
  88. package/dist/components/buttons/ShowButton.svelte +4 -2
  89. package/dist/components/fieldComponentMap.js +0 -3
  90. package/dist/components/fields/ArrayField.svelte +6 -4
  91. package/dist/components/fields/JsonField.svelte +5 -3
  92. package/dist/components/fields/RelationField.svelte +3 -3
  93. package/dist/components/fields/RichTextField.svelte +4 -2
  94. package/dist/components/network/TeamCrewTablePage.svelte +18 -16
  95. package/dist/components/network/UserCardsNFTPage.svelte +11 -9
  96. package/dist/components/profile/ActivityTimeline.svelte +10 -8
  97. package/dist/components/profile/ProfileCard.svelte +13 -11
  98. package/dist/components/profile/ProjectsGrid.svelte +7 -5
  99. package/dist/components/profile/PublicProfilePage.svelte +18 -16
  100. package/dist/components/profile/TeamsShowcase.svelte +7 -5
  101. package/dist/components/toast-host.svelte.d.ts +5 -0
  102. package/dist/components/toast-host.svelte.js +21 -0
  103. package/dist/components/ui/command/command-dialog.svelte +29 -13
  104. package/dist/components/ui/command/command-dialog.svelte.d.ts +4 -3
  105. package/dist/components/ui/command/command-empty.svelte +18 -0
  106. package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
  107. package/dist/components/ui/command/command-group.svelte +41 -0
  108. package/dist/components/ui/command/command-group.svelte.d.ts +8 -0
  109. package/dist/components/ui/command/command-input.svelte +23 -0
  110. package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
  111. package/dist/components/ui/command/command-item.svelte +43 -0
  112. package/dist/components/ui/command/command-item.svelte.d.ts +11 -0
  113. package/dist/components/ui/command/command-list.svelte +18 -0
  114. package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
  115. package/dist/components/ui/command/command-loading.svelte +12 -0
  116. package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
  117. package/dist/components/ui/command/command-separator.svelte +23 -0
  118. package/dist/components/ui/command/command-separator.svelte.d.ts +7 -0
  119. package/dist/components/ui/command/command.svelte +26 -0
  120. package/dist/components/ui/command/command.svelte.d.ts +4 -0
  121. package/dist/components/ui/command/index.d.ts +55 -2
  122. package/dist/components/ui/command/index.js +21 -2
  123. package/dist/components/ui/sheet/sheet-content.svelte +1 -4
  124. package/dist/components/ui/sheet/sheet-content.svelte.d.ts +1 -3
  125. package/dist/components/ui/tabs/tabs-content.svelte +6 -2
  126. package/dist/components/ui/tabs/tabs-context.svelte.d.ts +5 -0
  127. package/dist/components/ui/tabs/tabs-context.svelte.js +2 -0
  128. package/dist/components/ui/tabs/tabs-trigger.svelte +49 -4
  129. package/dist/components/ui/tabs/tabs.svelte +12 -1
  130. package/dist/components/ui/tooltip/index.d.ts +4 -1
  131. package/dist/components/ui/tooltip/index.js +3 -1
  132. package/dist/components/ui/tooltip/tooltip-content.svelte +308 -35
  133. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +42 -3
  134. package/dist/components/ui/tooltip/tooltip-context.svelte.d.ts +37 -0
  135. package/dist/components/ui/tooltip/tooltip-context.svelte.js +220 -0
  136. package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.d.ts +11 -0
  137. package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.js +93 -0
  138. package/dist/components/ui/tooltip/tooltip-trigger.svelte +56 -3
  139. package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +23 -2
  140. package/dist/components/ui/tooltip/tooltip.svelte +63 -3
  141. package/dist/components/ui/tooltip/tooltip.svelte.d.ts +23 -2
  142. package/dist/router-state.svelte.d.ts +13 -0
  143. package/dist/router-state.svelte.js +72 -37
  144. package/package.json +14 -11
@@ -2,13 +2,10 @@
2
2
  import { cn, type WithElementRef } from "../../../utils.js";
3
3
  import type { HTMLAttributes } from "svelte/elements";
4
4
 
5
- type Props = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
6
- side?: "left" | "right";
7
- };
5
+ type Props = WithElementRef<HTMLAttributes<HTMLDivElement>>;
8
6
 
9
7
  let {
10
8
  ref = $bindable(null),
11
- side = "right",
12
9
  class: className,
13
10
  children,
14
11
  ...restProps
@@ -1,8 +1,6 @@
1
1
  import { type WithElementRef } from "../../../utils.js";
2
2
  import type { HTMLAttributes } from "svelte/elements";
3
- type Props = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
4
- side?: "left" | "right";
5
- };
3
+ type Props = WithElementRef<HTMLAttributes<HTMLDivElement>>;
6
4
  declare const SheetContent: import("svelte").Component<Props, {}, "ref">;
7
5
  type SheetContent = ReturnType<typeof SheetContent>;
8
6
  export default SheetContent;
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { cn, type WithElementRef } from "../../../utils.js";
3
3
  import type { HTMLAttributes } from "svelte/elements";
4
+ import { getTabsContext } from "./tabs-context.svelte.js";
4
5
 
5
6
  type Props = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
6
7
  value: string;
@@ -10,14 +11,17 @@
10
11
  let {
11
12
  ref = $bindable(null),
12
13
  value,
13
- active = false,
14
+ active,
14
15
  class: className,
15
16
  children,
16
17
  ...restProps
17
18
  }: Props = $props();
19
+
20
+ const tabs = getTabsContext();
21
+ const isActive = $derived(active ?? tabs.value === value);
18
22
  </script>
19
23
 
20
- {#if active}
24
+ {#if isActive}
21
25
  <div
22
26
  bind:this={ref}
23
27
  data-slot="tabs-content"
@@ -0,0 +1,5 @@
1
+ export interface TabsContextValue {
2
+ readonly value: string;
3
+ select: (value: string) => void;
4
+ }
5
+ export declare const getTabsContext: () => TabsContextValue, setTabsContext: (context: TabsContextValue) => TabsContextValue;
@@ -0,0 +1,2 @@
1
+ import { createContext } from 'svelte';
2
+ export const [getTabsContext, setTabsContext] = createContext();
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { cn, type WithElementRef } from "../../../utils.js";
3
3
  import type { HTMLButtonAttributes } from "svelte/elements";
4
+ import { getTabsContext } from "./tabs-context.svelte.js";
4
5
 
5
6
  type Props = WithElementRef<HTMLButtonAttributes, HTMLButtonElement> & {
6
7
  value: string;
@@ -10,11 +11,52 @@
10
11
  let {
11
12
  ref = $bindable(null),
12
13
  value,
13
- active = false,
14
+ active,
15
+ onclick,
16
+ onkeydown,
17
+ tabindex,
14
18
  class: className,
15
19
  children,
16
20
  ...restProps
17
21
  }: Props = $props();
22
+
23
+ const tabs = getTabsContext();
24
+ const isActive = $derived(active ?? tabs.value === value);
25
+ type ButtonMouseEvent = MouseEvent & { currentTarget: EventTarget & HTMLButtonElement };
26
+ type ButtonKeyboardEvent = KeyboardEvent & { currentTarget: EventTarget & HTMLButtonElement };
27
+
28
+ function handleClick(event: ButtonMouseEvent) {
29
+ onclick?.(event);
30
+ if (!event.defaultPrevented) tabs.select(value);
31
+ }
32
+
33
+ function handleKeydown(event: ButtonKeyboardEvent) {
34
+ onkeydown?.(event);
35
+ if (event.defaultPrevented) return;
36
+
37
+ const keys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"];
38
+ if (!keys.includes(event.key)) return;
39
+
40
+ const tabList = event.currentTarget.closest('[role="tablist"]');
41
+ if (!tabList) return;
42
+ const triggers: HTMLButtonElement[] = Array.from(
43
+ tabList.querySelectorAll<HTMLButtonElement>('[role="tab"]:not(:disabled)')
44
+ );
45
+ const currentIndex = triggers.indexOf(event.currentTarget);
46
+ if (currentIndex === -1 || triggers.length === 0) return;
47
+
48
+ const nextIndex = event.key === "Home"
49
+ ? 0
50
+ : event.key === "End"
51
+ ? triggers.length - 1
52
+ : event.key === "ArrowRight" || event.key === "ArrowDown"
53
+ ? (currentIndex + 1) % triggers.length
54
+ : (currentIndex - 1 + triggers.length) % triggers.length;
55
+
56
+ event.preventDefault();
57
+ triggers[nextIndex]?.focus();
58
+ triggers[nextIndex]?.click();
59
+ }
18
60
  </script>
19
61
 
20
62
  <button
@@ -22,16 +64,19 @@
22
64
  type="button"
23
65
  role="tab"
24
66
  data-slot="tabs-trigger"
25
- data-state={active ? "active" : "inactive"}
26
- aria-selected={active}
67
+ data-state={isActive ? "active" : "inactive"}
68
+ aria-selected={isActive}
69
+ tabindex={tabindex ?? (isActive ? 0 : -1)}
27
70
  class={cn(
28
71
  "inline-flex items-center justify-center gap-1.5 whitespace-nowrap rounded-md px-2.5 py-1 text-sm font-medium transition-all outline-none focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0",
29
- active
72
+ isActive
30
73
  ? "bg-background text-foreground shadow-sm"
31
74
  : "text-muted-foreground hover:text-foreground",
32
75
  className
33
76
  )}
34
77
  {...restProps}
78
+ onclick={handleClick}
79
+ onkeydown={handleKeydown}
35
80
  >
36
81
  {@render children?.()}
37
82
  </button>
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
- /* eslint-disable no-useless-assignment */
3
2
  import { cn, type WithElementRef } from "../../../utils.js";
4
3
  import type { HTMLAttributes } from "svelte/elements";
4
+ import { setTabsContext } from "./tabs-context.svelte.js";
5
5
 
6
6
  type Props = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
7
7
  value?: string;
@@ -16,6 +16,17 @@
16
16
  children,
17
17
  ...restProps
18
18
  }: Props = $props();
19
+
20
+ setTabsContext({
21
+ get value() {
22
+ return value;
23
+ },
24
+ select(nextValue) {
25
+ if (nextValue === value) return;
26
+ value = nextValue;
27
+ onValueChange?.(nextValue);
28
+ },
29
+ });
19
30
  </script>
20
31
 
21
32
  <div
@@ -3,4 +3,7 @@ import Trigger from "./tooltip-trigger.svelte";
3
3
  import Content from "./tooltip-content.svelte";
4
4
  import Provider from "./tooltip-provider.svelte";
5
5
  import Portal from "./tooltip-portal.svelte";
6
- export { Root, Trigger, Content, Provider, Portal, Root as Tooltip, Content as TooltipContent, Trigger as TooltipTrigger, Provider as TooltipProvider, Portal as TooltipPortal, };
6
+ import { Tooltip as TooltipPrimitive } from "bits-ui";
7
+ declare const createTether: typeof TooltipPrimitive.createTether;
8
+ type Tether<Payload = never> = TooltipPrimitive.Tether<Payload>;
9
+ export { Root, Trigger, Content, Provider, Portal, createTether, type Tether, Root as Tooltip, Content as TooltipContent, Trigger as TooltipTrigger, Provider as TooltipProvider, Portal as TooltipPortal, };
@@ -3,6 +3,8 @@ import Trigger from "./tooltip-trigger.svelte";
3
3
  import Content from "./tooltip-content.svelte";
4
4
  import Provider from "./tooltip-provider.svelte";
5
5
  import Portal from "./tooltip-portal.svelte";
6
- export { Root, Trigger, Content, Provider, Portal,
6
+ import { Tooltip as TooltipPrimitive } from "bits-ui";
7
+ const createTether = TooltipPrimitive.createTether;
8
+ export { Root, Trigger, Content, Provider, Portal, createTether,
7
9
  //
8
10
  Root as Tooltip, Content as TooltipContent, Trigger as TooltipTrigger, Provider as TooltipProvider, Portal as TooltipPortal, };
@@ -1,52 +1,325 @@
1
1
  <script lang="ts">
2
+ import {
3
+ arrow,
4
+ autoUpdate,
5
+ computePosition,
6
+ flip,
7
+ hide,
8
+ limitShift,
9
+ offset,
10
+ shift,
11
+ type Boundary,
12
+ type Padding,
13
+ type Placement,
14
+ type ReferenceElement,
15
+ type Strategy,
16
+ } from "@floating-ui/dom";
2
17
  import { Tooltip as TooltipPrimitive } from "bits-ui";
18
+ import { onDestroy, type ComponentProps, type Snippet } from "svelte";
19
+ import { createAttachmentKey } from "svelte/attachments";
20
+ import type { HTMLAttributes } from "svelte/elements";
3
21
  import { cn } from "../../../utils.js";
22
+ import { getTooltipLayerContext } from "./tooltip-context.svelte.js";
23
+ import {
24
+ registerTooltipLayer,
25
+ type TooltipLayerBehavior,
26
+ } from "./tooltip-layer-registry.svelte.js";
4
27
  import TooltipPortal from "./tooltip-portal.svelte";
5
- import type { ComponentProps } from "svelte";
6
28
  import type { WithoutChildrenOrChild } from "../../../utils.js";
7
29
 
30
+ type Side = "top" | "right" | "bottom" | "left";
31
+ type Align = "start" | "center" | "end";
32
+ type OutsideBehavior = TooltipLayerBehavior;
33
+ type EscapeBehavior = TooltipLayerBehavior;
34
+ type ContentPointerHandler = NonNullable<HTMLAttributes<HTMLDivElement>["onpointerenter"]>;
35
+ type ContentPointerEvent = Parameters<ContentPointerHandler>[0];
36
+ type TooltipContentChildProps = {
37
+ props: Record<PropertyKey, unknown>;
38
+ wrapperProps: Record<string, unknown>;
39
+ open: boolean;
40
+ };
41
+ type TooltipContentProps = Omit<
42
+ HTMLAttributes<HTMLDivElement>,
43
+ "children" | "class" | "id" | "style"
44
+ > & {
45
+ ref?: HTMLDivElement | null;
46
+ class?: string;
47
+ id?: string;
48
+ style?: TooltipPrimitive.ContentProps["style"];
49
+ dir?: TooltipPrimitive.ContentProps["dir"];
50
+ side?: Side;
51
+ sideOffset?: number;
52
+ align?: Align;
53
+ alignOffset?: number;
54
+ arrowPadding?: Padding;
55
+ avoidCollisions?: boolean;
56
+ collisionBoundary?: Boundary;
57
+ collisionPadding?: Padding;
58
+ customAnchor?: ReferenceElement | string | null;
59
+ sticky?: "partial" | "always";
60
+ strategy?: Strategy;
61
+ updatePositionStrategy?: "optimized" | "always";
62
+ hideWhenDetached?: boolean;
63
+ forceMount?: boolean;
64
+ onPlaced?: () => void;
65
+ onInteractOutside?: (event: PointerEvent) => void;
66
+ onFocusOutside?: (event: FocusEvent) => void;
67
+ interactOutsideBehavior?: OutsideBehavior;
68
+ onEscapeKeydown?: (event: KeyboardEvent) => void;
69
+ escapeKeydownBehavior?: EscapeBehavior;
70
+ children?: Snippet;
71
+ child?: Snippet<[TooltipContentChildProps]>;
72
+ arrowClasses?: string;
73
+ portalProps?: WithoutChildrenOrChild<ComponentProps<typeof TooltipPortal>>;
74
+ };
75
+
8
76
  let {
9
77
  ref = $bindable(null),
10
78
  class: className,
11
- sideOffset = 0,
79
+ id,
80
+ style,
81
+ dir,
12
82
  side = "top",
83
+ sideOffset = 0,
84
+ align = "center",
85
+ alignOffset = 0,
86
+ arrowPadding = 0,
87
+ avoidCollisions = true,
88
+ collisionBoundary,
89
+ collisionPadding = 0,
90
+ customAnchor,
91
+ sticky = "partial",
92
+ strategy = "fixed",
93
+ updatePositionStrategy = "optimized",
94
+ hideWhenDetached = false,
95
+ forceMount = false,
96
+ onPlaced,
97
+ onInteractOutside,
98
+ onFocusOutside,
99
+ interactOutsideBehavior = "close",
100
+ onEscapeKeydown,
101
+ escapeKeydownBehavior = "close",
102
+ onpointerenter,
103
+ onpointerleave,
13
104
  children,
105
+ child,
14
106
  arrowClasses,
15
107
  portalProps,
16
- ...restProps
17
- }: TooltipPrimitive.ContentProps & {
18
- arrowClasses?: string;
19
- portalProps?: WithoutChildrenOrChild<ComponentProps<typeof TooltipPortal>>;
20
- } = $props();
21
- </script>
108
+ ...domProps
109
+ }: TooltipContentProps = $props();
110
+
111
+ const tooltip = getTooltipLayerContext();
112
+ const unregisterContentId = tooltip.registerContentId(() => id ?? tooltip.defaultContentId);
113
+ let x = $state(-10_000);
114
+ let y = $state(-10_000);
115
+ let resolvedSide = $state<Side>("top");
116
+ let arrowX = $state<number | undefined>();
117
+ let arrowY = $state<number | undefined>();
118
+ let arrowStaticSide = $state<Side>("bottom");
119
+ let referenceHidden = $state(false);
120
+ let positioned = $state(false);
121
+ let arrowRef: HTMLDivElement | null = $state(null);
122
+ let destroyed = false;
123
+ let positionGeneration = 0;
124
+ const contentAttachmentKey = createAttachmentKey();
22
125
 
23
- <TooltipPortal {...portalProps}>
24
- <TooltipPrimitive.Content
25
- bind:ref
26
- data-slot="tooltip-content"
27
- {sideOffset}
28
- {side}
29
- class={cn(
30
- "data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-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 inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-data-[slot=kbd]:pr-1.5 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm bg-primary text-primary-foreground z-50 w-fit max-w-xs origin-(--bits-tooltip-content-transform-origin)",
126
+ const contentId = $derived(id ?? tooltip.defaultContentId);
127
+ const placement = $derived<Placement>(align === "center" ? side : `${side}-${align}`);
128
+ const arrowStyle = $derived(
129
+ [
130
+ arrowX === undefined ? undefined : `left: ${arrowX}px`,
131
+ arrowY === undefined ? undefined : `top: ${arrowY}px`,
132
+ `${arrowStaticSide}: -5px`,
133
+ ]
134
+ .filter(Boolean)
135
+ .join("; ")
136
+ );
137
+ const contentClass = $derived(
138
+ cn(
139
+ "animate-in fade-in-0 zoom-in-95 inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-data-[slot=kbd]:pr-1.5 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm bg-primary text-primary-foreground z-50 w-fit max-w-xs",
31
140
  className
32
- )}
33
- {...restProps}
34
- >
35
- {@render children?.()}
36
- <TooltipPrimitive.Arrow>
37
- {#snippet child({ props })}
141
+ )
142
+ );
143
+ function serializeStyle(value: TooltipPrimitive.ContentProps["style"]) {
144
+ if (!value) return "";
145
+ if (typeof value === "string") return value.trim().replace(/;$/, "");
146
+ return Object.entries(value)
147
+ .filter(([, propertyValue]) => propertyValue !== null && propertyValue !== undefined)
148
+ .map(([property, propertyValue]) => {
149
+ const cssProperty = property.startsWith("--")
150
+ ? property
151
+ : property.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
152
+ return `${cssProperty}: ${String(propertyValue)}`;
153
+ })
154
+ .join("; ");
155
+ }
156
+ const contentStyle = $derived(
157
+ [
158
+ serializeStyle(style),
159
+ `position: ${strategy}`,
160
+ `left: ${x}px`,
161
+ `top: ${y}px`,
162
+ `visibility: ${(tooltip.open || tooltip.presenceStatus === "ending") && positioned && !referenceHidden ? "visible" : "hidden"}`,
163
+ `pointer-events: ${tooltip.open && !tooltip.disableHoverableContent ? "auto" : "none"}`,
164
+ ]
165
+ .filter(Boolean)
166
+ .join("; ")
167
+ );
168
+
169
+ function attachContent(node: HTMLDivElement) {
170
+ ref = node;
171
+ tooltip.setContentNode(node);
172
+ return () => {
173
+ if (ref === node) ref = null;
174
+ tooltip.setContentNode(null);
175
+ };
176
+ }
177
+
178
+ const contentProps = $derived.by(
179
+ () =>
180
+ ({
181
+ ...domProps,
182
+ [contentAttachmentKey]: attachContent,
183
+ id: contentId,
184
+ role: "tooltip",
185
+ "data-slot": "tooltip-content",
186
+ "data-state": tooltip.open ? "instant-open" : "closed",
187
+ "data-starting-style": tooltip.presenceStatus === "starting" ? "" : undefined,
188
+ "data-ending-style": tooltip.presenceStatus === "ending" ? "" : undefined,
189
+ "data-side": resolvedSide,
190
+ "aria-hidden": !tooltip.open,
191
+ dir,
192
+ style: contentStyle,
193
+ class: contentClass,
194
+ onpointerenter: handlePointerEnter,
195
+ onpointerleave: handlePointerLeave,
196
+ }) satisfies Record<PropertyKey, unknown>
197
+ );
198
+ const wrapperProps = $derived({ style: "display: contents" });
199
+
200
+ function getAnchor(): ReferenceElement | null {
201
+ if (typeof customAnchor === "string") return document.querySelector(customAnchor);
202
+ return customAnchor ?? tooltip.trigger;
203
+ }
204
+
205
+ function handlePointerEnter(event: ContentPointerEvent) {
206
+ tooltip.cancelScheduledClose();
207
+ onpointerenter?.(event);
208
+ }
209
+
210
+ function handlePointerLeave(event: ContentPointerEvent) {
211
+ onpointerleave?.(event);
212
+ if (!event.defaultPrevented) tooltip.scheduleClose();
213
+ }
214
+
215
+ $effect(() => {
216
+ const anchor = getAnchor();
217
+ const floating = ref;
218
+ const arrowElement = arrowRef;
219
+ if (!tooltip.open || !anchor || !floating || destroyed) return;
220
+
221
+ let active = true;
222
+ positioned = false;
223
+ const update = async () => {
224
+ const generation = ++positionGeneration;
225
+ const result = await computePosition(anchor, floating, {
226
+ placement,
227
+ strategy,
228
+ middleware: [
229
+ offset({ mainAxis: sideOffset, alignmentAxis: alignOffset || undefined }),
230
+ avoidCollisions && flip({ boundary: collisionBoundary, padding: collisionPadding }),
231
+ avoidCollisions &&
232
+ shift({
233
+ boundary: collisionBoundary,
234
+ padding: collisionPadding,
235
+ limiter: sticky === "partial" ? limitShift() : undefined,
236
+ }),
237
+ arrowElement && arrow({ element: arrowElement, padding: arrowPadding }),
238
+ hideWhenDetached && hide({ boundary: collisionBoundary, padding: collisionPadding }),
239
+ ],
240
+ });
241
+
242
+ if (
243
+ !active ||
244
+ destroyed ||
245
+ generation !== positionGeneration ||
246
+ ref !== floating ||
247
+ !tooltip.open
248
+ ) {
249
+ return;
250
+ }
251
+
252
+ x = result.x;
253
+ y = result.y;
254
+ resolvedSide = result.placement.split("-")[0] as Side;
255
+ arrowX = result.middlewareData.arrow?.x;
256
+ arrowY = result.middlewareData.arrow?.y;
257
+ arrowStaticSide = {
258
+ top: "bottom",
259
+ right: "left",
260
+ bottom: "top",
261
+ left: "right",
262
+ }[resolvedSide] as Side;
263
+ referenceHidden = Boolean(result.middlewareData.hide?.referenceHidden);
264
+ positioned = true;
265
+ onPlaced?.();
266
+ };
267
+
268
+ const stopAutoUpdate = autoUpdate(anchor, floating, () => void update(), {
269
+ animationFrame: updatePositionStrategy === "always",
270
+ });
271
+
272
+ return () => {
273
+ active = false;
274
+ positionGeneration += 1;
275
+ stopAutoUpdate();
276
+ };
277
+ });
278
+
279
+ $effect(() => {
280
+ const floating = ref;
281
+ if (!tooltip.open || !floating || destroyed) return;
282
+
283
+ const ownerDocument = floating.ownerDocument;
284
+ const NodeConstructor = ownerDocument.defaultView?.Node;
285
+ return registerTooltipLayer(ownerDocument, {
286
+ containsTarget: (target) => {
287
+ if (!NodeConstructor || !(target instanceof NodeConstructor)) return false;
288
+ return floating.contains(target) || Boolean(tooltip.trigger?.contains(target));
289
+ },
290
+ getInteractOutsideBehavior: () => interactOutsideBehavior,
291
+ getEscapeKeydownBehavior: () => escapeKeydownBehavior,
292
+ onInteractOutside: (event) => onInteractOutside?.(event),
293
+ onFocusOutside: (event) => onFocusOutside?.(event),
294
+ onEscapeKeydown: (event) => onEscapeKeydown?.(event),
295
+ close: () => tooltip.close(),
296
+ });
297
+ });
298
+
299
+ onDestroy(() => {
300
+ destroyed = true;
301
+ positionGeneration += 1;
302
+ unregisterContentId();
303
+ ref = null;
304
+ arrowRef = null;
305
+ });
306
+ </script>
307
+
308
+ {#if tooltip.shouldRender || forceMount}
309
+ <TooltipPortal {...portalProps}>
310
+ {#if child}
311
+ {@render child({ props: contentProps, wrapperProps, open: tooltip.open })}
312
+ {:else}
313
+ <div {...contentProps}>
314
+ {@render children?.()}
38
315
  <div
39
- class={cn(
40
- "size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-primary fill-primary z-50",
41
- "data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%+2px)]",
42
- "data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%+1px)]",
43
- "data-[side=right]:translate-x-[calc(50%+2px)] data-[side=right]:translate-y-1/2",
44
- "data-[side=left]:-translate-y-[calc(50%-3px)]",
45
- arrowClasses
46
- )}
47
- {...props}
316
+ bind:this={arrowRef}
317
+ aria-hidden="true"
318
+ data-side={resolvedSide}
319
+ style={arrowStyle}
320
+ class={cn("absolute size-2.5 rotate-45 rounded-[2px] bg-primary -z-10", arrowClasses)}
48
321
  ></div>
49
- {/snippet}
50
- </TooltipPrimitive.Arrow>
51
- </TooltipPrimitive.Content>
52
- </TooltipPortal>
322
+ </div>
323
+ {/if}
324
+ </TooltipPortal>
325
+ {/if}
@@ -1,11 +1,50 @@
1
+ import { type Boundary, type Padding, type ReferenceElement, type Strategy } from "@floating-ui/dom";
1
2
  import { Tooltip as TooltipPrimitive } from "bits-ui";
3
+ import { type ComponentProps, type Snippet } from "svelte";
4
+ import type { HTMLAttributes } from "svelte/elements";
5
+ import { type TooltipLayerBehavior } from "./tooltip-layer-registry.svelte.js";
2
6
  import TooltipPortal from "./tooltip-portal.svelte";
3
- import type { ComponentProps } from "svelte";
4
7
  import type { WithoutChildrenOrChild } from "../../../utils.js";
5
- type $$ComponentProps = TooltipPrimitive.ContentProps & {
8
+ type Side = "top" | "right" | "bottom" | "left";
9
+ type Align = "start" | "center" | "end";
10
+ type OutsideBehavior = TooltipLayerBehavior;
11
+ type EscapeBehavior = TooltipLayerBehavior;
12
+ type TooltipContentChildProps = {
13
+ props: Record<PropertyKey, unknown>;
14
+ wrapperProps: Record<string, unknown>;
15
+ open: boolean;
16
+ };
17
+ type TooltipContentProps = Omit<HTMLAttributes<HTMLDivElement>, "children" | "class" | "id" | "style"> & {
18
+ ref?: HTMLDivElement | null;
19
+ class?: string;
20
+ id?: string;
21
+ style?: TooltipPrimitive.ContentProps["style"];
22
+ dir?: TooltipPrimitive.ContentProps["dir"];
23
+ side?: Side;
24
+ sideOffset?: number;
25
+ align?: Align;
26
+ alignOffset?: number;
27
+ arrowPadding?: Padding;
28
+ avoidCollisions?: boolean;
29
+ collisionBoundary?: Boundary;
30
+ collisionPadding?: Padding;
31
+ customAnchor?: ReferenceElement | string | null;
32
+ sticky?: "partial" | "always";
33
+ strategy?: Strategy;
34
+ updatePositionStrategy?: "optimized" | "always";
35
+ hideWhenDetached?: boolean;
36
+ forceMount?: boolean;
37
+ onPlaced?: () => void;
38
+ onInteractOutside?: (event: PointerEvent) => void;
39
+ onFocusOutside?: (event: FocusEvent) => void;
40
+ interactOutsideBehavior?: OutsideBehavior;
41
+ onEscapeKeydown?: (event: KeyboardEvent) => void;
42
+ escapeKeydownBehavior?: EscapeBehavior;
43
+ children?: Snippet;
44
+ child?: Snippet<[TooltipContentChildProps]>;
6
45
  arrowClasses?: string;
7
46
  portalProps?: WithoutChildrenOrChild<ComponentProps<typeof TooltipPortal>>;
8
47
  };
9
- declare const TooltipContent: import("svelte").Component<$$ComponentProps, {}, "ref">;
48
+ declare const TooltipContent: import("svelte").Component<TooltipContentProps, {}, "ref">;
10
49
  type TooltipContent = ReturnType<typeof TooltipContent>;
11
50
  export default TooltipContent;
@@ -0,0 +1,37 @@
1
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
2
+ type TriggerReader = () => HTMLElement | null;
3
+ type ContentIdReader = () => string;
4
+ type TooltipTether = TooltipPrimitive.Tether<unknown>;
5
+ type PresenceStatus = 'starting' | 'ending' | undefined;
6
+ export declare class TooltipLayerContext {
7
+ #private;
8
+ readonly defaultContentId: string;
9
+ shouldRender: boolean;
10
+ presenceStatus: PresenceStatus;
11
+ constructor(defaultContentId: string, readOpen: () => boolean, writeOpen: (open: boolean) => void, readDisableHoverableContent: () => boolean, readActiveTriggerId: () => string | null, readExternalTrigger?: (triggerId: string) => HTMLElement | null, readOnOpenChangeComplete?: () => ((open: boolean) => void) | undefined);
12
+ get open(): boolean;
13
+ get trigger(): HTMLElement | null;
14
+ get activeTriggerId(): string | null;
15
+ get contentId(): string;
16
+ get disableHoverableContent(): boolean;
17
+ close(): void;
18
+ scheduleClose(delay?: number): void;
19
+ cancelScheduledClose(): void;
20
+ destroy(): void;
21
+ setContentNode(node: HTMLElement | null): void;
22
+ transitionPresence(open: boolean): void;
23
+ registerTrigger(triggerId: string, readTrigger: TriggerReader): () => void;
24
+ isActiveTrigger(triggerId: string): boolean;
25
+ registerContentId(readContentId: ContentIdReader): () => void;
26
+ }
27
+ export declare const getTooltipLayerContext: () => TooltipLayerContext, setTooltipLayerContext: (context: TooltipLayerContext) => TooltipLayerContext;
28
+ export declare function tryGetTooltipLayerContext(): TooltipLayerContext | undefined;
29
+ export declare class TooltipTetherBridge {
30
+ #private;
31
+ layer: TooltipLayerContext | null;
32
+ registerLayer(layer: TooltipLayerContext): () => void;
33
+ registerTrigger(triggerId: string, readTrigger: TriggerReader): () => void;
34
+ getTrigger(triggerId: string): HTMLElement | null;
35
+ }
36
+ export declare function getTooltipTetherBridge(tether: TooltipTether): TooltipTetherBridge;
37
+ export {};