@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.
- package/LICENSE +21 -0
- package/README.md +93 -0
- package/dist/Alert.svelte +108 -0
- package/dist/Alert.svelte.d.ts +16 -0
- package/dist/Alert.svelte.d.ts.map +1 -0
- package/dist/ApiDeclarationList.svelte +35 -0
- package/dist/ApiDeclarationList.svelte.d.ts +9 -0
- package/dist/ApiDeclarationList.svelte.d.ts.map +1 -0
- package/dist/ApiIndex.svelte +65 -0
- package/dist/ApiIndex.svelte.d.ts +23 -0
- package/dist/ApiIndex.svelte.d.ts.map +1 -0
- package/dist/ApiModule.svelte +124 -0
- package/dist/ApiModule.svelte.d.ts +22 -0
- package/dist/ApiModule.svelte.d.ts.map +1 -0
- package/dist/Breadcrumb.svelte +83 -0
- package/dist/Breadcrumb.svelte.d.ts +23 -0
- package/dist/Breadcrumb.svelte.d.ts.map +1 -0
- package/dist/Card.svelte +157 -0
- package/dist/Card.svelte.d.ts +13 -0
- package/dist/Card.svelte.d.ts.map +1 -0
- package/dist/ColorSchemeInput.svelte +65 -0
- package/dist/ColorSchemeInput.svelte.d.ts +11 -0
- package/dist/ColorSchemeInput.svelte.d.ts.map +1 -0
- package/dist/Contextmenu.svelte +30 -0
- package/dist/Contextmenu.svelte.d.ts +32 -0
- package/dist/Contextmenu.svelte.d.ts.map +1 -0
- package/dist/ContextmenuEntry.svelte +74 -0
- package/dist/ContextmenuEntry.svelte.d.ts +12 -0
- package/dist/ContextmenuEntry.svelte.d.ts.map +1 -0
- package/dist/ContextmenuLinkEntry.svelte +112 -0
- package/dist/ContextmenuLinkEntry.svelte.d.ts +12 -0
- package/dist/ContextmenuLinkEntry.svelte.d.ts.map +1 -0
- package/dist/ContextmenuRoot.svelte +372 -0
- package/dist/ContextmenuRoot.svelte.d.ts +71 -0
- package/dist/ContextmenuRoot.svelte.d.ts.map +1 -0
- package/dist/ContextmenuRootForSafariCompatibility.svelte +541 -0
- package/dist/ContextmenuRootForSafariCompatibility.svelte.d.ts +79 -0
- package/dist/ContextmenuRootForSafariCompatibility.svelte.d.ts.map +1 -0
- package/dist/ContextmenuSeparator.svelte +16 -0
- package/dist/ContextmenuSeparator.svelte.d.ts +4 -0
- package/dist/ContextmenuSeparator.svelte.d.ts.map +1 -0
- package/dist/ContextmenuSubmenu.svelte +116 -0
- package/dist/ContextmenuSubmenu.svelte.d.ts +10 -0
- package/dist/ContextmenuSubmenu.svelte.d.ts.map +1 -0
- package/dist/ContextmenuTextEntry.svelte +21 -0
- package/dist/ContextmenuTextEntry.svelte.d.ts +10 -0
- package/dist/ContextmenuTextEntry.svelte.d.ts.map +1 -0
- package/dist/CopyToClipboard.svelte +81 -0
- package/dist/CopyToClipboard.svelte.d.ts +18 -0
- package/dist/CopyToClipboard.svelte.d.ts.map +1 -0
- package/dist/DeclarationDetail.svelte +340 -0
- package/dist/DeclarationDetail.svelte.d.ts +8 -0
- package/dist/DeclarationDetail.svelte.d.ts.map +1 -0
- package/dist/DeclarationLink.svelte +50 -0
- package/dist/DeclarationLink.svelte.d.ts +8 -0
- package/dist/DeclarationLink.svelte.d.ts.map +1 -0
- package/dist/Details.svelte +51 -0
- package/dist/Details.svelte.d.ts +20 -0
- package/dist/Details.svelte.d.ts.map +1 -0
- package/dist/Dialog.svelte +217 -0
- package/dist/Dialog.svelte.d.ts +30 -0
- package/dist/Dialog.svelte.d.ts.map +1 -0
- package/dist/Dialogs.svelte +28 -0
- package/dist/Dialogs.svelte.d.ts +11 -0
- package/dist/Dialogs.svelte.d.ts.map +1 -0
- package/dist/Docs.svelte +179 -0
- package/dist/Docs.svelte.d.ts +13 -0
- package/dist/Docs.svelte.d.ts.map +1 -0
- package/dist/DocsContent.svelte +40 -0
- package/dist/DocsContent.svelte.d.ts +14 -0
- package/dist/DocsContent.svelte.d.ts.map +1 -0
- package/dist/DocsFooter.svelte +64 -0
- package/dist/DocsFooter.svelte.d.ts +15 -0
- package/dist/DocsFooter.svelte.d.ts.map +1 -0
- package/dist/DocsLink.svelte +41 -0
- package/dist/DocsLink.svelte.d.ts +12 -0
- package/dist/DocsLink.svelte.d.ts.map +1 -0
- package/dist/DocsList.svelte +44 -0
- package/dist/DocsList.svelte.d.ts +11 -0
- package/dist/DocsList.svelte.d.ts.map +1 -0
- package/dist/DocsMenu.svelte +55 -0
- package/dist/DocsMenu.svelte.d.ts +11 -0
- package/dist/DocsMenu.svelte.d.ts.map +1 -0
- package/dist/DocsMenuHeader.svelte +15 -0
- package/dist/DocsMenuHeader.svelte.d.ts +9 -0
- package/dist/DocsMenuHeader.svelte.d.ts.map +1 -0
- package/dist/DocsModulesList.svelte +32 -0
- package/dist/DocsModulesList.svelte.d.ts +7 -0
- package/dist/DocsModulesList.svelte.d.ts.map +1 -0
- package/dist/DocsPageLinks.svelte +61 -0
- package/dist/DocsPageLinks.svelte.d.ts +8 -0
- package/dist/DocsPageLinks.svelte.d.ts.map +1 -0
- package/dist/DocsPrimaryNav.svelte +93 -0
- package/dist/DocsPrimaryNav.svelte.d.ts +11 -0
- package/dist/DocsPrimaryNav.svelte.d.ts.map +1 -0
- package/dist/DocsSearch.svelte +48 -0
- package/dist/DocsSearch.svelte.d.ts +11 -0
- package/dist/DocsSearch.svelte.d.ts.map +1 -0
- package/dist/DocsSecondaryNav.svelte +63 -0
- package/dist/DocsSecondaryNav.svelte.d.ts +9 -0
- package/dist/DocsSecondaryNav.svelte.d.ts.map +1 -0
- package/dist/DocsTertiaryNav.svelte +118 -0
- package/dist/DocsTertiaryNav.svelte.d.ts +10 -0
- package/dist/DocsTertiaryNav.svelte.d.ts.map +1 -0
- package/dist/EcosystemLinks.svelte +53 -0
- package/dist/EcosystemLinks.svelte.d.ts +7 -0
- package/dist/EcosystemLinks.svelte.d.ts.map +1 -0
- package/dist/EcosystemLinksPanel.svelte +22 -0
- package/dist/EcosystemLinksPanel.svelte.d.ts +8 -0
- package/dist/EcosystemLinksPanel.svelte.d.ts.map +1 -0
- package/dist/GithubLink.svelte +75 -0
- package/dist/GithubLink.svelte.d.ts +14 -0
- package/dist/GithubLink.svelte.d.ts.map +1 -0
- package/dist/Glyph.svelte +28 -0
- package/dist/Glyph.svelte.d.ts +9 -0
- package/dist/Glyph.svelte.d.ts.map +1 -0
- package/dist/Hashlink.svelte +41 -0
- package/dist/Hashlink.svelte.d.ts +8 -0
- package/dist/Hashlink.svelte.d.ts.map +1 -0
- package/dist/HiddenPersonalLinks.svelte +6 -0
- package/dist/HiddenPersonalLinks.svelte.d.ts +27 -0
- package/dist/HiddenPersonalLinks.svelte.d.ts.map +1 -0
- package/dist/HueInput.svelte +127 -0
- package/dist/HueInput.svelte.d.ts +11 -0
- package/dist/HueInput.svelte.d.ts.map +1 -0
- package/dist/ImgOrSvg.svelte +58 -0
- package/dist/ImgOrSvg.svelte.d.ts +25 -0
- package/dist/ImgOrSvg.svelte.d.ts.map +1 -0
- package/dist/LibraryDetail.svelte +297 -0
- package/dist/LibraryDetail.svelte.d.ts +15 -0
- package/dist/LibraryDetail.svelte.d.ts.map +1 -0
- package/dist/LibrarySummary.svelte +151 -0
- package/dist/LibrarySummary.svelte.d.ts +16 -0
- package/dist/LibrarySummary.svelte.d.ts.map +1 -0
- package/dist/MdnLink.svelte +40 -0
- package/dist/MdnLink.svelte.d.ts +8 -0
- package/dist/MdnLink.svelte.d.ts.map +1 -0
- package/dist/Mdz.svelte +30 -0
- package/dist/Mdz.svelte.d.ts +10 -0
- package/dist/Mdz.svelte.d.ts.map +1 -0
- package/dist/MdzNodeView.svelte +93 -0
- package/dist/MdzNodeView.svelte.d.ts +9 -0
- package/dist/MdzNodeView.svelte.d.ts.map +1 -0
- package/dist/ModuleLink.svelte +48 -0
- package/dist/ModuleLink.svelte.d.ts +8 -0
- package/dist/ModuleLink.svelte.d.ts.map +1 -0
- package/dist/PasteFromClipboard.svelte +35 -0
- package/dist/PasteFromClipboard.svelte.d.ts +9 -0
- package/dist/PasteFromClipboard.svelte.d.ts.map +1 -0
- package/dist/PendingAnimation.svelte +62 -0
- package/dist/PendingAnimation.svelte.d.ts +13 -0
- package/dist/PendingAnimation.svelte.d.ts.map +1 -0
- package/dist/PendingButton.svelte +75 -0
- package/dist/PendingButton.svelte.d.ts +17 -0
- package/dist/PendingButton.svelte.d.ts.map +1 -0
- package/dist/ProjectLinks.svelte +54 -0
- package/dist/ProjectLinks.svelte.d.ts +19 -0
- package/dist/ProjectLinks.svelte.d.ts.map +1 -0
- package/dist/Redirect.svelte +44 -0
- package/dist/Redirect.svelte.d.ts +23 -0
- package/dist/Redirect.svelte.d.ts.map +1 -0
- package/dist/Spiders.svelte +57 -0
- package/dist/Spiders.svelte.d.ts +9 -0
- package/dist/Spiders.svelte.d.ts.map +1 -0
- package/dist/Svg.svelte +99 -0
- package/dist/Svg.svelte.d.ts +54 -0
- package/dist/Svg.svelte.d.ts.map +1 -0
- package/dist/Teleport.svelte +48 -0
- package/dist/Teleport.svelte.d.ts +15 -0
- package/dist/Teleport.svelte.d.ts.map +1 -0
- package/dist/ThemeInput.svelte +75 -0
- package/dist/ThemeInput.svelte.d.ts +15 -0
- package/dist/ThemeInput.svelte.d.ts.map +1 -0
- package/dist/Themed.svelte +101 -0
- package/dist/Themed.svelte.d.ts +24 -0
- package/dist/Themed.svelte.d.ts.map +1 -0
- package/dist/TomeContent.svelte +67 -0
- package/dist/TomeContent.svelte.d.ts +12 -0
- package/dist/TomeContent.svelte.d.ts.map +1 -0
- package/dist/TomeHeader.svelte +56 -0
- package/dist/TomeHeader.svelte.d.ts +4 -0
- package/dist/TomeHeader.svelte.d.ts.map +1 -0
- package/dist/TomeLink.svelte +29 -0
- package/dist/TomeLink.svelte.d.ts +10 -0
- package/dist/TomeLink.svelte.d.ts.map +1 -0
- package/dist/TomeSection.svelte +65 -0
- package/dist/TomeSection.svelte.d.ts +24 -0
- package/dist/TomeSection.svelte.d.ts.map +1 -0
- package/dist/TomeSectionHeader.svelte +90 -0
- package/dist/TomeSectionHeader.svelte.d.ts +13 -0
- package/dist/TomeSectionHeader.svelte.d.ts.map +1 -0
- package/dist/TypeLink.svelte +19 -0
- package/dist/TypeLink.svelte.d.ts +7 -0
- package/dist/TypeLink.svelte.d.ts.map +1 -0
- package/dist/alert.d.ts +7 -0
- package/dist/alert.d.ts.map +1 -0
- package/dist/alert.js +6 -0
- package/dist/api_search.svelte.d.ts +16 -0
- package/dist/api_search.svelte.d.ts.map +1 -0
- package/dist/api_search.svelte.js +61 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +3 -0
- package/dist/context_helpers.d.ts +17 -0
- package/dist/context_helpers.d.ts.map +1 -0
- package/dist/context_helpers.js +19 -0
- package/dist/contextmenu_helpers.d.ts +16 -0
- package/dist/contextmenu_helpers.d.ts.map +1 -0
- package/dist/contextmenu_helpers.js +39 -0
- package/dist/contextmenu_state.svelte.d.ts +152 -0
- package/dist/contextmenu_state.svelte.d.ts.map +1 -0
- package/dist/contextmenu_state.svelte.js +424 -0
- package/dist/csp.d.ts +160 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +354 -0
- package/dist/csp_of_ryanatkn.d.ts +6 -0
- package/dist/csp_of_ryanatkn.d.ts.map +1 -0
- package/dist/csp_of_ryanatkn.js +14 -0
- package/dist/declaration.svelte.d.ts +84 -0
- package/dist/declaration.svelte.d.ts.map +1 -0
- package/dist/declaration.svelte.js +66 -0
- package/dist/declaration_contextmenu.d.ts +4 -0
- package/dist/declaration_contextmenu.d.ts.map +1 -0
- package/dist/declaration_contextmenu.js +14 -0
- package/dist/dialog.d.ts +24 -0
- package/dist/dialog.d.ts.map +1 -0
- package/dist/dialog.js +12 -0
- package/dist/dimensions.svelte.d.ts +5 -0
- package/dist/dimensions.svelte.d.ts.map +1 -0
- package/dist/dimensions.svelte.js +4 -0
- package/dist/docs_helpers.svelte.d.ts +48 -0
- package/dist/docs_helpers.svelte.d.ts.map +1 -0
- package/dist/docs_helpers.svelte.js +99 -0
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +16 -0
- package/dist/intersect.svelte.d.ts +47 -0
- package/dist/intersect.svelte.d.ts.map +1 -0
- package/dist/intersect.svelte.js +92 -0
- package/dist/library.svelte.d.ts +197 -0
- package/dist/library.svelte.d.ts.map +1 -0
- package/dist/library.svelte.js +130 -0
- package/dist/library_gen.d.ts +34 -0
- package/dist/library_gen.d.ts.map +1 -0
- package/dist/library_gen.js +123 -0
- package/dist/library_gen_helpers.d.ts +85 -0
- package/dist/library_gen_helpers.d.ts.map +1 -0
- package/dist/library_gen_helpers.js +188 -0
- package/dist/library_helpers.d.ts +54 -0
- package/dist/library_helpers.d.ts.map +1 -0
- package/dist/library_helpers.js +102 -0
- package/dist/logos.d.ts +134 -0
- package/dist/logos.d.ts.map +1 -0
- package/dist/logos.js +281 -0
- package/dist/mdz.d.ts +106 -0
- package/dist/mdz.d.ts.map +1 -0
- package/dist/mdz.js +1481 -0
- package/dist/mdz_components.d.ts +37 -0
- package/dist/mdz_components.d.ts.map +1 -0
- package/dist/mdz_components.js +12 -0
- package/dist/module.svelte.d.ts +47 -0
- package/dist/module.svelte.d.ts.map +1 -0
- package/dist/module.svelte.js +56 -0
- package/dist/module_contextmenu.d.ts +4 -0
- package/dist/module_contextmenu.d.ts.map +1 -0
- package/dist/module_contextmenu.js +14 -0
- package/dist/module_helpers.d.ts +69 -0
- package/dist/module_helpers.d.ts.map +1 -0
- package/dist/module_helpers.js +87 -0
- package/dist/rune_helpers.svelte.d.ts +6 -0
- package/dist/rune_helpers.svelte.d.ts.map +1 -0
- package/dist/rune_helpers.svelte.js +10 -0
- package/dist/storage.d.ts +13 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +43 -0
- package/dist/svelte_helpers.d.ts +37 -0
- package/dist/svelte_helpers.d.ts.map +1 -0
- package/dist/svelte_helpers.js +245 -0
- package/dist/themer.svelte.d.ts +24 -0
- package/dist/themer.svelte.d.ts.map +1 -0
- package/dist/themer.svelte.js +43 -0
- package/dist/tome.d.ts +80 -0
- package/dist/tome.d.ts.map +1 -0
- package/dist/tome.js +27 -0
- package/dist/ts_helpers.d.ts +110 -0
- package/dist/ts_helpers.d.ts.map +1 -0
- package/dist/ts_helpers.js +533 -0
- package/dist/tsdoc_helpers.d.ts +98 -0
- package/dist/tsdoc_helpers.d.ts.map +1 -0
- package/dist/tsdoc_helpers.js +221 -0
- package/package.json +128 -0
- package/src/lib/alert.ts +14 -0
- package/src/lib/api_search.svelte.ts +85 -0
- package/src/lib/constants.ts +3 -0
- package/src/lib/context_helpers.ts +47 -0
- package/src/lib/contextmenu_helpers.ts +63 -0
- package/src/lib/contextmenu_state.svelte.ts +515 -0
- package/src/lib/csp.ts +576 -0
- package/src/lib/csp_of_ryanatkn.ts +16 -0
- package/src/lib/declaration.svelte.ts +102 -0
- package/src/lib/declaration_contextmenu.ts +22 -0
- package/src/lib/dialog.ts +35 -0
- package/src/lib/dimensions.svelte.ts +4 -0
- package/src/lib/docs_helpers.svelte.ts +149 -0
- package/src/lib/helpers.ts +10 -0
- package/src/lib/intersect.svelte.ts +152 -0
- package/src/lib/library.svelte.ts +162 -0
- package/src/lib/library_gen.ts +160 -0
- package/src/lib/library_gen_helpers.ts +262 -0
- package/src/lib/library_helpers.ts +123 -0
- package/src/lib/logos.ts +302 -0
- package/src/lib/mdz.ts +1819 -0
- package/src/lib/mdz_components.ts +34 -0
- package/src/lib/module.svelte.ts +78 -0
- package/src/lib/module_contextmenu.ts +20 -0
- package/src/lib/module_helpers.ts +113 -0
- package/src/lib/rune_helpers.svelte.ts +10 -0
- package/src/lib/storage.ts +48 -0
- package/src/lib/svelte_helpers.ts +303 -0
- package/src/lib/themer.svelte.ts +68 -0
- package/src/lib/tome.ts +38 -0
- package/src/lib/ts_helpers.ts +662 -0
- 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"}
|