@svadmin/ui 0.38.1 → 0.38.5

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 +10 -5
  2. package/dist/app.theme.css +1374 -0
  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 +85 -71
  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 -9
  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 +18 -12
@@ -0,0 +1,220 @@
1
+ import { createContext } from 'svelte';
2
+ import { Tooltip as TooltipPrimitive } from 'bits-ui';
3
+ export class TooltipLayerContext {
4
+ defaultContentId;
5
+ #readOpen;
6
+ #writeOpen;
7
+ #readDisableHoverableContent;
8
+ #readActiveTriggerId;
9
+ #readExternalTrigger;
10
+ #readOnOpenChangeComplete;
11
+ #triggers = new Map();
12
+ #readContentId;
13
+ #contentNode = null;
14
+ #closeTimer;
15
+ #transitionFrame = null;
16
+ #transitionGeneration = 0;
17
+ #destroyed = false;
18
+ shouldRender = $state(false);
19
+ presenceStatus = $state(undefined);
20
+ constructor(defaultContentId, readOpen, writeOpen, readDisableHoverableContent, readActiveTriggerId, readExternalTrigger = () => null, readOnOpenChangeComplete = () => undefined) {
21
+ this.defaultContentId = defaultContentId;
22
+ this.#readOpen = readOpen;
23
+ this.#writeOpen = writeOpen;
24
+ this.#readDisableHoverableContent = readDisableHoverableContent;
25
+ this.#readActiveTriggerId = readActiveTriggerId;
26
+ this.#readExternalTrigger = readExternalTrigger;
27
+ this.#readOnOpenChangeComplete = readOnOpenChangeComplete;
28
+ this.#readContentId = () => defaultContentId;
29
+ this.shouldRender = readOpen();
30
+ }
31
+ get open() {
32
+ return this.#readOpen();
33
+ }
34
+ get trigger() {
35
+ const activeTriggerId = this.activeTriggerId;
36
+ if (!activeTriggerId)
37
+ return null;
38
+ return this.#triggers.get(activeTriggerId)?.() ?? this.#readExternalTrigger(activeTriggerId);
39
+ }
40
+ get activeTriggerId() {
41
+ return this.#readActiveTriggerId();
42
+ }
43
+ get contentId() {
44
+ return this.#readContentId();
45
+ }
46
+ get disableHoverableContent() {
47
+ return this.#readDisableHoverableContent();
48
+ }
49
+ close() {
50
+ if (this.#destroyed)
51
+ return;
52
+ this.cancelScheduledClose();
53
+ this.#writeOpen(false);
54
+ }
55
+ scheduleClose(delay = 80) {
56
+ if (this.#destroyed)
57
+ return;
58
+ this.cancelScheduledClose();
59
+ this.#closeTimer = setTimeout(() => {
60
+ this.#closeTimer = undefined;
61
+ if (!this.#destroyed)
62
+ this.#writeOpen(false);
63
+ }, delay);
64
+ }
65
+ cancelScheduledClose() {
66
+ if (this.#closeTimer === undefined)
67
+ return;
68
+ clearTimeout(this.#closeTimer);
69
+ this.#closeTimer = undefined;
70
+ }
71
+ destroy() {
72
+ this.#destroyed = true;
73
+ this.cancelScheduledClose();
74
+ this.#cancelTransitionFrame();
75
+ this.#transitionGeneration += 1;
76
+ this.#triggers.clear();
77
+ this.#contentNode = null;
78
+ this.#readExternalTrigger = () => null;
79
+ this.#readOnOpenChangeComplete = () => undefined;
80
+ this.#readContentId = () => this.defaultContentId;
81
+ }
82
+ setContentNode(node) {
83
+ this.#contentNode = node;
84
+ }
85
+ transitionPresence(open) {
86
+ if (this.#destroyed)
87
+ return;
88
+ const generation = ++this.#transitionGeneration;
89
+ this.#cancelTransitionFrame();
90
+ if (open)
91
+ this.shouldRender = true;
92
+ this.presenceStatus = open ? 'starting' : 'ending';
93
+ const readAnimations = () => {
94
+ this.#transitionFrame = null;
95
+ if (!this.#isCurrentTransition(generation, open))
96
+ return;
97
+ const contentNode = this.#contentNode;
98
+ if (!contentNode || typeof contentNode.getAnimations !== 'function') {
99
+ this.#completeTransition(generation, open);
100
+ return;
101
+ }
102
+ let animations;
103
+ try {
104
+ animations = contentNode.getAnimations({ subtree: true });
105
+ }
106
+ catch {
107
+ this.#completeTransition(generation, open);
108
+ return;
109
+ }
110
+ if (animations.length === 0) {
111
+ this.#completeTransition(generation, open);
112
+ return;
113
+ }
114
+ void Promise.allSettled(animations.map((animation) => animation.finished)).then(() => {
115
+ this.#completeTransition(generation, open);
116
+ });
117
+ };
118
+ const scheduleAnimationRead = () => {
119
+ if (typeof window.requestAnimationFrame === 'function') {
120
+ this.#transitionFrame = window.requestAnimationFrame(readAnimations);
121
+ }
122
+ else {
123
+ queueMicrotask(readAnimations);
124
+ }
125
+ };
126
+ const prepareAnimationRead = () => {
127
+ this.#transitionFrame = null;
128
+ if (!this.#isCurrentTransition(generation, open))
129
+ return;
130
+ if (open) {
131
+ this.presenceStatus = undefined;
132
+ scheduleAnimationRead();
133
+ return;
134
+ }
135
+ readAnimations();
136
+ };
137
+ if (typeof window.requestAnimationFrame === 'function') {
138
+ this.#transitionFrame = window.requestAnimationFrame(prepareAnimationRead);
139
+ }
140
+ else {
141
+ queueMicrotask(prepareAnimationRead);
142
+ }
143
+ }
144
+ #isCurrentTransition(generation, open) {
145
+ return !this.#destroyed && generation === this.#transitionGeneration && this.open === open;
146
+ }
147
+ #completeTransition(generation, open) {
148
+ if (!this.#isCurrentTransition(generation, open))
149
+ return;
150
+ this.presenceStatus = undefined;
151
+ if (!open)
152
+ this.shouldRender = false;
153
+ this.#readOnOpenChangeComplete()?.(open);
154
+ }
155
+ #cancelTransitionFrame() {
156
+ if (this.#transitionFrame === null)
157
+ return;
158
+ window.cancelAnimationFrame(this.#transitionFrame);
159
+ this.#transitionFrame = null;
160
+ }
161
+ registerTrigger(triggerId, readTrigger) {
162
+ this.#triggers.set(triggerId, readTrigger);
163
+ return () => {
164
+ if (this.#triggers.get(triggerId) === readTrigger) {
165
+ this.#triggers.delete(triggerId);
166
+ }
167
+ };
168
+ }
169
+ isActiveTrigger(triggerId) {
170
+ return this.open && this.activeTriggerId === triggerId;
171
+ }
172
+ registerContentId(readContentId) {
173
+ this.#readContentId = readContentId;
174
+ return () => {
175
+ if (this.#readContentId === readContentId) {
176
+ this.#readContentId = () => this.defaultContentId;
177
+ }
178
+ };
179
+ }
180
+ }
181
+ export const [getTooltipLayerContext, setTooltipLayerContext] = createContext();
182
+ export function tryGetTooltipLayerContext() {
183
+ try {
184
+ return getTooltipLayerContext();
185
+ }
186
+ catch {
187
+ return undefined;
188
+ }
189
+ }
190
+ export class TooltipTetherBridge {
191
+ layer = $state(null);
192
+ #triggers = new Map();
193
+ registerLayer(layer) {
194
+ this.layer = layer;
195
+ return () => {
196
+ if (this.layer === layer)
197
+ this.layer = null;
198
+ };
199
+ }
200
+ registerTrigger(triggerId, readTrigger) {
201
+ this.#triggers.set(triggerId, readTrigger);
202
+ return () => {
203
+ if (this.#triggers.get(triggerId) === readTrigger) {
204
+ this.#triggers.delete(triggerId);
205
+ }
206
+ };
207
+ }
208
+ getTrigger(triggerId) {
209
+ return this.#triggers.get(triggerId)?.() ?? null;
210
+ }
211
+ }
212
+ const tetherBridges = new WeakMap();
213
+ export function getTooltipTetherBridge(tether) {
214
+ let bridge = tetherBridges.get(tether);
215
+ if (!bridge) {
216
+ bridge = new TooltipTetherBridge();
217
+ tetherBridges.set(tether, bridge);
218
+ }
219
+ return bridge;
220
+ }
@@ -0,0 +1,11 @@
1
+ export type TooltipLayerBehavior = 'close' | 'defer-otherwise-close' | 'defer-otherwise-ignore' | 'ignore';
2
+ export type TooltipLayerRegistration = {
3
+ containsTarget: (target: EventTarget | null) => boolean;
4
+ getInteractOutsideBehavior: () => TooltipLayerBehavior;
5
+ getEscapeKeydownBehavior: () => TooltipLayerBehavior;
6
+ onInteractOutside: (event: PointerEvent) => void;
7
+ onFocusOutside: (event: FocusEvent) => void;
8
+ onEscapeKeydown: (event: KeyboardEvent) => void;
9
+ close: () => void;
10
+ };
11
+ export declare function registerTooltipLayer(ownerDocument: Document, layer: TooltipLayerRegistration): () => void;
@@ -0,0 +1,93 @@
1
+ class TooltipLayerRegistry {
2
+ ownerDocument;
3
+ #layers = [];
4
+ #listening = false;
5
+ constructor(ownerDocument) {
6
+ this.ownerDocument = ownerDocument;
7
+ }
8
+ register(layer) {
9
+ this.#layers = [...this.#layers, layer];
10
+ this.#ensureListeners();
11
+ return () => {
12
+ this.#layers = this.#layers.filter((candidate) => candidate !== layer);
13
+ if (this.#layers.length === 0)
14
+ this.#removeListeners();
15
+ };
16
+ }
17
+ #ensureListeners() {
18
+ if (this.#listening)
19
+ return;
20
+ this.#listening = true;
21
+ this.ownerDocument.addEventListener('pointerdown', this.#handlePointerDown, true);
22
+ this.ownerDocument.addEventListener('focusin', this.#handleFocusIn, true);
23
+ this.ownerDocument.defaultView?.addEventListener('keydown', this.#handleKeydown, true);
24
+ }
25
+ #removeListeners() {
26
+ if (!this.#listening)
27
+ return;
28
+ this.#listening = false;
29
+ this.ownerDocument.removeEventListener('pointerdown', this.#handlePointerDown, true);
30
+ this.ownerDocument.removeEventListener('focusin', this.#handleFocusIn, true);
31
+ this.ownerDocument.defaultView?.removeEventListener('keydown', this.#handleKeydown, true);
32
+ }
33
+ #handlePointerDown = (event) => {
34
+ if (this.#layers.some((candidate) => candidate.containsTarget(event.target)))
35
+ return;
36
+ const layer = this.#getResponsibleLayer((candidate) => candidate.getInteractOutsideBehavior());
37
+ if (!layer)
38
+ return;
39
+ const behavior = layer.getInteractOutsideBehavior();
40
+ if (behavior !== 'close' && behavior !== 'defer-otherwise-close')
41
+ return;
42
+ layer.onInteractOutside(event);
43
+ if (!event.defaultPrevented)
44
+ layer.close();
45
+ };
46
+ #handleFocusIn = (event) => {
47
+ for (const layer of [...this.#layers]) {
48
+ if (!layer.containsTarget(event.target))
49
+ layer.onFocusOutside(event);
50
+ }
51
+ };
52
+ #handleKeydown = (event) => {
53
+ if (event.key !== 'Escape')
54
+ return;
55
+ const layer = this.#getResponsibleLayer((candidate) => candidate.getEscapeKeydownBehavior());
56
+ if (!layer)
57
+ return;
58
+ const behavior = layer.getEscapeKeydownBehavior();
59
+ if (behavior !== 'close' && behavior !== 'defer-otherwise-close') {
60
+ event.preventDefault();
61
+ return;
62
+ }
63
+ const KeyboardEventConstructor = this.ownerDocument.defaultView?.KeyboardEvent;
64
+ const callbackEvent = KeyboardEventConstructor
65
+ ? new KeyboardEventConstructor(event.type, event)
66
+ : event;
67
+ layer.onEscapeKeydown(callbackEvent);
68
+ const callbackPrevented = callbackEvent.defaultPrevented;
69
+ event.preventDefault();
70
+ if (!callbackPrevented)
71
+ layer.close();
72
+ };
73
+ #getResponsibleLayer(readBehavior) {
74
+ for (let index = this.#layers.length - 1; index >= 0; index -= 1) {
75
+ const layer = this.#layers[index];
76
+ if (!layer)
77
+ continue;
78
+ const behavior = readBehavior(layer);
79
+ if (behavior === 'close' || behavior === 'ignore')
80
+ return layer;
81
+ }
82
+ return this.#layers[0];
83
+ }
84
+ }
85
+ const registries = new WeakMap();
86
+ export function registerTooltipLayer(ownerDocument, layer) {
87
+ let registry = registries.get(ownerDocument);
88
+ if (!registry) {
89
+ registry = new TooltipLayerRegistry(ownerDocument);
90
+ registries.set(ownerDocument, registry);
91
+ }
92
+ return registry.register(layer);
93
+ }
@@ -1,7 +1,60 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="Payload = never">
2
2
  import { Tooltip as TooltipPrimitive } from "bits-ui";
3
+ import {
4
+ getTooltipTetherBridge,
5
+ tryGetTooltipLayerContext,
6
+ } from "./tooltip-context.svelte.js";
3
7
 
4
- let { ref = $bindable(null), ...restProps }: TooltipPrimitive.TriggerProps = $props();
8
+ type PointerHandler = NonNullable<TooltipPrimitive.TriggerProps["onpointerenter"]>;
9
+ type TriggerPointerEvent = Parameters<PointerHandler>[0];
10
+
11
+ const uid = $props.id();
12
+ let {
13
+ ref = $bindable(null),
14
+ id = `tooltip-trigger-${uid}`,
15
+ "aria-describedby": ariaDescribedBy,
16
+ onpointerenter,
17
+ onpointerleave,
18
+ tether,
19
+ ...restProps
20
+ }: TooltipPrimitive.TriggerProps<Payload> = $props();
21
+
22
+ const localTooltip = tryGetTooltipLayerContext();
23
+ const tetherBridge = $derived(tether ? getTooltipTetherBridge(tether) : undefined);
24
+ const tooltip = $derived(tether ? tetherBridge?.layer : localTooltip);
25
+
26
+ $effect(() => {
27
+ const currentBridge = tetherBridge;
28
+ if (currentBridge) return currentBridge.registerTrigger(id, () => ref);
29
+ if (!localTooltip) return;
30
+ return localTooltip.registerTrigger(id, () => ref);
31
+ });
32
+
33
+ const describedBy = $derived.by(() => {
34
+ const ids = [ariaDescribedBy, tooltip?.isActiveTrigger(id) ? tooltip.contentId : undefined]
35
+ .flatMap((value) => value?.split(/\s+/) ?? [])
36
+ .filter(Boolean);
37
+ return [...new Set(ids)].join(" ") || undefined;
38
+ });
39
+
40
+ function handlePointerEnter(event: TriggerPointerEvent) {
41
+ tooltip?.cancelScheduledClose();
42
+ onpointerenter?.(event);
43
+ }
44
+
45
+ function handlePointerLeave(event: TriggerPointerEvent) {
46
+ onpointerleave?.(event);
47
+ if (!event.defaultPrevented) tooltip?.scheduleClose();
48
+ }
5
49
  </script>
6
50
 
7
- <TooltipPrimitive.Trigger bind:ref data-slot="tooltip-trigger" {...restProps} />
51
+ <TooltipPrimitive.Trigger
52
+ bind:ref
53
+ {...restProps}
54
+ {id}
55
+ {tether}
56
+ aria-describedby={describedBy}
57
+ data-slot="tooltip-trigger"
58
+ onpointerenter={handlePointerEnter}
59
+ onpointerleave={handlePointerLeave}
60
+ />
@@ -1,4 +1,25 @@
1
1
  import { Tooltip as TooltipPrimitive } from "bits-ui";
2
- declare const TooltipTrigger: import("svelte").Component<TooltipPrimitive.TriggerProps, {}, "ref">;
3
- type TooltipTrigger = ReturnType<typeof TooltipTrigger>;
2
+ declare function $$render<Payload = never>(): {
3
+ props: TooltipPrimitive.TriggerProps<Payload>;
4
+ exports: {};
5
+ bindings: "ref";
6
+ slots: {};
7
+ events: {};
8
+ };
9
+ declare class __sveltets_Render<Payload = never> {
10
+ props(): ReturnType<typeof $$render<Payload>>['props'];
11
+ events(): ReturnType<typeof $$render<Payload>>['events'];
12
+ slots(): ReturnType<typeof $$render<Payload>>['slots'];
13
+ bindings(): "ref";
14
+ exports(): {};
15
+ }
16
+ interface $$IsomorphicComponent {
17
+ new <Payload = never>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Payload>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Payload>['props']>, ReturnType<__sveltets_Render<Payload>['events']>, ReturnType<__sveltets_Render<Payload>['slots']>> & {
18
+ $$bindings?: ReturnType<__sveltets_Render<Payload>['bindings']>;
19
+ } & ReturnType<__sveltets_Render<Payload>['exports']>;
20
+ <Payload = never>(internal: unknown, props: ReturnType<__sveltets_Render<Payload>['props']> & {}): ReturnType<__sveltets_Render<Payload>['exports']>;
21
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
22
+ }
23
+ declare const TooltipTrigger: $$IsomorphicComponent;
24
+ type TooltipTrigger<Payload = never> = InstanceType<typeof TooltipTrigger<Payload>>;
4
25
  export default TooltipTrigger;
@@ -1,10 +1,70 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="Payload = never">
2
2
  import { Tooltip as TooltipPrimitive } from "bits-ui";
3
+ import { onDestroy } from "svelte";
3
4
  import TooltipProvider from "./tooltip-provider.svelte";
5
+ import {
6
+ getTooltipTetherBridge,
7
+ TooltipLayerContext,
8
+ setTooltipLayerContext,
9
+ } from "./tooltip-context.svelte.js";
4
10
 
5
- let { open = $bindable(false), ...restProps }: TooltipPrimitive.RootProps = $props();
11
+ const uid = $props.id();
12
+ let {
13
+ open = $bindable(false),
14
+ triggerId = $bindable<string | null>(null),
15
+ disableHoverableContent,
16
+ onOpenChange,
17
+ onOpenChangeComplete,
18
+ tether,
19
+ ...restProps
20
+ }: TooltipPrimitive.RootProps<Payload> = $props();
21
+ let observedInitialOpen = false;
22
+
23
+ function setOpen(value: boolean) {
24
+ if (open === value) return;
25
+ open = value;
26
+ onOpenChange?.(value);
27
+ }
28
+
29
+ const tooltipLayer = new TooltipLayerContext(
30
+ `tooltip-${uid}`,
31
+ () => open,
32
+ setOpen,
33
+ () => disableHoverableContent ?? false,
34
+ () => triggerId,
35
+ (activeTriggerId) =>
36
+ tether ? getTooltipTetherBridge(tether).getTrigger(activeTriggerId) : null,
37
+ () => onOpenChangeComplete
38
+ );
39
+ setTooltipLayerContext(tooltipLayer);
40
+
41
+ $effect(() => {
42
+ const currentTether = tether;
43
+ if (!currentTether) return;
44
+ return getTooltipTetherBridge(currentTether).registerLayer(tooltipLayer);
45
+ });
46
+
47
+ $effect(() => {
48
+ const currentOpen = open;
49
+ if (!observedInitialOpen) {
50
+ observedInitialOpen = true;
51
+ return;
52
+ }
53
+ tooltipLayer.transitionPresence(currentOpen);
54
+ });
55
+
56
+ onDestroy(() => {
57
+ tooltipLayer.destroy();
58
+ });
6
59
  </script>
7
60
 
8
61
  <TooltipProvider>
9
- <TooltipPrimitive.Root bind:open {...restProps} />
62
+ <TooltipPrimitive.Root
63
+ bind:open
64
+ bind:triggerId
65
+ {disableHoverableContent}
66
+ {onOpenChange}
67
+ {tether}
68
+ {...restProps}
69
+ />
10
70
  </TooltipProvider>
@@ -1,4 +1,25 @@
1
1
  import { Tooltip as TooltipPrimitive } from "bits-ui";
2
- declare const Tooltip: import("svelte").Component<TooltipPrimitive.RootProps, {}, "open">;
3
- type Tooltip = ReturnType<typeof Tooltip>;
2
+ declare function $$render<Payload = never>(): {
3
+ props: TooltipPrimitive.RootProps<Payload>;
4
+ exports: {};
5
+ bindings: "open" | "triggerId";
6
+ slots: {};
7
+ events: {};
8
+ };
9
+ declare class __sveltets_Render<Payload = never> {
10
+ props(): ReturnType<typeof $$render<Payload>>['props'];
11
+ events(): ReturnType<typeof $$render<Payload>>['events'];
12
+ slots(): ReturnType<typeof $$render<Payload>>['slots'];
13
+ bindings(): "open" | "triggerId";
14
+ exports(): {};
15
+ }
16
+ interface $$IsomorphicComponent {
17
+ new <Payload = never>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Payload>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Payload>['props']>, ReturnType<__sveltets_Render<Payload>['events']>, ReturnType<__sveltets_Render<Payload>['slots']>> & {
18
+ $$bindings?: ReturnType<__sveltets_Render<Payload>['bindings']>;
19
+ } & ReturnType<__sveltets_Render<Payload>['exports']>;
20
+ <Payload = never>(internal: unknown, props: ReturnType<__sveltets_Render<Payload>['props']> & {}): ReturnType<__sveltets_Render<Payload>['exports']>;
21
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
22
+ }
23
+ declare const Tooltip: $$IsomorphicComponent;
24
+ type Tooltip<Payload = never> = InstanceType<typeof Tooltip<Payload>>;
4
25
  export default Tooltip;
@@ -1,4 +1,17 @@
1
1
  import type { RouterProvider } from '@svadmin/core';
2
+ export interface RouterState {
3
+ readonly route: string;
4
+ readonly params: Record<string, string>;
5
+ readonly path: string;
6
+ readonly provider: RouterProvider | undefined;
7
+ init: (provider: RouterProvider | undefined) => void;
8
+ sync: () => void;
9
+ destroy: () => void;
10
+ }
11
+ export declare function createRouterState(): RouterState;
12
+ /** Create and provide router state scoped to the current AdminApp tree. */
13
+ export declare function provideRouterState(provider: RouterProvider | undefined): RouterState;
14
+ export declare function getRouterState(): RouterState | undefined;
2
15
  export declare function initRouter(provider?: RouterProvider): void;
3
16
  export declare function getRoute(): string;
4
17
  export declare function getPath(): string;
@@ -1,10 +1,5 @@
1
- // Shared reactive hash router state — .svelte.ts enables $state at module level
2
- import { matchRoute, setActiveRouterProvider } from '@svadmin/core/router';
3
- let _route = $state('/');
4
- let _params = $state({});
5
- let _path = $state('/');
6
- let _initialized = false;
7
- let _provider;
1
+ import { createContext } from 'svelte';
2
+ import { matchRoute } from '@svadmin/core/router';
8
3
  const ROUTES = [
9
4
  '/login',
10
5
  '/register',
@@ -56,47 +51,87 @@ const ROUTES = [
56
51
  '/:parent/:parentId/:resource/show/:id',
57
52
  '/:parent/:parentId/:resource/clone/:id',
58
53
  ];
59
- function sync() {
60
- let path;
61
- if (_provider) {
62
- const parsed = _provider.parse();
63
- path = parsed.pathname || '/';
64
- const m = matchRoute(path, ROUTES);
65
- _route = m?.route ?? '/';
66
- _path = path;
67
- _params = { ...(m?.params ?? {}), ...parsed.params };
54
+ export function createRouterState() {
55
+ let route = $state('/');
56
+ let params = $state({});
57
+ let path = $state('/');
58
+ let provider;
59
+ let listenersAttached = false;
60
+ function sync() {
61
+ let nextPath;
62
+ let providerParams = {};
63
+ if (provider) {
64
+ const parsed = provider.parse();
65
+ nextPath = parsed.pathname || '/';
66
+ providerParams = parsed.params;
67
+ }
68
+ else {
69
+ nextPath = typeof window !== 'undefined'
70
+ ? window.location.hash.replace(/^#/, '') || '/'
71
+ : '/';
72
+ }
73
+ const match = matchRoute(nextPath, ROUTES);
74
+ route = match?.route ?? '/';
75
+ path = nextPath;
76
+ params = { ...providerParams, ...(match?.params ?? {}) };
68
77
  }
69
- else {
70
- path = typeof window !== 'undefined' ? window.location.hash.replace(/^#/, '') || '/' : '/';
71
- const m = matchRoute(path, ROUTES);
72
- _route = m?.route ?? '/';
73
- _path = path;
74
- _params = m?.params ?? {};
78
+ function attachListeners() {
79
+ if (listenersAttached || typeof window === 'undefined')
80
+ return;
81
+ window.addEventListener('hashchange', sync);
82
+ window.addEventListener('popstate', sync);
83
+ listenersAttached = true;
75
84
  }
85
+ return {
86
+ get route() { return route; },
87
+ get params() { return params; },
88
+ get path() { return path; },
89
+ get provider() { return provider; },
90
+ init(nextProvider) {
91
+ provider = nextProvider;
92
+ sync();
93
+ attachListeners();
94
+ },
95
+ sync,
96
+ destroy() {
97
+ if (!listenersAttached || typeof window === 'undefined')
98
+ return;
99
+ window.removeEventListener('hashchange', sync);
100
+ window.removeEventListener('popstate', sync);
101
+ listenersAttached = false;
102
+ },
103
+ };
76
104
  }
77
- export function initRouter(provider) {
78
- _provider = provider;
79
- setActiveRouterProvider(provider);
80
- if (_initialized) {
81
- sync(); // Re-sync manually since provider might have switched format/URLs
82
- return;
105
+ const [getRequiredRouterState, setRouterState] = createContext();
106
+ /** Create and provide router state scoped to the current AdminApp tree. */
107
+ export function provideRouterState(provider) {
108
+ const state = createRouterState();
109
+ state.init(provider);
110
+ setRouterState(state);
111
+ return state;
112
+ }
113
+ export function getRouterState() {
114
+ try {
115
+ return getRequiredRouterState();
83
116
  }
84
- _initialized = true;
85
- sync();
86
- if (typeof window !== 'undefined') {
87
- window.addEventListener('hashchange', sync);
88
- window.addEventListener('popstate', sync);
117
+ catch {
118
+ return undefined;
89
119
  }
90
120
  }
121
+ // Compatibility singleton for consumers that call initRouter outside AdminApp.
122
+ const legacyRouterState = createRouterState();
123
+ export function initRouter(provider) {
124
+ legacyRouterState.init(provider);
125
+ }
91
126
  export function getRoute() {
92
- return _route;
127
+ return getRouterState()?.route ?? legacyRouterState.route;
93
128
  }
94
129
  export function getPath() {
95
- return _path;
130
+ return getRouterState()?.path ?? legacyRouterState.path;
96
131
  }
97
132
  export function getParams() {
98
- return _params;
133
+ return getRouterState()?.params ?? legacyRouterState.params;
99
134
  }
100
135
  export function getRouterProviderInstance() {
101
- return _provider;
136
+ return getRouterState()?.provider ?? legacyRouterState.provider;
102
137
  }