@fuzdev/fuz_ui 0.169.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 (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/Alert.svelte +108 -0
  4. package/dist/Alert.svelte.d.ts +16 -0
  5. package/dist/Alert.svelte.d.ts.map +1 -0
  6. package/dist/ApiDeclarationList.svelte +35 -0
  7. package/dist/ApiDeclarationList.svelte.d.ts +9 -0
  8. package/dist/ApiDeclarationList.svelte.d.ts.map +1 -0
  9. package/dist/ApiIndex.svelte +65 -0
  10. package/dist/ApiIndex.svelte.d.ts +23 -0
  11. package/dist/ApiIndex.svelte.d.ts.map +1 -0
  12. package/dist/ApiModule.svelte +124 -0
  13. package/dist/ApiModule.svelte.d.ts +22 -0
  14. package/dist/ApiModule.svelte.d.ts.map +1 -0
  15. package/dist/Breadcrumb.svelte +83 -0
  16. package/dist/Breadcrumb.svelte.d.ts +23 -0
  17. package/dist/Breadcrumb.svelte.d.ts.map +1 -0
  18. package/dist/Card.svelte +157 -0
  19. package/dist/Card.svelte.d.ts +13 -0
  20. package/dist/Card.svelte.d.ts.map +1 -0
  21. package/dist/ColorSchemeInput.svelte +65 -0
  22. package/dist/ColorSchemeInput.svelte.d.ts +11 -0
  23. package/dist/ColorSchemeInput.svelte.d.ts.map +1 -0
  24. package/dist/Contextmenu.svelte +30 -0
  25. package/dist/Contextmenu.svelte.d.ts +32 -0
  26. package/dist/Contextmenu.svelte.d.ts.map +1 -0
  27. package/dist/ContextmenuEntry.svelte +74 -0
  28. package/dist/ContextmenuEntry.svelte.d.ts +12 -0
  29. package/dist/ContextmenuEntry.svelte.d.ts.map +1 -0
  30. package/dist/ContextmenuLinkEntry.svelte +112 -0
  31. package/dist/ContextmenuLinkEntry.svelte.d.ts +12 -0
  32. package/dist/ContextmenuLinkEntry.svelte.d.ts.map +1 -0
  33. package/dist/ContextmenuRoot.svelte +372 -0
  34. package/dist/ContextmenuRoot.svelte.d.ts +71 -0
  35. package/dist/ContextmenuRoot.svelte.d.ts.map +1 -0
  36. package/dist/ContextmenuRootForSafariCompatibility.svelte +541 -0
  37. package/dist/ContextmenuRootForSafariCompatibility.svelte.d.ts +79 -0
  38. package/dist/ContextmenuRootForSafariCompatibility.svelte.d.ts.map +1 -0
  39. package/dist/ContextmenuSeparator.svelte +16 -0
  40. package/dist/ContextmenuSeparator.svelte.d.ts +4 -0
  41. package/dist/ContextmenuSeparator.svelte.d.ts.map +1 -0
  42. package/dist/ContextmenuSubmenu.svelte +116 -0
  43. package/dist/ContextmenuSubmenu.svelte.d.ts +10 -0
  44. package/dist/ContextmenuSubmenu.svelte.d.ts.map +1 -0
  45. package/dist/ContextmenuTextEntry.svelte +21 -0
  46. package/dist/ContextmenuTextEntry.svelte.d.ts +10 -0
  47. package/dist/ContextmenuTextEntry.svelte.d.ts.map +1 -0
  48. package/dist/CopyToClipboard.svelte +81 -0
  49. package/dist/CopyToClipboard.svelte.d.ts +18 -0
  50. package/dist/CopyToClipboard.svelte.d.ts.map +1 -0
  51. package/dist/DeclarationDetail.svelte +340 -0
  52. package/dist/DeclarationDetail.svelte.d.ts +8 -0
  53. package/dist/DeclarationDetail.svelte.d.ts.map +1 -0
  54. package/dist/DeclarationLink.svelte +50 -0
  55. package/dist/DeclarationLink.svelte.d.ts +8 -0
  56. package/dist/DeclarationLink.svelte.d.ts.map +1 -0
  57. package/dist/Details.svelte +51 -0
  58. package/dist/Details.svelte.d.ts +20 -0
  59. package/dist/Details.svelte.d.ts.map +1 -0
  60. package/dist/Dialog.svelte +217 -0
  61. package/dist/Dialog.svelte.d.ts +30 -0
  62. package/dist/Dialog.svelte.d.ts.map +1 -0
  63. package/dist/Dialogs.svelte +28 -0
  64. package/dist/Dialogs.svelte.d.ts +11 -0
  65. package/dist/Dialogs.svelte.d.ts.map +1 -0
  66. package/dist/Docs.svelte +179 -0
  67. package/dist/Docs.svelte.d.ts +13 -0
  68. package/dist/Docs.svelte.d.ts.map +1 -0
  69. package/dist/DocsContent.svelte +40 -0
  70. package/dist/DocsContent.svelte.d.ts +14 -0
  71. package/dist/DocsContent.svelte.d.ts.map +1 -0
  72. package/dist/DocsFooter.svelte +64 -0
  73. package/dist/DocsFooter.svelte.d.ts +15 -0
  74. package/dist/DocsFooter.svelte.d.ts.map +1 -0
  75. package/dist/DocsLink.svelte +41 -0
  76. package/dist/DocsLink.svelte.d.ts +12 -0
  77. package/dist/DocsLink.svelte.d.ts.map +1 -0
  78. package/dist/DocsList.svelte +44 -0
  79. package/dist/DocsList.svelte.d.ts +11 -0
  80. package/dist/DocsList.svelte.d.ts.map +1 -0
  81. package/dist/DocsMenu.svelte +55 -0
  82. package/dist/DocsMenu.svelte.d.ts +11 -0
  83. package/dist/DocsMenu.svelte.d.ts.map +1 -0
  84. package/dist/DocsMenuHeader.svelte +15 -0
  85. package/dist/DocsMenuHeader.svelte.d.ts +9 -0
  86. package/dist/DocsMenuHeader.svelte.d.ts.map +1 -0
  87. package/dist/DocsModulesList.svelte +32 -0
  88. package/dist/DocsModulesList.svelte.d.ts +7 -0
  89. package/dist/DocsModulesList.svelte.d.ts.map +1 -0
  90. package/dist/DocsPageLinks.svelte +61 -0
  91. package/dist/DocsPageLinks.svelte.d.ts +8 -0
  92. package/dist/DocsPageLinks.svelte.d.ts.map +1 -0
  93. package/dist/DocsPrimaryNav.svelte +93 -0
  94. package/dist/DocsPrimaryNav.svelte.d.ts +11 -0
  95. package/dist/DocsPrimaryNav.svelte.d.ts.map +1 -0
  96. package/dist/DocsSearch.svelte +48 -0
  97. package/dist/DocsSearch.svelte.d.ts +11 -0
  98. package/dist/DocsSearch.svelte.d.ts.map +1 -0
  99. package/dist/DocsSecondaryNav.svelte +63 -0
  100. package/dist/DocsSecondaryNav.svelte.d.ts +9 -0
  101. package/dist/DocsSecondaryNav.svelte.d.ts.map +1 -0
  102. package/dist/DocsTertiaryNav.svelte +118 -0
  103. package/dist/DocsTertiaryNav.svelte.d.ts +10 -0
  104. package/dist/DocsTertiaryNav.svelte.d.ts.map +1 -0
  105. package/dist/EcosystemLinks.svelte +53 -0
  106. package/dist/EcosystemLinks.svelte.d.ts +7 -0
  107. package/dist/EcosystemLinks.svelte.d.ts.map +1 -0
  108. package/dist/EcosystemLinksPanel.svelte +22 -0
  109. package/dist/EcosystemLinksPanel.svelte.d.ts +8 -0
  110. package/dist/EcosystemLinksPanel.svelte.d.ts.map +1 -0
  111. package/dist/GithubLink.svelte +75 -0
  112. package/dist/GithubLink.svelte.d.ts +14 -0
  113. package/dist/GithubLink.svelte.d.ts.map +1 -0
  114. package/dist/Glyph.svelte +28 -0
  115. package/dist/Glyph.svelte.d.ts +9 -0
  116. package/dist/Glyph.svelte.d.ts.map +1 -0
  117. package/dist/Hashlink.svelte +41 -0
  118. package/dist/Hashlink.svelte.d.ts +8 -0
  119. package/dist/Hashlink.svelte.d.ts.map +1 -0
  120. package/dist/HiddenPersonalLinks.svelte +6 -0
  121. package/dist/HiddenPersonalLinks.svelte.d.ts +27 -0
  122. package/dist/HiddenPersonalLinks.svelte.d.ts.map +1 -0
  123. package/dist/HueInput.svelte +127 -0
  124. package/dist/HueInput.svelte.d.ts +11 -0
  125. package/dist/HueInput.svelte.d.ts.map +1 -0
  126. package/dist/ImgOrSvg.svelte +58 -0
  127. package/dist/ImgOrSvg.svelte.d.ts +25 -0
  128. package/dist/ImgOrSvg.svelte.d.ts.map +1 -0
  129. package/dist/LibraryDetail.svelte +297 -0
  130. package/dist/LibraryDetail.svelte.d.ts +15 -0
  131. package/dist/LibraryDetail.svelte.d.ts.map +1 -0
  132. package/dist/LibrarySummary.svelte +151 -0
  133. package/dist/LibrarySummary.svelte.d.ts +16 -0
  134. package/dist/LibrarySummary.svelte.d.ts.map +1 -0
  135. package/dist/MdnLink.svelte +40 -0
  136. package/dist/MdnLink.svelte.d.ts +8 -0
  137. package/dist/MdnLink.svelte.d.ts.map +1 -0
  138. package/dist/Mdz.svelte +30 -0
  139. package/dist/Mdz.svelte.d.ts +10 -0
  140. package/dist/Mdz.svelte.d.ts.map +1 -0
  141. package/dist/MdzNodeView.svelte +93 -0
  142. package/dist/MdzNodeView.svelte.d.ts +9 -0
  143. package/dist/MdzNodeView.svelte.d.ts.map +1 -0
  144. package/dist/ModuleLink.svelte +48 -0
  145. package/dist/ModuleLink.svelte.d.ts +8 -0
  146. package/dist/ModuleLink.svelte.d.ts.map +1 -0
  147. package/dist/PasteFromClipboard.svelte +35 -0
  148. package/dist/PasteFromClipboard.svelte.d.ts +9 -0
  149. package/dist/PasteFromClipboard.svelte.d.ts.map +1 -0
  150. package/dist/PendingAnimation.svelte +62 -0
  151. package/dist/PendingAnimation.svelte.d.ts +13 -0
  152. package/dist/PendingAnimation.svelte.d.ts.map +1 -0
  153. package/dist/PendingButton.svelte +75 -0
  154. package/dist/PendingButton.svelte.d.ts +17 -0
  155. package/dist/PendingButton.svelte.d.ts.map +1 -0
  156. package/dist/ProjectLinks.svelte +54 -0
  157. package/dist/ProjectLinks.svelte.d.ts +19 -0
  158. package/dist/ProjectLinks.svelte.d.ts.map +1 -0
  159. package/dist/Redirect.svelte +44 -0
  160. package/dist/Redirect.svelte.d.ts +23 -0
  161. package/dist/Redirect.svelte.d.ts.map +1 -0
  162. package/dist/Spiders.svelte +57 -0
  163. package/dist/Spiders.svelte.d.ts +9 -0
  164. package/dist/Spiders.svelte.d.ts.map +1 -0
  165. package/dist/Svg.svelte +99 -0
  166. package/dist/Svg.svelte.d.ts +54 -0
  167. package/dist/Svg.svelte.d.ts.map +1 -0
  168. package/dist/Teleport.svelte +48 -0
  169. package/dist/Teleport.svelte.d.ts +15 -0
  170. package/dist/Teleport.svelte.d.ts.map +1 -0
  171. package/dist/ThemeInput.svelte +75 -0
  172. package/dist/ThemeInput.svelte.d.ts +15 -0
  173. package/dist/ThemeInput.svelte.d.ts.map +1 -0
  174. package/dist/Themed.svelte +101 -0
  175. package/dist/Themed.svelte.d.ts +24 -0
  176. package/dist/Themed.svelte.d.ts.map +1 -0
  177. package/dist/TomeContent.svelte +67 -0
  178. package/dist/TomeContent.svelte.d.ts +12 -0
  179. package/dist/TomeContent.svelte.d.ts.map +1 -0
  180. package/dist/TomeHeader.svelte +56 -0
  181. package/dist/TomeHeader.svelte.d.ts +4 -0
  182. package/dist/TomeHeader.svelte.d.ts.map +1 -0
  183. package/dist/TomeLink.svelte +29 -0
  184. package/dist/TomeLink.svelte.d.ts +10 -0
  185. package/dist/TomeLink.svelte.d.ts.map +1 -0
  186. package/dist/TomeSection.svelte +65 -0
  187. package/dist/TomeSection.svelte.d.ts +24 -0
  188. package/dist/TomeSection.svelte.d.ts.map +1 -0
  189. package/dist/TomeSectionHeader.svelte +90 -0
  190. package/dist/TomeSectionHeader.svelte.d.ts +13 -0
  191. package/dist/TomeSectionHeader.svelte.d.ts.map +1 -0
  192. package/dist/TypeLink.svelte +19 -0
  193. package/dist/TypeLink.svelte.d.ts +7 -0
  194. package/dist/TypeLink.svelte.d.ts.map +1 -0
  195. package/dist/alert.d.ts +7 -0
  196. package/dist/alert.d.ts.map +1 -0
  197. package/dist/alert.js +6 -0
  198. package/dist/api_search.svelte.d.ts +16 -0
  199. package/dist/api_search.svelte.d.ts.map +1 -0
  200. package/dist/api_search.svelte.js +61 -0
  201. package/dist/constants.d.ts +2 -0
  202. package/dist/constants.d.ts.map +1 -0
  203. package/dist/constants.js +3 -0
  204. package/dist/context_helpers.d.ts +17 -0
  205. package/dist/context_helpers.d.ts.map +1 -0
  206. package/dist/context_helpers.js +19 -0
  207. package/dist/contextmenu_helpers.d.ts +16 -0
  208. package/dist/contextmenu_helpers.d.ts.map +1 -0
  209. package/dist/contextmenu_helpers.js +39 -0
  210. package/dist/contextmenu_state.svelte.d.ts +152 -0
  211. package/dist/contextmenu_state.svelte.d.ts.map +1 -0
  212. package/dist/contextmenu_state.svelte.js +424 -0
  213. package/dist/csp.d.ts +160 -0
  214. package/dist/csp.d.ts.map +1 -0
  215. package/dist/csp.js +354 -0
  216. package/dist/csp_of_ryanatkn.d.ts +6 -0
  217. package/dist/csp_of_ryanatkn.d.ts.map +1 -0
  218. package/dist/csp_of_ryanatkn.js +14 -0
  219. package/dist/declaration.svelte.d.ts +84 -0
  220. package/dist/declaration.svelte.d.ts.map +1 -0
  221. package/dist/declaration.svelte.js +66 -0
  222. package/dist/declaration_contextmenu.d.ts +4 -0
  223. package/dist/declaration_contextmenu.d.ts.map +1 -0
  224. package/dist/declaration_contextmenu.js +14 -0
  225. package/dist/dialog.d.ts +24 -0
  226. package/dist/dialog.d.ts.map +1 -0
  227. package/dist/dialog.js +12 -0
  228. package/dist/dimensions.svelte.d.ts +5 -0
  229. package/dist/dimensions.svelte.d.ts.map +1 -0
  230. package/dist/dimensions.svelte.js +4 -0
  231. package/dist/docs_helpers.svelte.d.ts +48 -0
  232. package/dist/docs_helpers.svelte.d.ts.map +1 -0
  233. package/dist/docs_helpers.svelte.js +99 -0
  234. package/dist/helpers.d.ts +2 -0
  235. package/dist/helpers.d.ts.map +1 -0
  236. package/dist/helpers.js +16 -0
  237. package/dist/intersect.svelte.d.ts +47 -0
  238. package/dist/intersect.svelte.d.ts.map +1 -0
  239. package/dist/intersect.svelte.js +92 -0
  240. package/dist/library.svelte.d.ts +197 -0
  241. package/dist/library.svelte.d.ts.map +1 -0
  242. package/dist/library.svelte.js +130 -0
  243. package/dist/library_gen.d.ts +34 -0
  244. package/dist/library_gen.d.ts.map +1 -0
  245. package/dist/library_gen.js +123 -0
  246. package/dist/library_gen_helpers.d.ts +85 -0
  247. package/dist/library_gen_helpers.d.ts.map +1 -0
  248. package/dist/library_gen_helpers.js +188 -0
  249. package/dist/library_helpers.d.ts +54 -0
  250. package/dist/library_helpers.d.ts.map +1 -0
  251. package/dist/library_helpers.js +102 -0
  252. package/dist/logos.d.ts +134 -0
  253. package/dist/logos.d.ts.map +1 -0
  254. package/dist/logos.js +281 -0
  255. package/dist/mdz.d.ts +106 -0
  256. package/dist/mdz.d.ts.map +1 -0
  257. package/dist/mdz.js +1481 -0
  258. package/dist/mdz_components.d.ts +37 -0
  259. package/dist/mdz_components.d.ts.map +1 -0
  260. package/dist/mdz_components.js +12 -0
  261. package/dist/module.svelte.d.ts +47 -0
  262. package/dist/module.svelte.d.ts.map +1 -0
  263. package/dist/module.svelte.js +56 -0
  264. package/dist/module_contextmenu.d.ts +4 -0
  265. package/dist/module_contextmenu.d.ts.map +1 -0
  266. package/dist/module_contextmenu.js +14 -0
  267. package/dist/module_helpers.d.ts +69 -0
  268. package/dist/module_helpers.d.ts.map +1 -0
  269. package/dist/module_helpers.js +87 -0
  270. package/dist/rune_helpers.svelte.d.ts +6 -0
  271. package/dist/rune_helpers.svelte.d.ts.map +1 -0
  272. package/dist/rune_helpers.svelte.js +10 -0
  273. package/dist/storage.d.ts +13 -0
  274. package/dist/storage.d.ts.map +1 -0
  275. package/dist/storage.js +43 -0
  276. package/dist/svelte_helpers.d.ts +37 -0
  277. package/dist/svelte_helpers.d.ts.map +1 -0
  278. package/dist/svelte_helpers.js +245 -0
  279. package/dist/themer.svelte.d.ts +24 -0
  280. package/dist/themer.svelte.d.ts.map +1 -0
  281. package/dist/themer.svelte.js +43 -0
  282. package/dist/tome.d.ts +80 -0
  283. package/dist/tome.d.ts.map +1 -0
  284. package/dist/tome.js +27 -0
  285. package/dist/ts_helpers.d.ts +110 -0
  286. package/dist/ts_helpers.d.ts.map +1 -0
  287. package/dist/ts_helpers.js +533 -0
  288. package/dist/tsdoc_helpers.d.ts +98 -0
  289. package/dist/tsdoc_helpers.d.ts.map +1 -0
  290. package/dist/tsdoc_helpers.js +221 -0
  291. package/package.json +128 -0
  292. package/src/lib/alert.ts +14 -0
  293. package/src/lib/api_search.svelte.ts +85 -0
  294. package/src/lib/constants.ts +3 -0
  295. package/src/lib/context_helpers.ts +47 -0
  296. package/src/lib/contextmenu_helpers.ts +63 -0
  297. package/src/lib/contextmenu_state.svelte.ts +515 -0
  298. package/src/lib/csp.ts +576 -0
  299. package/src/lib/csp_of_ryanatkn.ts +16 -0
  300. package/src/lib/declaration.svelte.ts +102 -0
  301. package/src/lib/declaration_contextmenu.ts +22 -0
  302. package/src/lib/dialog.ts +35 -0
  303. package/src/lib/dimensions.svelte.ts +4 -0
  304. package/src/lib/docs_helpers.svelte.ts +149 -0
  305. package/src/lib/helpers.ts +10 -0
  306. package/src/lib/intersect.svelte.ts +152 -0
  307. package/src/lib/library.svelte.ts +162 -0
  308. package/src/lib/library_gen.ts +160 -0
  309. package/src/lib/library_gen_helpers.ts +262 -0
  310. package/src/lib/library_helpers.ts +123 -0
  311. package/src/lib/logos.ts +302 -0
  312. package/src/lib/mdz.ts +1819 -0
  313. package/src/lib/mdz_components.ts +34 -0
  314. package/src/lib/module.svelte.ts +78 -0
  315. package/src/lib/module_contextmenu.ts +20 -0
  316. package/src/lib/module_helpers.ts +113 -0
  317. package/src/lib/rune_helpers.svelte.ts +10 -0
  318. package/src/lib/storage.ts +48 -0
  319. package/src/lib/svelte_helpers.ts +303 -0
  320. package/src/lib/themer.svelte.ts +68 -0
  321. package/src/lib/tome.ts +38 -0
  322. package/src/lib/ts_helpers.ts +662 -0
  323. package/src/lib/tsdoc_helpers.ts +259 -0
@@ -0,0 +1,372 @@
1
+ <script lang="ts">
2
+ /**
3
+ * This is the default contextmenu root component.
4
+ * It relies on the standard browser `contextmenu` event
5
+ * which means it does not work on iOS Safari
6
+ * because it doesn't fire the `contextmenu` event as of October 2025 --
7
+ * see https://bugs.webkit.org/show_bug.cgi?id=213953.
8
+ *
9
+ * This is the recommended default because
10
+ * it supports haptic feedback with `navigator.vibrate()`.
11
+ * The Safari compatibility version uses timeout-based longpress detection,
12
+ * which causes browsers to block `navigator.vibrate()` since it's not
13
+ * triggered directly by a user event.
14
+ *
15
+ * If you need iOS Safari support, use `ContextmenuRootForSafariCompatibility.svelte`
16
+ * instead. That version implements custom touch handlers and longpress detection at the
17
+ * cost of significantly more complexity and no vibrate support.
18
+ */
19
+ import {swallow} from '@fuzdev/fuz_util/dom.js';
20
+ import {DEV} from 'esm-env';
21
+ import type {ComponentProps, Snippet} from 'svelte';
22
+
23
+ import {
24
+ contextmenu_context,
25
+ contextmenu_dimensions_context,
26
+ ContextmenuState,
27
+ contextmenu_open,
28
+ contextmenu_check_global_root,
29
+ } from './contextmenu_state.svelte.js';
30
+ import ContextmenuLinkEntry from './ContextmenuLinkEntry.svelte';
31
+ import ContextmenuTextEntry from './ContextmenuTextEntry.svelte';
32
+ import ContextmenuSeparator from './ContextmenuSeparator.svelte';
33
+ import {
34
+ CONTEXTMENU_DEFAULT_OPEN_OFFSET_X,
35
+ CONTEXTMENU_DEFAULT_OPEN_OFFSET_Y,
36
+ CONTEXTMENU_DEFAULT_BYPASS_WINDOW,
37
+ CONTEXTMENU_DEFAULT_BYPASS_MOVE_TOLERANCE,
38
+ contextmenu_is_valid_target,
39
+ contextmenu_create_keyboard_handlers,
40
+ contextmenu_create_keydown_handler,
41
+ contextmenu_calculate_constrained_x,
42
+ contextmenu_calculate_constrained_y,
43
+ } from './contextmenu_helpers.js';
44
+
45
+ const {
46
+ contextmenu = new ContextmenuState(),
47
+ open_offset_x = CONTEXTMENU_DEFAULT_OPEN_OFFSET_X,
48
+ open_offset_y = CONTEXTMENU_DEFAULT_OPEN_OFFSET_Y,
49
+ bypass_with_tap_then_longpress = true,
50
+ bypass_window = CONTEXTMENU_DEFAULT_BYPASS_WINDOW,
51
+ bypass_move_tolerance = CONTEXTMENU_DEFAULT_BYPASS_MOVE_TOLERANCE,
52
+ scoped = false,
53
+ link_entry = link_entry_default,
54
+ text_entry = text_entry_default,
55
+ separator_entry = separator_entry_default,
56
+ children,
57
+ }: {
58
+ /**
59
+ * The `contextmenu` prop is not reactive because that's a rare corner case and
60
+ * it's easier to put the `contextmenu` directly in the context
61
+ * rather than wrapping with a store or other reactivity.
62
+ * If you need to change the contextmenu prop for some reason, use a `{#key contextmenu}` block:
63
+ * https://svelte.dev/docs#template-syntax-key
64
+ * @nonreactive
65
+ */
66
+ contextmenu?: ContextmenuState;
67
+ /**
68
+ * The number of pixels to offset from the pointer X position when opened.
69
+ * Useful to ensure the first menu item is immediately under the pointer.
70
+ */
71
+ open_offset_x?: number;
72
+ /**
73
+ * The number of pixels to offset from the pointer Y position when opened.
74
+ * Useful to ensure the first menu item is immediately under the pointer.
75
+ */
76
+ open_offset_y?: number;
77
+ /**
78
+ * Whether to detect tap-then-longpress to bypass the Fuz contextmenu.
79
+ * This allows access to the system contextmenu by tapping once then rightclicking/long-pressing.
80
+ * Setting to `false` disables the gesture.
81
+ */
82
+ bypass_with_tap_then_longpress?: boolean;
83
+ /**
84
+ * The number of milliseconds between taps to detect a gesture that bypasses the Fuz contextmenu.
85
+ * Used only when `bypass_with_tap_then_longpress` is true.
86
+ * If the duration is too long, it'll detect more false positives and interrupt normal usage,
87
+ * but too short and some people will have difficulty performing the gesture.
88
+ */
89
+ bypass_window?: number;
90
+ /**
91
+ * The number of pixels the pointer can be moved between taps to detect a tap-then-longpress.
92
+ * Used only when `bypass_with_tap_then_longpress` is true.
93
+ */
94
+ bypass_move_tolerance?: number;
95
+ /**
96
+ * If `true`, wraps `children` with a div and listens to events on it instead of the window.
97
+ */
98
+ scoped?: boolean;
99
+ /**
100
+ * Snippet for rendering link entries.
101
+ * Set to `null` to disable automatic link detection.
102
+ * Defaults to `link_entry_default` which renders `ContextmenuLinkEntry`.
103
+ */
104
+ link_entry?: Snippet<[ComponentProps<typeof ContextmenuLinkEntry>]> | null;
105
+ /**
106
+ * Snippet for rendering copy text entries.
107
+ * Set to `null` to disable automatic copy text detection.
108
+ * Defaults to `text_entry_default` which renders `ContextmenuTextEntry`.
109
+ */
110
+ text_entry?: Snippet<[ComponentProps<typeof ContextmenuTextEntry>]> | null;
111
+ /**
112
+ * Snippet for rendering separator entries.
113
+ * Set to `null` to disable automatic separator rendering.
114
+ * Defaults to `separator_entry_default` which renders `ContextmenuSeparator`.
115
+ */
116
+ separator_entry?: Snippet<[ComponentProps<typeof ContextmenuSeparator>]> | null;
117
+ children: Snippet;
118
+ } = $props();
119
+
120
+ contextmenu_context.set(contextmenu);
121
+
122
+ if (DEV) contextmenu_check_global_root(() => scoped); // TODO @many is this import tree-shaken?
123
+
124
+ let el: HTMLElement | undefined = $state();
125
+
126
+ const {layout} = $derived(contextmenu);
127
+
128
+ const dimensions = contextmenu_dimensions_context.set();
129
+
130
+ const x = $derived(
131
+ contextmenu_calculate_constrained_x(contextmenu.x, dimensions.width, layout.width),
132
+ );
133
+ const y = $derived(
134
+ contextmenu_calculate_constrained_y(contextmenu.y, dimensions.height, layout.height),
135
+ );
136
+
137
+ // State for tap-then-longpress bypass detection.
138
+ // These values are `undefined` when unused, and `null` after being reset.
139
+ let touch_x: number | undefined | null = $state();
140
+ let touch_y: number | undefined | null = $state();
141
+ let first_tap_time: number | undefined | null = $state();
142
+ let longpress_bypass: boolean | undefined = $state();
143
+ let tap_tracking_timeout: NodeJS.Timeout | undefined | null = $state();
144
+
145
+ const on_window_contextmenu = (e: MouseEvent) => {
146
+ // Handle the tap-then-longpress bypass gesture
147
+ if (longpress_bypass) {
148
+ // Clear all tap tracking state after consuming the bypass
149
+ longpress_bypass = false;
150
+ first_tap_time = null;
151
+ touch_x = null;
152
+ touch_y = null;
153
+ if (tap_tracking_timeout != null) {
154
+ clearTimeout(tap_tracking_timeout);
155
+ tap_tracking_timeout = null;
156
+ }
157
+ return;
158
+ }
159
+ const {target} = e;
160
+ if (!contextmenu_is_valid_target(target, e.shiftKey)) {
161
+ return;
162
+ }
163
+ // Don't open contextmenu when clicking on the menu itself
164
+ if (el?.contains(target)) {
165
+ return;
166
+ }
167
+ if (
168
+ contextmenu_open(target, e.clientX + open_offset_x, e.clientY + open_offset_y, contextmenu, {
169
+ link_enabled: link_entry !== null,
170
+ text_enabled: text_entry !== null,
171
+ separator_enabled: separator_entry !== null,
172
+ })
173
+ ) {
174
+ swallow(e);
175
+ }
176
+ };
177
+
178
+ /**
179
+ * Touch event handler for tap-then-longpress bypass detection.
180
+ *
181
+ * This allows users to access the native context menu by performing a tap
182
+ * followed by a longpress/rightclick within a specified time window.
183
+ * The bypass gesture is useful for accessing browser features like text selection
184
+ * or the native context menu when the Fuz contextmenu would normally override it.
185
+ *
186
+ * Note: preventDefault is not called as we're only observing touch patterns,
187
+ * not intercepting them. The actual bypass happens in on_window_contextmenu.
188
+ */
189
+ const touchstart = (e: TouchEvent): void => {
190
+ if (!bypass_with_tap_then_longpress) return;
191
+
192
+ const {touches, target} = e;
193
+ if (
194
+ contextmenu.opened ||
195
+ touches.length !== 1 ||
196
+ !contextmenu_is_valid_target(target, e.shiftKey)
197
+ ) {
198
+ // Reset all state if conditions aren't met
199
+ first_tap_time = null;
200
+ touch_x = null;
201
+ touch_y = null;
202
+ longpress_bypass = false;
203
+ if (tap_tracking_timeout != null) {
204
+ clearTimeout(tap_tracking_timeout);
205
+ tap_tracking_timeout = null;
206
+ }
207
+ return;
208
+ }
209
+
210
+ const {clientX, clientY} = touches[0]!;
211
+
212
+ // Check if this is a tap-then-longpress gesture
213
+ if (
214
+ first_tap_time != null &&
215
+ performance.now() - first_tap_time < bypass_window &&
216
+ Math.hypot(clientX - touch_x!, clientY - touch_y!) < bypass_move_tolerance
217
+ ) {
218
+ // This is a tap-then-longpress - set bypass flag and clear tap tracking
219
+ longpress_bypass = true;
220
+ first_tap_time = null;
221
+ touch_x = null;
222
+ touch_y = null;
223
+ if (tap_tracking_timeout != null) {
224
+ clearTimeout(tap_tracking_timeout);
225
+ tap_tracking_timeout = null;
226
+ }
227
+ return;
228
+ }
229
+
230
+ // Record this tap for potential future bypass detection
231
+ first_tap_time = performance.now();
232
+ touch_x = clientX;
233
+ touch_y = clientY;
234
+
235
+ // Set timeout to clear stale tap tracking after the detection window expires
236
+ if (tap_tracking_timeout != null) {
237
+ clearTimeout(tap_tracking_timeout);
238
+ }
239
+ tap_tracking_timeout = setTimeout(() => {
240
+ first_tap_time = null;
241
+ touch_x = null;
242
+ touch_y = null;
243
+ tap_tracking_timeout = null;
244
+ }, bypass_window);
245
+ };
246
+
247
+ /**
248
+ * Reset state when touch is cancelled (e.g., when scrolling starts).
249
+ */
250
+ const touchcancel = (): void => {
251
+ // Reset all bypass detection state
252
+ first_tap_time = null;
253
+ touch_x = null;
254
+ touch_y = null;
255
+ longpress_bypass = false;
256
+ if (tap_tracking_timeout != null) {
257
+ clearTimeout(tap_tracking_timeout);
258
+ tap_tracking_timeout = null;
259
+ }
260
+ };
261
+
262
+ // Passive listener that runs during the event's `capture` phase
263
+ // so that things like the Dialog don't eat the events and prevent the contextmenu from closing.
264
+ const mousedown = (e: MouseEvent) => {
265
+ if (el && !el.contains(e.target as any)) {
266
+ contextmenu.close();
267
+ }
268
+ };
269
+
270
+ const keyboard_handlers = contextmenu_create_keyboard_handlers(contextmenu);
271
+ const keydown = contextmenu_create_keydown_handler(keyboard_handlers);
272
+ </script>
273
+
274
+ <svelte:window
275
+ oncontextmenu={scoped ? undefined : on_window_contextmenu}
276
+ onmousedown={!contextmenu.opened ? undefined : mousedown}
277
+ onkeydown={!contextmenu.opened ? undefined : keydown}
278
+ ontouchstartcapture={scoped || !bypass_with_tap_then_longpress ? undefined : touchstart}
279
+ ontouchcancelcapture={scoped || !bypass_with_tap_then_longpress ? undefined : touchcancel}
280
+ />
281
+
282
+ {#if scoped}
283
+ <div
284
+ class="contextmenu_root"
285
+ role="region"
286
+ oncontextmenu={on_window_contextmenu}
287
+ ontouchstartcapture={!bypass_with_tap_then_longpress ? undefined : touchstart}
288
+ ontouchcancelcapture={!bypass_with_tap_then_longpress ? undefined : touchcancel}
289
+ >
290
+ {@render children()}
291
+ </div>
292
+ {:else}
293
+ {@render children()}
294
+ {/if}
295
+
296
+ {#if !contextmenu.has_custom_layout}
297
+ <div
298
+ class="contextmenu_layout"
299
+ bind:clientWidth={layout.width}
300
+ bind:clientHeight={layout.height}
301
+ aria-hidden="true"
302
+ ></div>
303
+ {/if}
304
+
305
+ <!-- TODO animate the contextmenu as it appears somehow -->
306
+ <!-- TODO implement focus management per APG: store `document.activeElement` when opening, focus menu on mount, restore focus on close if element `isConnected` -->
307
+ {#if contextmenu.opened}
308
+ <ul
309
+ class="contextmenu unstyled pane"
310
+ role="menu"
311
+ aria-label="context menu"
312
+ tabindex="-1"
313
+ bind:this={el}
314
+ bind:offsetWidth={dimensions.width}
315
+ bind:offsetHeight={dimensions.height}
316
+ style:transform="translate3d({x}px, {y}px, 0)"
317
+ >
318
+ <!-- TODO maybe this should be generic? -->
319
+ {#each contextmenu.params as p (p)}
320
+ {#if typeof p === 'function'}
321
+ {@render p()}
322
+ {:else if p.snippet === 'link'}
323
+ {@render link_entry?.(p.props)}
324
+ {:else if p.snippet === 'text'}
325
+ {@render text_entry?.(p.props)}
326
+ {:else if p.snippet === 'separator'}
327
+ {@render separator_entry?.(p.props)}
328
+ {/if}
329
+ {/each}
330
+ </ul>
331
+ {/if}
332
+
333
+ {#snippet link_entry_default(props: ComponentProps<typeof ContextmenuLinkEntry>)}
334
+ <ContextmenuLinkEntry {...props} />
335
+ {/snippet}
336
+
337
+ {#snippet text_entry_default(props: ComponentProps<typeof ContextmenuTextEntry>)}
338
+ <ContextmenuTextEntry {...props} />
339
+ {/snippet}
340
+
341
+ {#snippet separator_entry_default(props: ComponentProps<typeof ContextmenuSeparator>)}
342
+ <ContextmenuSeparator {...props} />
343
+ {/snippet}
344
+
345
+ <style>
346
+ .contextmenu_root {
347
+ display: contents;
348
+ }
349
+ .contextmenu {
350
+ --icon_size: var(--icon_size_xs);
351
+ --contextmenu_width: 320px;
352
+ position: fixed;
353
+ left: 0;
354
+ top: 0;
355
+ z-index: var(--contextmenu_z_index, 200);
356
+ max-width: var(--contextmenu_width);
357
+ width: 100%;
358
+ }
359
+ .contextmenu,
360
+ .contextmenu :global(menu.pane) {
361
+ border: var(--contextmenu_border_width, var(--border_width))
362
+ var(--contextmenu_border_style, var(--border_style))
363
+ var(--contextmenu_border_color, var(--border_color));
364
+ border-radius: var(--contextmenu_border_radius, var(--border_radius_xs));
365
+ }
366
+
367
+ .contextmenu_layout {
368
+ z-index: -200;
369
+ position: fixed;
370
+ inset: 0;
371
+ }
372
+ </style>
@@ -0,0 +1,71 @@
1
+ import type { ComponentProps, Snippet } from 'svelte';
2
+ import { ContextmenuState } from './contextmenu_state.svelte.js';
3
+ import ContextmenuLinkEntry from './ContextmenuLinkEntry.svelte';
4
+ import ContextmenuTextEntry from './ContextmenuTextEntry.svelte';
5
+ import ContextmenuSeparator from './ContextmenuSeparator.svelte';
6
+ type $$ComponentProps = {
7
+ /**
8
+ * The `contextmenu` prop is not reactive because that's a rare corner case and
9
+ * it's easier to put the `contextmenu` directly in the context
10
+ * rather than wrapping with a store or other reactivity.
11
+ * If you need to change the contextmenu prop for some reason, use a `{#key contextmenu}` block:
12
+ * https://svelte.dev/docs#template-syntax-key
13
+ * @nonreactive
14
+ */
15
+ contextmenu?: ContextmenuState;
16
+ /**
17
+ * The number of pixels to offset from the pointer X position when opened.
18
+ * Useful to ensure the first menu item is immediately under the pointer.
19
+ */
20
+ open_offset_x?: number;
21
+ /**
22
+ * The number of pixels to offset from the pointer Y position when opened.
23
+ * Useful to ensure the first menu item is immediately under the pointer.
24
+ */
25
+ open_offset_y?: number;
26
+ /**
27
+ * Whether to detect tap-then-longpress to bypass the Fuz contextmenu.
28
+ * This allows access to the system contextmenu by tapping once then rightclicking/long-pressing.
29
+ * Setting to `false` disables the gesture.
30
+ */
31
+ bypass_with_tap_then_longpress?: boolean;
32
+ /**
33
+ * The number of milliseconds between taps to detect a gesture that bypasses the Fuz contextmenu.
34
+ * Used only when `bypass_with_tap_then_longpress` is true.
35
+ * If the duration is too long, it'll detect more false positives and interrupt normal usage,
36
+ * but too short and some people will have difficulty performing the gesture.
37
+ */
38
+ bypass_window?: number;
39
+ /**
40
+ * The number of pixels the pointer can be moved between taps to detect a tap-then-longpress.
41
+ * Used only when `bypass_with_tap_then_longpress` is true.
42
+ */
43
+ bypass_move_tolerance?: number;
44
+ /**
45
+ * If `true`, wraps `children` with a div and listens to events on it instead of the window.
46
+ */
47
+ scoped?: boolean;
48
+ /**
49
+ * Snippet for rendering link entries.
50
+ * Set to `null` to disable automatic link detection.
51
+ * Defaults to `link_entry_default` which renders `ContextmenuLinkEntry`.
52
+ */
53
+ link_entry?: Snippet<[ComponentProps<typeof ContextmenuLinkEntry>]> | null;
54
+ /**
55
+ * Snippet for rendering copy text entries.
56
+ * Set to `null` to disable automatic copy text detection.
57
+ * Defaults to `text_entry_default` which renders `ContextmenuTextEntry`.
58
+ */
59
+ text_entry?: Snippet<[ComponentProps<typeof ContextmenuTextEntry>]> | null;
60
+ /**
61
+ * Snippet for rendering separator entries.
62
+ * Set to `null` to disable automatic separator rendering.
63
+ * Defaults to `separator_entry_default` which renders `ContextmenuSeparator`.
64
+ */
65
+ separator_entry?: Snippet<[ComponentProps<typeof ContextmenuSeparator>]> | null;
66
+ children: Snippet;
67
+ };
68
+ declare const ContextmenuRoot: import("svelte").Component<$$ComponentProps, {}, "">;
69
+ type ContextmenuRoot = ReturnType<typeof ContextmenuRoot>;
70
+ export default ContextmenuRoot;
71
+ //# sourceMappingURL=ContextmenuRoot.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContextmenuRoot.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/ContextmenuRoot.svelte"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAC,cAAc,EAAE,OAAO,EAAC,MAAM,QAAQ,CAAC;AAEpD,OAAO,EAGL,gBAAgB,EAGhB,MAAM,+BAA+B,CAAC;AACxC,OAAO,oBAAoB,MAAM,+BAA+B,CAAC;AACjE,OAAO,oBAAoB,MAAM,+BAA+B,CAAC;AACjE,OAAO,oBAAoB,MAAM,+BAA+B,CAAC;AAahE,KAAK,gBAAgB,GAAI;IACxB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC3E;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC3E;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChF,QAAQ,EAAE,OAAO,CAAC;CAClB,CAAC;AAsOH,QAAA,MAAM,eAAe,sDAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}