@spaethtech/svelte-ui 0.1.10 → 0.3.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 (208) hide show
  1. package/README.md +166 -42
  2. package/dist/components/Alert.svelte +119 -0
  3. package/dist/components/Alert.svelte.d.ts +29 -0
  4. package/dist/components/Badge/Badge.svelte +142 -69
  5. package/dist/components/Badge/Badge.svelte.d.ts +26 -6
  6. package/dist/components/Badge/index.d.ts +1 -1
  7. package/dist/components/Badge/index.js +1 -1
  8. package/dist/components/Banner.svelte +133 -0
  9. package/dist/components/Banner.svelte.d.ts +31 -0
  10. package/dist/components/Button.svelte +240 -0
  11. package/dist/components/Button.svelte.d.ts +33 -0
  12. package/dist/components/ButtonDropdown.svelte +145 -0
  13. package/dist/components/ButtonDropdown.svelte.d.ts +31 -0
  14. package/dist/components/Calendar.svelte +259 -0
  15. package/dist/components/Calendar.svelte.d.ts +27 -0
  16. package/dist/components/Card.svelte +78 -0
  17. package/dist/components/Card.svelte.d.ts +17 -0
  18. package/dist/components/CardBody.svelte +41 -0
  19. package/dist/components/CardBody.svelte.d.ts +17 -0
  20. package/dist/components/CardFooter.svelte +49 -0
  21. package/dist/components/CardFooter.svelte.d.ts +16 -0
  22. package/dist/components/CardHeader.svelte +49 -0
  23. package/dist/components/CardHeader.svelte.d.ts +16 -0
  24. package/dist/components/Checkbox.svelte +131 -0
  25. package/dist/components/Checkbox.svelte.d.ts +16 -0
  26. package/dist/components/DataTable.svelte +518 -0
  27. package/dist/components/DataTable.svelte.d.ts +74 -0
  28. package/dist/components/DatePicker.svelte +312 -0
  29. package/dist/components/DatePicker.svelte.d.ts +32 -0
  30. package/dist/components/DateTimeInput.svelte +93 -0
  31. package/dist/components/DateTimeInput.svelte.d.ts +20 -0
  32. package/dist/components/Dialog.svelte +132 -0
  33. package/dist/components/Dialog.svelte.d.ts +24 -0
  34. package/dist/components/{EmailInput/EmailInput.svelte → EmailInput.svelte} +17 -8
  35. package/dist/components/EmailInput.svelte.d.ts +21 -0
  36. package/dist/components/Input.svelte +369 -0
  37. package/dist/components/Input.svelte.d.ts +35 -0
  38. package/dist/components/{List/List.svelte → List.svelte} +194 -133
  39. package/dist/components/{List/List.svelte.d.ts → List.svelte.d.ts} +9 -1
  40. package/dist/components/Menu.svelte +117 -0
  41. package/dist/components/Menu.svelte.d.ts +20 -0
  42. package/dist/components/NotesEditor.svelte +127 -0
  43. package/dist/components/NotesEditor.svelte.d.ts +17 -0
  44. package/dist/components/{NumberInput/NumberInput.svelte → NumberInput.svelte} +146 -105
  45. package/dist/components/{NumberInput/NumberInput.svelte.d.ts → NumberInput.svelte.d.ts} +11 -4
  46. package/dist/components/PasswordInput.svelte +52 -0
  47. package/dist/components/PasswordInput.svelte.d.ts +20 -0
  48. package/dist/components/Popup.svelte +140 -0
  49. package/dist/components/Popup.svelte.d.ts +31 -0
  50. package/dist/components/Query.svelte +284 -0
  51. package/dist/components/Query.svelte.d.ts +15 -0
  52. package/dist/components/{Rating/Rating.svelte → Rating.svelte} +19 -10
  53. package/dist/components/Rating.svelte.d.ts +13 -0
  54. package/dist/components/{SearchInput/SearchInput.svelte → SearchInput.svelte} +19 -8
  55. package/dist/components/{SearchInput/SearchInput.svelte.d.ts → SearchInput.svelte.d.ts} +9 -2
  56. package/dist/components/{Select/Select.svelte → Select.svelte} +48 -76
  57. package/dist/components/{Select/Select.svelte.d.ts → Select.svelte.d.ts} +11 -1
  58. package/dist/components/SideBarMenu/SideBarMenu.svelte +724 -0
  59. package/dist/components/SideBarMenu/SideBarMenu.svelte.d.ts +109 -0
  60. package/dist/components/SideBarMenu/index.d.ts +2 -0
  61. package/dist/components/SideBarMenu/index.js +1 -0
  62. package/dist/components/TabStrip/TabStrip.svelte +384 -0
  63. package/dist/components/TabStrip/TabStrip.svelte.d.ts +50 -0
  64. package/dist/components/TabStrip/index.d.ts +3 -0
  65. package/dist/components/TabStrip/index.js +2 -0
  66. package/dist/components/{TextArea/TextArea.svelte → TextArea.svelte} +143 -112
  67. package/dist/components/TextArea.svelte.d.ts +38 -0
  68. package/dist/components/ThemeSelector.svelte +81 -0
  69. package/dist/components/ThemeSelector.svelte.d.ts +10 -0
  70. package/dist/components/TimePicker.svelte +148 -0
  71. package/dist/components/TimePicker.svelte.d.ts +28 -0
  72. package/dist/components/TimeRangeInput.svelte +203 -0
  73. package/dist/components/TimeRangeInput.svelte.d.ts +29 -0
  74. package/dist/components/TimeSpinner.svelte +202 -0
  75. package/dist/components/TimeSpinner.svelte.d.ts +26 -0
  76. package/dist/components/Toast/Toaster.svelte +51 -0
  77. package/dist/components/Toast/Toaster.svelte.d.ts +12 -0
  78. package/dist/components/Toast/toast.svelte.d.ts +29 -0
  79. package/dist/components/Toast/toast.svelte.js +27 -0
  80. package/dist/components/Toggle.svelte +93 -0
  81. package/dist/components/Toggle.svelte.d.ts +15 -0
  82. package/dist/data/dataset.d.ts +42 -0
  83. package/dist/data/dataset.js +3 -0
  84. package/dist/data/index.d.ts +3 -0
  85. package/dist/data/index.js +3 -0
  86. package/dist/data/query/ast.d.ts +62 -0
  87. package/dist/data/query/ast.js +12 -0
  88. package/dist/data/query/index.d.ts +3 -0
  89. package/dist/data/query/index.js +3 -0
  90. package/dist/data/query/lexer.d.ts +33 -0
  91. package/dist/data/query/lexer.js +225 -0
  92. package/dist/data/query/parser.d.ts +11 -0
  93. package/dist/data/query/parser.js +252 -0
  94. package/dist/data/table/grid.svelte.d.ts +57 -0
  95. package/dist/data/table/grid.svelte.js +139 -0
  96. package/dist/data/table/index.d.ts +2 -0
  97. package/dist/data/table/index.js +2 -0
  98. package/dist/data/table/types.d.ts +79 -0
  99. package/dist/index.d.ts +48 -22
  100. package/dist/index.js +45 -21
  101. package/dist/positioning/anchored.d.ts +61 -0
  102. package/dist/positioning/anchored.js +122 -0
  103. package/dist/positioning/tooltip.d.ts +41 -0
  104. package/dist/positioning/tooltip.js +147 -0
  105. package/dist/theme.css +205 -0
  106. package/dist/types/breakpoints.d.ts +24 -0
  107. package/dist/types/breakpoints.js +13 -0
  108. package/dist/types/responsive.d.ts +32 -0
  109. package/dist/types/responsive.js +54 -0
  110. package/dist/types/sizes.d.ts +10 -3
  111. package/dist/types/time.d.ts +19 -0
  112. package/dist/types/time.js +10 -0
  113. package/dist/types/variants.d.ts +8 -1
  114. package/dist/types/variants.js +16 -1
  115. package/package.json +93 -63
  116. package/dist/components/Button/Button.svelte +0 -172
  117. package/dist/components/Button/Button.svelte.d.ts +0 -32
  118. package/dist/components/Button/index.d.ts +0 -1
  119. package/dist/components/Button/index.js +0 -1
  120. package/dist/components/Dialog/Dialog.svelte +0 -101
  121. package/dist/components/Dialog/Dialog.svelte.d.ts +0 -18
  122. package/dist/components/Dialog/index.d.ts +0 -1
  123. package/dist/components/Dialog/index.js +0 -1
  124. package/dist/components/ElementManager/ElementManager.svelte +0 -397
  125. package/dist/components/ElementManager/ElementManager.svelte.d.ts +0 -43
  126. package/dist/components/ElementManager/index.d.ts +0 -1
  127. package/dist/components/ElementManager/index.js +0 -1
  128. package/dist/components/EmailInput/EmailInput.svelte.d.ts +0 -14
  129. package/dist/components/EmailInput/index.d.ts +0 -1
  130. package/dist/components/EmailInput/index.js +0 -1
  131. package/dist/components/Icon/Icon.svelte +0 -74
  132. package/dist/components/Icon/Icon.svelte.d.ts +0 -13
  133. package/dist/components/Icon/index.d.ts +0 -1
  134. package/dist/components/Icon/index.js +0 -1
  135. package/dist/components/Input/Input.svelte +0 -268
  136. package/dist/components/Input/Input.svelte.d.ts +0 -18
  137. package/dist/components/Input/index.d.ts +0 -1
  138. package/dist/components/Input/index.js +0 -1
  139. package/dist/components/List/index.d.ts +0 -1
  140. package/dist/components/List/index.js +0 -1
  141. package/dist/components/ListItem/ListItem.svelte +0 -175
  142. package/dist/components/ListItem/ListItem.svelte.d.ts +0 -24
  143. package/dist/components/ListItem/index.d.ts +0 -2
  144. package/dist/components/ListItem/index.js +0 -2
  145. package/dist/components/ListView/ListView.svelte +0 -463
  146. package/dist/components/ListView/ListView.svelte.d.ts +0 -37
  147. package/dist/components/ListView/index.d.ts +0 -2
  148. package/dist/components/ListView/index.js +0 -2
  149. package/dist/components/NodeGraph/BaseNode.d.ts +0 -57
  150. package/dist/components/NodeGraph/BaseNode.js +0 -30
  151. package/dist/components/NodeGraph/Edge.svelte +0 -60
  152. package/dist/components/NodeGraph/Edge.svelte.d.ts +0 -16
  153. package/dist/components/NodeGraph/ExpressionEvaluator.d.ts +0 -82
  154. package/dist/components/NodeGraph/ExpressionEvaluator.js +0 -152
  155. package/dist/components/NodeGraph/Node.svelte +0 -100
  156. package/dist/components/NodeGraph/Node.svelte.d.ts +0 -10
  157. package/dist/components/NodeGraph/NodeGraph.svelte +0 -52
  158. package/dist/components/NodeGraph/NodeGraph.svelte.d.ts +0 -14
  159. package/dist/components/NodeGraph/NodeInstance.svelte.d.ts +0 -80
  160. package/dist/components/NodeGraph/NodeInstance.svelte.js +0 -241
  161. package/dist/components/NodeGraph/NodePort.svelte +0 -75
  162. package/dist/components/NodeGraph/NodePort.svelte.d.ts +0 -11
  163. package/dist/components/NodeGraph/decorators.d.ts +0 -81
  164. package/dist/components/NodeGraph/decorators.js +0 -148
  165. package/dist/components/NodeGraph/index.d.ts +0 -9
  166. package/dist/components/NodeGraph/index.js +0 -9
  167. package/dist/components/NodeGraph/types.d.ts +0 -94
  168. package/dist/components/NodeGraph/types.js +0 -33
  169. package/dist/components/NotesEditor/NotesEditor.svelte +0 -203
  170. package/dist/components/NotesEditor/NotesEditor.svelte.d.ts +0 -9
  171. package/dist/components/NotesEditor/index.d.ts +0 -1
  172. package/dist/components/NotesEditor/index.js +0 -1
  173. package/dist/components/NumberInput/index.d.ts +0 -1
  174. package/dist/components/NumberInput/index.js +0 -1
  175. package/dist/components/PasswordInput/PasswordInput.svelte +0 -41
  176. package/dist/components/PasswordInput/PasswordInput.svelte.d.ts +0 -13
  177. package/dist/components/PasswordInput/index.d.ts +0 -1
  178. package/dist/components/PasswordInput/index.js +0 -1
  179. package/dist/components/Popup/index.d.ts +0 -1
  180. package/dist/components/Popup/index.js +0 -1
  181. package/dist/components/Rating/Rating.svelte.d.ts +0 -7
  182. package/dist/components/Rating/index.d.ts +0 -1
  183. package/dist/components/Rating/index.js +0 -1
  184. package/dist/components/ScrollView/ScrollView.svelte +0 -285
  185. package/dist/components/ScrollView/ScrollView.svelte.d.ts +0 -19
  186. package/dist/components/ScrollView/index.d.ts +0 -1
  187. package/dist/components/ScrollView/index.js +0 -1
  188. package/dist/components/SearchInput/index.d.ts +0 -1
  189. package/dist/components/SearchInput/index.js +0 -1
  190. package/dist/components/Select/index.d.ts +0 -1
  191. package/dist/components/Select/index.js +0 -1
  192. package/dist/components/TextArea/TextArea.svelte.d.ts +0 -19
  193. package/dist/components/TextArea/index.d.ts +0 -1
  194. package/dist/components/TextArea/index.js +0 -1
  195. package/dist/components/ThemeSelector/ThemeSelector.svelte +0 -64
  196. package/dist/components/ThemeSelector/ThemeSelector.svelte.d.ts +0 -3
  197. package/dist/components/ThemeSelector/index.d.ts +0 -1
  198. package/dist/components/ThemeSelector/index.js +0 -1
  199. package/dist/components/TooltipHandler/TooltipHandler.svelte +0 -593
  200. package/dist/components/TooltipHandler/TooltipHandler.svelte.d.ts +0 -10
  201. package/dist/components/TooltipHandler/index.d.ts +0 -1
  202. package/dist/components/TooltipHandler/index.js +0 -1
  203. package/dist/styles/sizes.d.ts +0 -78
  204. package/dist/styles/sizes.js +0 -120
  205. package/dist/styles/variants.d.ts +0 -106
  206. package/dist/styles/variants.js +0 -194
  207. package/dist/types/ManagerTypes.d.ts +0 -42
  208. /package/dist/{types/ManagerTypes.js → data/table/types.js} +0 -0
@@ -1,64 +0,0 @@
1
- <script lang="ts">
2
- import Select from '../Select/Select.svelte';
3
-
4
- const options = [
5
- { label: 'Auto (System)', value: 'auto' },
6
- { label: 'Light', value: 'light' },
7
- { label: 'Dark', value: 'dark' }
8
- ];
9
-
10
- const themeClasses = {
11
- 'light': 'theme-light',
12
- 'dark': 'theme-dark'
13
- };
14
-
15
- let selectedTheme = $state('auto');
16
-
17
- // Load saved theme on mount
18
- $effect(() => {
19
- if (typeof window !== 'undefined') {
20
- const savedTheme = localStorage.getItem('svelte-ui-theme');
21
- if (savedTheme && options.some(opt => opt.value === savedTheme)) {
22
- selectedTheme = savedTheme;
23
- }
24
- }
25
- });
26
-
27
- // Apply theme to document body
28
- function applyTheme(themeValue: string) {
29
- const body = document.body;
30
- const html = document.documentElement;
31
-
32
- // Remove all existing theme classes from both body and html
33
- Object.values(themeClasses).forEach(className => {
34
- body.classList.remove(className);
35
- html.classList.remove(className);
36
- });
37
-
38
- // Add the selected theme class to body
39
- const themeClass = themeClasses[themeValue as keyof typeof themeClasses];
40
- if (themeClass) {
41
- body.classList.add(themeClass);
42
- }
43
-
44
- // Save theme to localStorage
45
- if (typeof window !== 'undefined') {
46
- localStorage.setItem('svelte-ui-theme', themeValue);
47
- }
48
- }
49
-
50
- // Apply theme when selection changes
51
- $effect(() => {
52
- applyTheme(selectedTheme);
53
- });
54
- </script>
55
-
56
- <div class="flex items-center gap-2">
57
- <label for="theme-selector" class="text-sm font-medium text-[var(--color-text)]">Theme:</label>
58
- <Select
59
- bind:value={selectedTheme}
60
- {options}
61
- placeholder="Select theme..."
62
- class="w-40"
63
- />
64
- </div>
@@ -1,3 +0,0 @@
1
- declare const ThemeSelector: import("svelte").Component<Record<string, never>, {}, "">;
2
- type ThemeSelector = ReturnType<typeof ThemeSelector>;
3
- export default ThemeSelector;
@@ -1 +0,0 @@
1
- export { default } from './ThemeSelector.svelte';
@@ -1 +0,0 @@
1
- export { default } from './ThemeSelector.svelte';
@@ -1,593 +0,0 @@
1
- <!--
2
- /**
3
- * TooltipHandler - Automatic tooltip handling using elements from ElementManager
4
- *
5
- * Receives elements from an external ElementManager and creates tooltips for any element
6
- * with data-tooltip attributes. Supports both text content and custom snippets.
7
- *
8
- * @example Basic Usage
9
- * ```svelte
10
- * <script>
11
- * import { ElementManager, TooltipHandler } from 'svelte-ui';
12
- * let tooltipElements = $state(new Map());
13
- * </script>
14
- *
15
- * <ElementManager
16
- * selector="[data-tooltip], [data-tooltip-text]"
17
- * bind:elements={tooltipElements}
18
- * >
19
- * <TooltipHandler elements={tooltipElements}>
20
- * <button data-tooltip-text="Hello World!">Hover me</button>
21
- * </TooltipHandler>
22
- * </ElementManager>
23
- * ```
24
- *
25
- * @example With Custom Snippets
26
- * ```svelte
27
- * <ElementManager
28
- * selector="[data-tooltip], [data-tooltip-text]"
29
- * bind:elements={tooltipElements}
30
- * >
31
- * <TooltipHandler
32
- * elements={tooltipElements}
33
- * warning={warningSnippet}
34
- * info={infoSnippet}
35
- * >
36
- * {#snippet warningSnippet()}
37
- * <div class="text-center">
38
- * <div class="font-semibold text-yellow-300">⚠️ Warning</div>
39
- * <div class="text-xs mt-1">Action cannot be undone!</div>
40
- * </div>
41
- * {/snippet}
42
- *
43
- * <button data-tooltip="warning">Delete</button>
44
- * <button data-tooltip-text="Simple text">Save</button>
45
- * </TooltipHandler>
46
- * </ElementManager>
47
- * ```
48
- */
49
- -->
50
- <script lang="ts">
51
- import { tick } from 'svelte';
52
- import type { Snippet } from 'svelte';
53
- import type { HandlerComponentProps } from '../../types/ManagerTypes.js';
54
-
55
- interface Props extends HandlerComponentProps {
56
- /** Container for tooltip positioning bounds. Can be HTMLElement or CSS selector. Default: viewport */
57
- container?: HTMLElement | string;
58
- /** Named snippets for tooltip content */
59
- [key: string]: any;
60
- }
61
-
62
- let { elements = new Map(), children, container, ...snippets }: Props = $props();
63
-
64
- // Set CSS custom property for tooltip max-width when in browser
65
- $effect(() => {
66
- if (typeof window !== 'undefined') {
67
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
68
- const maxWidth = window.innerWidth - scrollbarWidth - 16; // viewport width - margins
69
- document.documentElement.style.setProperty('--tooltip-max-width', `${maxWidth}px`);
70
- }
71
- });
72
-
73
- // Filter out non-snippet props to get only actual snippets
74
- const snippetProps = $derived(Object.fromEntries(
75
- Object.entries(snippets).filter(([_, value]) =>
76
- typeof value === 'function'
77
- )
78
- ));
79
-
80
- interface TooltipState {
81
- element: HTMLElement;
82
- text?: string;
83
- snippet?: Snippet;
84
- side: 'top' | 'left' | 'right' | 'bottom';
85
- wait: number;
86
- isVisible: boolean;
87
- tooltipElement?: HTMLDivElement;
88
- hoverTimeout?: ReturnType<typeof setTimeout>;
89
- tooltipStyle: string;
90
- arrowStyle: string;
91
- effectiveSide: 'top' | 'left' | 'right' | 'bottom';
92
- showArrow: boolean;
93
- originalTitle?: string;
94
- }
95
-
96
- let tooltipStates = $state<TooltipState[]>([]);
97
-
98
- function updatePosition(state: TooltipState) {
99
- if (!state.tooltipElement || !state.element) {
100
- return;
101
- }
102
-
103
- const targetRect = state.element.getBoundingClientRect();
104
-
105
- // Get tooltip dimensions - ensure they're fully calculated
106
- const tooltipRect = state.tooltipElement.getBoundingClientRect();
107
- const actualWidth = tooltipRect.width;
108
- const actualHeight = tooltipRect.height;
109
-
110
- // If dimensions are not yet available, wait for next frame
111
- if (actualWidth === 0 || actualHeight === 0) {
112
- requestAnimationFrame(() => updatePosition(state));
113
- return;
114
- }
115
-
116
-
117
- // Determine boundary constraints
118
- let boundaryRect: DOMRect;
119
- let viewportWidth: number;
120
- let viewportHeight: number;
121
-
122
- if (container) {
123
- // Resolve container from prop (HTMLElement or CSS selector)
124
- let containerElement: HTMLElement;
125
-
126
- if (typeof container === 'string') {
127
- // Query selector
128
- const found = document.querySelector(container);
129
- if (found instanceof HTMLElement) {
130
- containerElement = found;
131
- } else {
132
- containerElement = document.body;
133
- }
134
- } else {
135
- // Direct element reference
136
- containerElement = container;
137
- }
138
-
139
- boundaryRect = containerElement.getBoundingClientRect();
140
- viewportWidth = boundaryRect.width;
141
- viewportHeight = boundaryRect.height;
142
- } else {
143
- // Use viewport bounds (default) - account for scrollbars
144
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
145
- const scrollbarHeight = window.innerHeight - document.documentElement.clientHeight;
146
-
147
- boundaryRect = new DOMRect(0, 0,
148
- window.innerWidth - scrollbarWidth,
149
- window.innerHeight - scrollbarHeight
150
- );
151
- viewportWidth = window.innerWidth - scrollbarWidth;
152
- viewportHeight = window.innerHeight - scrollbarHeight;
153
- }
154
-
155
- const offset = 16; // 1rem offset
156
- const margin = 8; // minimum margin from boundary edges
157
-
158
- // Helper function to check if a side has enough room
159
- function hasRoomForSide(side: 'top' | 'right' | 'bottom' | 'left'): boolean {
160
- switch (side) {
161
- case 'top':
162
- return targetRect.top - actualHeight - offset >= boundaryRect.top + margin;
163
- case 'bottom':
164
- return targetRect.bottom + actualHeight + offset <= boundaryRect.bottom - margin;
165
- case 'left':
166
- return targetRect.left - actualWidth - offset >= boundaryRect.left + margin;
167
- case 'right':
168
- return targetRect.right + actualWidth + offset <= boundaryRect.right - margin;
169
- }
170
- }
171
-
172
- // Helper function to get opposite side
173
- function getOppositeSide(side: 'top' | 'right' | 'bottom' | 'left'): 'top' | 'right' | 'bottom' | 'left' {
174
- const opposites = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' } as const;
175
- return opposites[side];
176
- }
177
-
178
- // Smart positioning logic
179
- let useSide = state.side;
180
- let showArrow = true;
181
-
182
-
183
- // 1. Check if requested side has room
184
- if (!hasRoomForSide(state.side)) {
185
- // 2. Try opposite side first
186
- const oppositeSide = getOppositeSide(state.side);
187
- if (hasRoomForSide(oppositeSide)) {
188
- useSide = oppositeSide;
189
- } else {
190
- // 3. Try all sides in order: top, right, bottom, left
191
- const fallbackOrder: Array<'top' | 'right' | 'bottom' | 'left'> = ['top', 'right', 'bottom', 'left'];
192
- let foundSide = false;
193
-
194
- for (const side of fallbackOrder) {
195
- if (hasRoomForSide(side)) {
196
- useSide = side;
197
- foundSide = true;
198
- break;
199
- }
200
- }
201
-
202
- // 4. If no side fits, center the tooltip and hide arrow
203
- if (!foundSide) {
204
- useSide = 'top'; // fallback, but we'll position it centered
205
- showArrow = false;
206
- }
207
- }
208
- }
209
-
210
- state.effectiveSide = useSide;
211
-
212
- let x = 0;
213
- let y = 0;
214
-
215
- if (!showArrow) {
216
- // Center the tooltip on the target element
217
- x = targetRect.left + targetRect.width / 2 - actualWidth / 2;
218
- y = targetRect.top + targetRect.height / 2 - actualHeight / 2;
219
-
220
- // Ensure it stays within boundary bounds
221
- if (x < boundaryRect.left + margin) x = boundaryRect.left + margin;
222
- if (x + actualWidth > boundaryRect.right - margin) x = boundaryRect.right - actualWidth - margin;
223
- if (y < boundaryRect.top + margin) y = boundaryRect.top + margin;
224
- if (y + actualHeight > boundaryRect.bottom - margin) y = boundaryRect.bottom - actualHeight - margin;
225
- } else {
226
- // Position according to the chosen side
227
- switch (useSide) {
228
- case 'top':
229
- x = targetRect.left + targetRect.width / 2 - actualWidth / 2;
230
- y = targetRect.top - actualHeight - offset;
231
- break;
232
-
233
- case 'bottom':
234
- x = targetRect.left + targetRect.width / 2 - actualWidth / 2;
235
- y = targetRect.bottom + offset;
236
- break;
237
-
238
- case 'left':
239
- x = targetRect.left - actualWidth - offset;
240
- y = targetRect.top + targetRect.height / 2 - actualHeight / 2;
241
- break;
242
-
243
- case 'right':
244
- x = targetRect.right + offset;
245
- y = targetRect.top + targetRect.height / 2 - actualHeight / 2;
246
- break;
247
- }
248
-
249
- // Store original calculated position for triangle adjustment
250
- const originalX = x;
251
- const originalY = y;
252
-
253
- // Boundary collision detection with special handling for oversized tooltips
254
- const availableWidth = boundaryRect.width - (2 * margin);
255
- const availableHeight = boundaryRect.height - (2 * margin);
256
-
257
- // Handle horizontal positioning
258
- if (actualWidth > availableWidth) {
259
- // Tooltip wider than available space - center it within bounds
260
- x = boundaryRect.left + boundaryRect.width / 2 - actualWidth / 2;
261
- } else {
262
- // Normal collision detection for width
263
- if (x < boundaryRect.left + margin) {
264
- x = boundaryRect.left + margin;
265
- } else if (x + actualWidth > boundaryRect.right - margin) {
266
- x = boundaryRect.right - actualWidth - margin;
267
- }
268
- }
269
-
270
- // Handle vertical positioning
271
- if (actualHeight > availableHeight) {
272
- // Tooltip taller than available space - center it within margin bounds
273
- y = boundaryRect.top + margin + availableHeight / 2 - actualHeight / 2;
274
- } else {
275
- // Normal collision detection for height
276
- if (y < boundaryRect.top + margin) {
277
- y = boundaryRect.top + margin;
278
- } else if (y + actualHeight > boundaryRect.bottom - margin) {
279
- y = boundaryRect.bottom - actualHeight - margin;
280
- }
281
- }
282
-
283
- // Calculate arrow offset based on position adjustments
284
- let arrowOffsetX = 0;
285
- let arrowOffsetY = 0;
286
-
287
- if (useSide === 'top' || useSide === 'bottom') {
288
- // For top/bottom tooltips, adjust triangle horizontal position
289
- arrowOffsetX = originalX - x;
290
- } else if (useSide === 'left' || useSide === 'right') {
291
- // For left/right tooltips, adjust triangle vertical position
292
- arrowOffsetY = originalY - y;
293
- }
294
-
295
- // Update arrow position if needed
296
- if (arrowOffsetX !== 0 || arrowOffsetY !== 0) {
297
- if (useSide === 'top' || useSide === 'bottom') {
298
- state.arrowStyle = `transform: translateX(calc(-50% + ${arrowOffsetX}px));`;
299
- } else {
300
- state.arrowStyle = `transform: translateY(calc(-50% + ${arrowOffsetY}px));`;
301
- }
302
- } else {
303
- state.arrowStyle = '';
304
- }
305
- }
306
-
307
- // Apply tooltip position and arrow visibility
308
- state.tooltipStyle = `left: ${x}px; top: ${y}px;`;
309
- state.showArrow = showArrow;
310
-
311
- // Trigger reactivity
312
- tooltipStates = tooltipStates;
313
- }
314
-
315
- function handleMouseEnter(state: TooltipState) {
316
- if (state.hoverTimeout) clearTimeout(state.hoverTimeout);
317
-
318
- state.hoverTimeout = setTimeout(() => {
319
- // Force reactivity by creating a new array with new objects
320
- tooltipStates = tooltipStates.map(s => {
321
- if (s.element === state.element) {
322
- return { ...s, isVisible: true };
323
- }
324
- return s;
325
- });
326
- // Update position after making visible - use multiple timing strategies
327
- tick().then(() => {
328
- const currentState = tooltipStates.find(s => s.element === state.element);
329
- if (currentState) {
330
- // Try positioning immediately
331
- updatePosition(currentState);
332
- // Also try again after a short delay to handle timing issues
333
- setTimeout(() => {
334
- updatePosition(currentState);
335
- }, 1);
336
- }
337
- });
338
- }, state.wait);
339
- }
340
-
341
- function handleMouseLeave(state: TooltipState) {
342
- if (state.hoverTimeout) clearTimeout(state.hoverTimeout);
343
- // Force reactivity by creating a new array with new objects
344
- tooltipStates = tooltipStates.map(s => {
345
- if (s.element === state.element) {
346
- return { ...s, isVisible: false };
347
- }
348
- return s;
349
- });
350
- }
351
-
352
- function handleFocus(state: TooltipState) {
353
- tooltipStates = tooltipStates.map(s => {
354
- if (s.element === state.element) {
355
- return { ...s, isVisible: true };
356
- }
357
- return s;
358
- });
359
- tick().then(() => {
360
- const currentState = tooltipStates.find(s => s.element === state.element);
361
- if (currentState) {
362
- updatePosition(currentState);
363
- }
364
- });
365
- }
366
-
367
- function handleBlur(state: TooltipState) {
368
- tooltipStates = tooltipStates.map(s => {
369
- if (s.element === state.element) {
370
- return { ...s, isVisible: false };
371
- }
372
- return s;
373
- });
374
- }
375
-
376
- function handleScroll() {
377
- tooltipStates.forEach((state) => {
378
- if (state.isVisible && state.tooltipElement) {
379
- updatePosition(state);
380
- }
381
- });
382
- }
383
-
384
- function setupTooltip(element: HTMLElement, data: any) {
385
- // Skip if already set up
386
- if (data.tooltipState) {
387
- return;
388
- }
389
-
390
- // Determine tooltip content (text or snippet)
391
- let text: string | undefined;
392
- let snippet: Snippet | undefined;
393
-
394
- // Check for data-tooltip-text first
395
- if (element.dataset.tooltipText) {
396
- text = element.dataset.tooltipText;
397
- }
398
- // Check for data-tooltip (could be text or snippet name)
399
- else if (element.dataset.tooltip !== undefined) {
400
- const value = element.dataset.tooltip;
401
- if (!value) {
402
- // No value - use title attribute
403
- text = element.getAttribute('title') || '';
404
- } else if (snippetProps[value]) {
405
- // Value is a snippet name
406
- snippet = snippetProps[value];
407
- } else {
408
- // Value is plain text
409
- text = value;
410
- }
411
- }
412
-
413
- // Skip if no content
414
- if (!text && !snippet) {
415
- return;
416
- }
417
-
418
- const state: TooltipState = {
419
- element,
420
- text,
421
- snippet,
422
- side: (element.dataset.tooltipSide as any) || 'top',
423
- wait: element.dataset.tooltipWait ? parseInt(element.dataset.tooltipWait) : 500,
424
- isVisible: false,
425
- tooltipStyle: 'left: 0px; top: 0px;', // Start with basic positioning
426
- arrowStyle: '',
427
- effectiveSide: (element.dataset.tooltipSide as any) || 'top',
428
- showArrow: true // Will be updated during positioning
429
- };
430
-
431
- // Store and remove original title to prevent native tooltip
432
- const originalTitle = element.getAttribute('title');
433
- if (originalTitle) {
434
- state.originalTitle = originalTitle;
435
- element.removeAttribute('title');
436
- }
437
-
438
- // Create event handlers
439
- const enterHandler = () => handleMouseEnter(state);
440
- const leaveHandler = () => handleMouseLeave(state);
441
- const focusHandler = () => handleFocus(state);
442
- const blurHandler = () => handleBlur(state);
443
-
444
- element.addEventListener('mouseenter', enterHandler);
445
- element.addEventListener('mouseleave', leaveHandler);
446
- element.addEventListener('focus', focusHandler);
447
- element.addEventListener('blur', blurHandler);
448
-
449
- // Store cleanup function on the data object
450
- data.cleanup = () => {
451
- element.removeEventListener('mouseenter', enterHandler);
452
- element.removeEventListener('mouseleave', leaveHandler);
453
- element.removeEventListener('focus', focusHandler);
454
- element.removeEventListener('blur', blurHandler);
455
-
456
- if (state.hoverTimeout) clearTimeout(state.hoverTimeout);
457
- if (state.originalTitle) element.setAttribute('title', state.originalTitle);
458
-
459
- // Remove from tooltipStates array
460
- const index = tooltipStates.indexOf(state);
461
- if (index > -1) {
462
- tooltipStates.splice(index, 1);
463
- tooltipStates = tooltipStates;
464
- }
465
- };
466
-
467
- // Store state on the data object and add to array
468
- data.tooltipState = state;
469
- tooltipStates.push(state);
470
- tooltipStates = tooltipStates;
471
- }
472
-
473
- // React to changes in elements Map from ElementManager
474
- $effect(() => {
475
- // Process all elements in the current Map
476
- elements.forEach((data, element) => {
477
- setupTooltip(element, data);
478
- });
479
- });
480
-
481
- // Global scroll/resize listeners
482
- $effect(() => {
483
- function handleWindowEvents() {
484
- handleScroll();
485
- }
486
-
487
- window.addEventListener('scroll', handleWindowEvents, { passive: true });
488
- window.addEventListener('resize', handleWindowEvents, { passive: true });
489
-
490
- return () => {
491
- window.removeEventListener('scroll', handleWindowEvents);
492
- window.removeEventListener('resize', handleWindowEvents);
493
- };
494
- });
495
-
496
- // Action to attach tooltip element reference
497
- function tooltipRef(node: HTMLDivElement, state: TooltipState) {
498
- state.tooltipElement = node;
499
-
500
- // Update position immediately if tooltip is visible
501
- if (state.isVisible) {
502
- updatePosition(state);
503
- }
504
-
505
- return {
506
- destroy() {
507
- // Clean up reference
508
- }
509
- };
510
- }
511
- </script>
512
-
513
- {#if children}
514
- {@render children()}
515
- {/if}
516
-
517
- <!-- Render tooltips -->
518
- {#each tooltipStates as state (state.element)}
519
- <div
520
- use:tooltipRef={state}
521
- class="tooltip fixed z-[10000] px-3 py-2 text-sm rounded-md shadow-lg pointer-events-none transition-opacity duration-200 ease-out"
522
- style="{state.tooltipStyle}; background-color: var(--color-text); color: var(--color-background); opacity: {state.isVisible ? '1' : '0'};"
523
- role="tooltip"
524
- aria-hidden={!state.isVisible}
525
- >
526
- {#if state.snippet}
527
- {@render state.snippet()}
528
- {:else if state.text}
529
- {state.text}
530
- {/if}
531
-
532
- <!-- Triangle connector (only show if showArrow is true) -->
533
- {#if state.showArrow}
534
- <div
535
- class="tooltip-arrow absolute"
536
- class:top={state.effectiveSide === 'bottom'}
537
- class:bottom={state.effectiveSide === 'top'}
538
- class:left={state.effectiveSide === 'right'}
539
- class:right={state.effectiveSide === 'left'}
540
- style={state.arrowStyle}
541
- ></div>
542
- {/if}
543
- </div>
544
- {/each}
545
-
546
- <style>
547
- .tooltip-arrow {
548
- width: 0;
549
- height: 0;
550
- position: absolute;
551
- z-index: 1;
552
- }
553
-
554
- .tooltip-arrow.top {
555
- bottom: 100%;
556
- left: 50%;
557
- transform: translateX(-50%);
558
- border-left: 6px solid transparent;
559
- border-right: 6px solid transparent;
560
- border-bottom: 6px solid var(--color-text);
561
- }
562
-
563
- .tooltip-arrow.bottom {
564
- top: 100%;
565
- left: 50%;
566
- transform: translateX(-50%);
567
- border-left: 6px solid transparent;
568
- border-right: 6px solid transparent;
569
- border-top: 6px solid var(--color-text);
570
- }
571
-
572
- .tooltip-arrow.left {
573
- right: 100%;
574
- top: 50%;
575
- transform: translateY(-50%);
576
- border-top: 6px solid transparent;
577
- border-bottom: 6px solid transparent;
578
- border-right: 6px solid var(--color-text);
579
- }
580
-
581
- .tooltip-arrow.right {
582
- left: 100%;
583
- top: 50%;
584
- transform: translateY(-50%);
585
- border-top: 6px solid transparent;
586
- border-bottom: 6px solid transparent;
587
- border-left: 6px solid var(--color-text);
588
- }
589
-
590
- .tooltip {
591
- max-width: var(--tooltip-max-width, calc(100vw - 1rem));
592
- }
593
- </style>
@@ -1,10 +0,0 @@
1
- import type { HandlerComponentProps } from '../../types/ManagerTypes.js';
2
- interface Props extends HandlerComponentProps {
3
- /** Container for tooltip positioning bounds. Can be HTMLElement or CSS selector. Default: viewport */
4
- container?: HTMLElement | string;
5
- /** Named snippets for tooltip content */
6
- [key: string]: any;
7
- }
8
- declare const TooltipHandler: import("svelte").Component<Props, {}, "">;
9
- type TooltipHandler = ReturnType<typeof TooltipHandler>;
10
- export default TooltipHandler;
@@ -1 +0,0 @@
1
- export { default } from './TooltipHandler.svelte';
@@ -1 +0,0 @@
1
- export { default } from './TooltipHandler.svelte';