@omerdlw/base-framework 1.0.6 → 2.0.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/dist/atoms/index.js +1 -103
- package/dist/atoms/index.js.map +1 -1
- package/dist/chunk-3BUP64Q6.js +105 -0
- package/dist/chunk-3BUP64Q6.js.map +1 -0
- package/dist/chunk-3MSK4XGK.js +9888 -0
- package/dist/chunk-3MSK4XGK.js.map +1 -0
- package/dist/chunk-4GMQKD6F.js +353 -0
- package/dist/chunk-4GMQKD6F.js.map +1 -0
- package/dist/chunk-65ZB4QTQ.js +888 -0
- package/dist/chunk-65ZB4QTQ.js.map +1 -0
- package/dist/chunk-6SG7V77J.js +577 -0
- package/dist/chunk-6SG7V77J.js.map +1 -0
- package/dist/chunk-7HQMXTNP.js +419 -0
- package/dist/chunk-7HQMXTNP.js.map +1 -0
- package/dist/chunk-7TAVYJ3A.js +371 -0
- package/dist/chunk-7TAVYJ3A.js.map +1 -0
- package/dist/chunk-AQ4BNXA2.js +349 -0
- package/dist/chunk-AQ4BNXA2.js.map +1 -0
- package/dist/chunk-CGHZSSFR.js +2166 -0
- package/dist/chunk-CGHZSSFR.js.map +1 -0
- package/dist/chunk-DGCQWQLZ.js +61 -0
- package/dist/chunk-DGCQWQLZ.js.map +1 -0
- package/dist/chunk-FIMPHODX.js +67 -0
- package/dist/chunk-FIMPHODX.js.map +1 -0
- package/dist/chunk-GF7TUX5I.js +1889 -0
- package/dist/chunk-GF7TUX5I.js.map +1 -0
- package/dist/chunk-JELG4JLT.js +454 -0
- package/dist/chunk-JELG4JLT.js.map +1 -0
- package/dist/chunk-K676GL7Z.js +51 -0
- package/dist/chunk-K676GL7Z.js.map +1 -0
- package/dist/chunk-KKQT46TP.js +26 -0
- package/dist/chunk-KKQT46TP.js.map +1 -0
- package/dist/chunk-QI3ALLIB.js +38 -0
- package/dist/chunk-QI3ALLIB.js.map +1 -0
- package/dist/chunk-QXR55VVS.js +1139 -0
- package/dist/chunk-QXR55VVS.js.map +1 -0
- package/dist/chunk-R2YGFJDJ.js +408 -0
- package/dist/chunk-R2YGFJDJ.js.map +1 -0
- package/dist/chunk-RFZQWLC5.js +27 -0
- package/dist/chunk-RFZQWLC5.js.map +1 -0
- package/dist/chunk-SYAVGTOX.js +521 -0
- package/dist/chunk-SYAVGTOX.js.map +1 -0
- package/dist/chunk-TV3WEF5G.js +784 -0
- package/dist/chunk-TV3WEF5G.js.map +1 -0
- package/dist/chunk-XASQT4CQ.js +280 -0
- package/dist/chunk-XASQT4CQ.js.map +1 -0
- package/dist/error/index.js +5 -682
- package/dist/error/index.js.map +1 -1
- package/dist/error/reporter.d.ts.map +1 -1
- package/dist/error/theme.d.ts +1 -1
- package/dist/error/theme.d.ts.map +1 -1
- package/dist/events.js +1 -7
- package/dist/events.js.map +1 -1
- package/dist/hooks/index.js +1 -358
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/use-global-event.d.ts +1 -1
- package/dist/index.js +18 -20029
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.js +2 -2035
- package/dist/kernel/index.js.map +1 -1
- package/dist/kernel/types.d.ts +1 -1
- package/dist/modules/ambient/analyze.d.ts +46 -0
- package/dist/modules/ambient/analyze.d.ts.map +1 -0
- package/dist/modules/ambient/constants.d.ts +1 -5
- package/dist/modules/ambient/constants.d.ts.map +1 -1
- package/dist/modules/ambient/context.d.ts.map +1 -1
- package/dist/modules/ambient/dom.d.ts +11 -12
- package/dist/modules/ambient/dom.d.ts.map +1 -1
- package/dist/modules/ambient/extractor.d.ts +10 -10
- package/dist/modules/ambient/extractor.d.ts.map +1 -1
- package/dist/modules/ambient/hooks.d.ts +1 -1
- package/dist/modules/ambient/hooks.d.ts.map +1 -1
- package/dist/modules/ambient/index.d.ts +6 -4
- package/dist/modules/ambient/index.d.ts.map +1 -1
- package/dist/modules/ambient/index.js +7 -762
- package/dist/modules/ambient/index.js.map +1 -1
- package/dist/modules/ambient/module.d.ts +1 -1
- package/dist/modules/ambient/module.d.ts.map +1 -1
- package/dist/modules/ambient/oklab.d.ts +36 -0
- package/dist/modules/ambient/oklab.d.ts.map +1 -0
- package/dist/modules/ambient/pipeline.d.ts +28 -0
- package/dist/modules/ambient/pipeline.d.ts.map +1 -0
- package/dist/modules/ambient/quantize.d.ts +22 -0
- package/dist/modules/ambient/quantize.d.ts.map +1 -0
- package/dist/modules/ambient/server/address.d.ts +8 -0
- package/dist/modules/ambient/server/address.d.ts.map +1 -0
- package/dist/modules/ambient/server/index.d.ts +53 -0
- package/dist/modules/ambient/server/index.d.ts.map +1 -0
- package/dist/modules/ambient/server/index.js +221 -0
- package/dist/modules/ambient/server/index.js.map +1 -0
- package/dist/modules/ambient/settings.d.ts +19 -0
- package/dist/modules/ambient/settings.d.ts.map +1 -0
- package/dist/modules/ambient/types.d.ts +105 -9
- package/dist/modules/ambient/types.d.ts.map +1 -1
- package/dist/modules/ambient/worker/index.d.ts +15 -0
- package/dist/modules/ambient/worker/index.d.ts.map +1 -0
- package/dist/modules/ambient/worker/index.js +114 -0
- package/dist/modules/ambient/worker/index.js.map +1 -0
- package/dist/modules/background/constants.d.ts +2 -2
- package/dist/modules/background/constants.d.ts.map +1 -1
- package/dist/modules/background/hooks.d.ts +1 -1
- package/dist/modules/background/index.js +6 -3078
- package/dist/modules/background/index.js.map +1 -1
- package/dist/modules/background/motion.d.ts +9 -7
- package/dist/modules/background/motion.d.ts.map +1 -1
- package/dist/modules/background/overlay.d.ts +3 -8
- package/dist/modules/background/overlay.d.ts.map +1 -1
- package/dist/modules/background/types.d.ts +16 -2
- package/dist/modules/background/types.d.ts.map +1 -1
- package/dist/modules/context-menu/constants.d.ts +2 -2
- package/dist/modules/context-menu/constants.d.ts.map +1 -1
- package/dist/modules/context-menu/hooks.d.ts +1 -1
- package/dist/modules/context-menu/index.js +10 -1896
- package/dist/modules/context-menu/index.js.map +1 -1
- package/dist/modules/context-menu/motion.d.ts +1 -8
- package/dist/modules/context-menu/motion.d.ts.map +1 -1
- package/dist/modules/context-menu/overlay.d.ts.map +1 -1
- package/dist/modules/context-menu/types.d.ts +16 -2
- package/dist/modules/context-menu/types.d.ts.map +1 -1
- package/dist/modules/controls/constants.d.ts +1 -1
- package/dist/modules/controls/constants.d.ts.map +1 -1
- package/dist/modules/controls/entries.d.ts +1 -1
- package/dist/modules/controls/hooks.d.ts +1 -1
- package/dist/modules/controls/index.js +4 -1110
- package/dist/modules/controls/index.js.map +1 -1
- package/dist/modules/controls/overlay.d.ts +1 -1
- package/dist/modules/controls/types.d.ts +2 -2
- package/dist/modules/dock/cards.d.ts +708 -24
- package/dist/modules/dock/cards.d.ts.map +1 -1
- package/dist/modules/dock/constants.d.ts +2 -2
- package/dist/modules/dock/constants.d.ts.map +1 -1
- package/dist/modules/dock/helpers.d.ts +2 -2
- package/dist/modules/dock/hooks.d.ts +2 -2
- package/dist/modules/dock/index.d.ts +1 -1
- package/dist/modules/dock/index.d.ts.map +1 -1
- package/dist/modules/dock/index.js +9 -12132
- package/dist/modules/dock/index.js.map +1 -1
- package/dist/modules/dock/module.d.ts +1 -1
- package/dist/modules/dock/motion.d.ts +41 -915
- package/dist/modules/dock/motion.d.ts.map +1 -1
- package/dist/modules/dock/overlay.d.ts.map +1 -1
- package/dist/modules/dock/runtime/dock.d.ts +1 -23
- package/dist/modules/dock/runtime/dock.d.ts.map +1 -1
- package/dist/modules/dock/styles.d.ts +4 -4
- package/dist/modules/dock/surface/hooks.d.ts +5 -20
- package/dist/modules/dock/surface/hooks.d.ts.map +1 -1
- package/dist/modules/dock/surface/machine.d.ts +4 -3
- package/dist/modules/dock/surface/machine.d.ts.map +1 -1
- package/dist/modules/dock/surface/stack.d.ts.map +1 -1
- package/dist/modules/dock/types.d.ts +158 -3
- package/dist/modules/dock/types.d.ts.map +1 -1
- package/dist/modules/index.js +16 -17883
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/loading/constants.d.ts +1 -1
- package/dist/modules/loading/constants.d.ts.map +1 -1
- package/dist/modules/loading/context.d.ts +1 -1
- package/dist/modules/loading/hooks.d.ts +1 -1
- package/dist/modules/loading/index.js +5 -1153
- package/dist/modules/loading/index.js.map +1 -1
- package/dist/modules/loading/types.d.ts +2 -2
- package/dist/modules/media/context.d.ts +1 -1
- package/dist/modules/media/index.js +3 -570
- package/dist/modules/media/index.js.map +1 -1
- package/dist/modules/media/types.d.ts +2 -2
- package/dist/modules/modal/constants.d.ts +2 -2
- package/dist/modules/modal/constants.d.ts.map +1 -1
- package/dist/modules/modal/context.d.ts +1 -1
- package/dist/modules/modal/hooks.d.ts +3 -3
- package/dist/modules/modal/index.js +8 -2355
- package/dist/modules/modal/index.js.map +1 -1
- package/dist/modules/modal/motion.d.ts +3 -8
- package/dist/modules/modal/motion.d.ts.map +1 -1
- package/dist/modules/modal/overlay.d.ts.map +1 -1
- package/dist/modules/modal/types.d.ts +21 -0
- package/dist/modules/modal/types.d.ts.map +1 -1
- package/dist/modules/notification/constants.d.ts +2 -2
- package/dist/modules/notification/constants.d.ts.map +1 -1
- package/dist/modules/notification/context.d.ts +1 -1
- package/dist/modules/notification/hooks.d.ts +1 -1
- package/dist/modules/notification/index.js +4 -828
- package/dist/modules/notification/index.js.map +1 -1
- package/dist/modules/notification/motion.d.ts +1 -7
- package/dist/modules/notification/motion.d.ts.map +1 -1
- package/dist/modules/notification/overlay.d.ts.map +1 -1
- package/dist/modules/notification/types.d.ts +9 -1
- package/dist/modules/notification/types.d.ts.map +1 -1
- package/dist/overlay-TZ6L52A5.js +9 -0
- package/dist/overlay-TZ6L52A5.js.map +1 -0
- package/dist/provider.d.ts +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +6 -1968
- package/dist/provider.js.map +1 -1
- package/dist/result.js +2 -63
- package/dist/result.js.map +1 -1
- package/dist/theme.d.ts +45 -8
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +3 -41
- package/dist/theme.js.map +1 -1
- package/dist/tokens/index.d.ts +1 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +126 -155
- package/dist/tokens/index.js.map +1 -1
- package/dist/tokens/motion-config.d.ts +44 -0
- package/dist/tokens/motion-config.d.ts.map +1 -0
- package/dist/tokens/motion.d.ts +23 -174
- package/dist/tokens/motion.d.ts.map +1 -1
- package/dist/utils/index.js +4 -82
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/report.d.ts.map +1 -1
- package/docs/README.md +1 -0
- package/docs/architecture-and-rules.md +4 -3
- package/docs/core-engine.md +7 -10
- package/docs/getting-started.md +4 -2
- package/docs/modules/ambient.md +194 -80
- package/docs/modules/background.md +3 -3
- package/docs/modules/context-menu.md +2 -2
- package/docs/modules/dock.md +8 -5
- package/docs/modules/modal.md +2 -2
- package/docs/modules/notification.md +2 -2
- package/docs/theming.md +81 -9
- package/package.json +22 -5
- package/templates/AGENTS.md +1 -1
- package/templates/motion/background.ts +17 -0
- package/templates/motion/context-menu.ts +63 -0
- package/templates/motion/dock.ts +327 -0
- package/templates/motion/modal.ts +79 -0
- package/templates/motion/notification.ts +27 -0
- package/templates/motion/tokens.ts +47 -0
- package/templates/tsconfig.json +7 -0
- package/dist/modules/ambient/color.d.ts +0 -21
- package/dist/modules/ambient/color.d.ts.map +0 -1
|
@@ -0,0 +1,888 @@
|
|
|
1
|
+
import { useOptionalDockState } from './chunk-3MSK4XGK.js';
|
|
2
|
+
import { defineThemeSpec, useTheme, useModuleMotion } from './chunk-7TAVYJ3A.js';
|
|
3
|
+
import { getOrCreateGlobalContext, defineModule, useRegistryEntries, useModuleRegistration } from './chunk-GF7TUX5I.js';
|
|
4
|
+
import { createStore, useStore, useIsomorphicLayoutEffect, useRequiredContext, isBrowser, toArray, toFiniteNumber, report, isObject, isImageIconSource } from './chunk-TV3WEF5G.js';
|
|
5
|
+
import { Icon } from './chunk-3BUP64Q6.js';
|
|
6
|
+
import { useMemo, useState, useRef, useEffect, useSyncExternalStore, useLayoutEffect, useCallback, isValidElement } from 'react';
|
|
7
|
+
import { usePathname } from 'next/navigation';
|
|
8
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
9
|
+
import { createPortal } from 'react-dom';
|
|
10
|
+
import { AnimatePresence, motion } from 'motion/react';
|
|
11
|
+
|
|
12
|
+
// src/modules/context-menu/constants.ts
|
|
13
|
+
var contextMenuTheme = defineThemeSpec("context-menu");
|
|
14
|
+
var CURRENT_PAGE_KEY = "current-page";
|
|
15
|
+
var GLOBAL_MENU_KEY = "*";
|
|
16
|
+
var MENU_SCREEN_MARGIN = 10;
|
|
17
|
+
var CONTEXT_MENU_VISIBILITY_EVENT = "context-menu:visibility";
|
|
18
|
+
var INITIAL_POSITION = Object.freeze({
|
|
19
|
+
x: 0,
|
|
20
|
+
y: 0
|
|
21
|
+
});
|
|
22
|
+
function extractNodeText(value) {
|
|
23
|
+
if (typeof value === "string") return value;
|
|
24
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
25
|
+
return String(value);
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
return value.map(extractNodeText).filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
|
|
29
|
+
}
|
|
30
|
+
if (isValidElement(value)) {
|
|
31
|
+
return extractNodeText(value.props?.children);
|
|
32
|
+
}
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
function resolveAsBoolean(value, context, defaultValue = true) {
|
|
36
|
+
if (typeof value === "function") {
|
|
37
|
+
try {
|
|
38
|
+
return Boolean(value(context));
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (value === void 0) {
|
|
44
|
+
return defaultValue;
|
|
45
|
+
}
|
|
46
|
+
return Boolean(value);
|
|
47
|
+
}
|
|
48
|
+
function resolveAsValue(value, context, fallback) {
|
|
49
|
+
if (typeof value === "function") {
|
|
50
|
+
try {
|
|
51
|
+
const resolved = value(context);
|
|
52
|
+
return resolved === void 0 ? fallback : resolved;
|
|
53
|
+
} catch {
|
|
54
|
+
return fallback;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return value === void 0 ? fallback : value;
|
|
58
|
+
}
|
|
59
|
+
function safeInvoke(handler, ...args) {
|
|
60
|
+
if (typeof handler !== "function") return void 0;
|
|
61
|
+
try {
|
|
62
|
+
const result = handler(...args);
|
|
63
|
+
if (result && typeof result.then === "function") {
|
|
64
|
+
result.catch(
|
|
65
|
+
(err) => report("Context menu async callback", err)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
report("Context menu callback", error);
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function resolveContextMenuPageMeta(dockItem, pathname = "") {
|
|
75
|
+
if (!isObject(dockItem)) return null;
|
|
76
|
+
const title = dockItem.contextMenuTitle ?? dockItem.title ?? null;
|
|
77
|
+
const description = dockItem.contextMenuDescription ?? dockItem.description ?? null;
|
|
78
|
+
const eyebrow = dockItem.contextMenuEyebrow ?? dockItem.eyebrow ?? null;
|
|
79
|
+
const icon = dockItem.contextMenuIcon ?? dockItem.icon ?? null;
|
|
80
|
+
if (!title && !description && !icon && !eyebrow) return null;
|
|
81
|
+
return {
|
|
82
|
+
description,
|
|
83
|
+
descriptionText: extractNodeText(description),
|
|
84
|
+
eyebrow,
|
|
85
|
+
icon,
|
|
86
|
+
path: typeof dockItem.path === "string" && dockItem.path ? dockItem.path : pathname,
|
|
87
|
+
title,
|
|
88
|
+
titleText: extractNodeText(title) || (typeof dockItem.name === "string" ? dockItem.name : "") || ""
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function resolveItem(item, index, context) {
|
|
92
|
+
if (!item) return null;
|
|
93
|
+
if (item === "separator" || item.type === "separator") {
|
|
94
|
+
return {
|
|
95
|
+
key: item !== "separator" && item.key || `separator-${index}`,
|
|
96
|
+
type: "separator"
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (resolveAsBoolean(item.hidden, context, false) || !resolveAsBoolean(item.visible, context, true)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const label = resolveAsValue(item.label, context, "");
|
|
103
|
+
if (typeof label !== "string" && typeof label !== "number" || !String(label).trim()) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const handler = item.onSelect ?? item.onClick ?? null;
|
|
107
|
+
const shortcut = resolveAsValue(item.shortcut, context, null);
|
|
108
|
+
return {
|
|
109
|
+
...item,
|
|
110
|
+
className: resolveAsValue(item.className, context, "") || "",
|
|
111
|
+
closeOnSelect: item.closeOnSelect !== false,
|
|
112
|
+
danger: resolveAsBoolean(item.danger, context, false),
|
|
113
|
+
disabled: resolveAsBoolean(item.disabled, context, false),
|
|
114
|
+
icon: resolveAsValue(item.icon, context, null) || null,
|
|
115
|
+
itemIconClassName: resolveAsValue(item.itemIconClassName, context, "") || "",
|
|
116
|
+
key: item.key || `item-${index}`,
|
|
117
|
+
label: String(label),
|
|
118
|
+
onClick: handler,
|
|
119
|
+
onSelect: handler,
|
|
120
|
+
shortcut: typeof shortcut === "string" ? shortcut : null,
|
|
121
|
+
type: "action"
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function resolveMenuItems(config, context) {
|
|
125
|
+
const rawItems = resolveAsValue(config?.items, context, []);
|
|
126
|
+
const items = [];
|
|
127
|
+
toArray(rawItems).forEach((raw, index) => {
|
|
128
|
+
const item = resolveItem(raw, index, context);
|
|
129
|
+
if (!item) return;
|
|
130
|
+
const previous = items[items.length - 1];
|
|
131
|
+
if (item.type === "separator" && (!previous || previous.type === "separator")) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
items.push(item);
|
|
135
|
+
});
|
|
136
|
+
if (items[items.length - 1]?.type === "separator") items.pop();
|
|
137
|
+
return items;
|
|
138
|
+
}
|
|
139
|
+
function resolveMenuHeader(config, context) {
|
|
140
|
+
if (config?.header === false || config?.showPageHeader === false) return null;
|
|
141
|
+
const configured = resolveAsValue(config?.header, context, null);
|
|
142
|
+
const source = isObject(configured) ? configured : isObject(context.page) ? context.page : null;
|
|
143
|
+
if (!source) return null;
|
|
144
|
+
const title = resolveAsValue(source.title, context, null);
|
|
145
|
+
const description = resolveAsValue(source.description, context, null);
|
|
146
|
+
const icon = resolveAsValue(source.icon, context, null);
|
|
147
|
+
const eyebrow = resolveAsValue(source.eyebrow, context, null);
|
|
148
|
+
if (!title && !description && !icon && !eyebrow) return null;
|
|
149
|
+
return {
|
|
150
|
+
description,
|
|
151
|
+
descriptionText: extractNodeText(description),
|
|
152
|
+
eyebrow,
|
|
153
|
+
icon,
|
|
154
|
+
title,
|
|
155
|
+
titleText: extractNodeText(title)
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/modules/context-menu/resolver.ts
|
|
160
|
+
function normalizeMenuCandidates(registryMenus) {
|
|
161
|
+
const candidates = [];
|
|
162
|
+
for (const [registryKey, rawConfig] of Object.entries(registryMenus || {})) {
|
|
163
|
+
if (!isObject(rawConfig)) continue;
|
|
164
|
+
const { menus, ...sharedConfig } = rawConfig;
|
|
165
|
+
const nestedMenus = toArray(menus);
|
|
166
|
+
const hasSharedItems = Array.isArray(sharedConfig.items) || typeof sharedConfig.items === "function";
|
|
167
|
+
if (hasSharedItems || nestedMenus.length === 0) {
|
|
168
|
+
candidates.push({
|
|
169
|
+
config: sharedConfig,
|
|
170
|
+
order: candidates.length,
|
|
171
|
+
registryKey
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
for (const menu of nestedMenus) {
|
|
175
|
+
candidates.push({
|
|
176
|
+
config: {
|
|
177
|
+
...sharedConfig,
|
|
178
|
+
...menu,
|
|
179
|
+
classNames: { ...sharedConfig.classNames, ...menu.classNames }
|
|
180
|
+
},
|
|
181
|
+
order: candidates.length,
|
|
182
|
+
registryKey
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return candidates;
|
|
187
|
+
}
|
|
188
|
+
function isPathAllowed(config, registryKey, pathname) {
|
|
189
|
+
if (!pathname) return true;
|
|
190
|
+
if (config.path === pathname) return true;
|
|
191
|
+
const explicitPaths = toArray(config.paths).filter(Boolean);
|
|
192
|
+
if (explicitPaths.length > 0) return explicitPaths.includes(pathname);
|
|
193
|
+
if (typeof config.pathMatcher === "function") {
|
|
194
|
+
try {
|
|
195
|
+
return Boolean(config.pathMatcher(pathname));
|
|
196
|
+
} catch {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return registryKey === pathname || registryKey === CURRENT_PAGE_KEY || registryKey === GLOBAL_MENU_KEY;
|
|
201
|
+
}
|
|
202
|
+
function getElementDepth(from, ancestor) {
|
|
203
|
+
let depth = 0;
|
|
204
|
+
for (let node = from; node && node !== ancestor; node = node.parentElement) {
|
|
205
|
+
depth += 1;
|
|
206
|
+
}
|
|
207
|
+
return depth;
|
|
208
|
+
}
|
|
209
|
+
function getTargetScore(config, targetElement) {
|
|
210
|
+
const selectors = toArray(config.target).filter(
|
|
211
|
+
(selector) => typeof selector === "string" && selector.trim()
|
|
212
|
+
);
|
|
213
|
+
if (selectors.length === 0) return 0;
|
|
214
|
+
if (!targetElement) return null;
|
|
215
|
+
let minDepth = Infinity;
|
|
216
|
+
for (const selector of selectors) {
|
|
217
|
+
try {
|
|
218
|
+
const matched = targetElement.closest(selector);
|
|
219
|
+
if (matched) {
|
|
220
|
+
minDepth = Math.min(minDepth, getElementDepth(targetElement, matched));
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return minDepth === Infinity ? null : Math.max(0, 100 - minDepth);
|
|
226
|
+
}
|
|
227
|
+
function getRouteScore(config, registryKey, pathname) {
|
|
228
|
+
if (!pathname) return 0;
|
|
229
|
+
if (config.path === pathname || toArray(config.paths).includes(pathname) || registryKey === pathname) {
|
|
230
|
+
return 100;
|
|
231
|
+
}
|
|
232
|
+
if (registryKey === CURRENT_PAGE_KEY) return 70;
|
|
233
|
+
if (registryKey === GLOBAL_MENU_KEY) return 40;
|
|
234
|
+
return 10;
|
|
235
|
+
}
|
|
236
|
+
function buildMenuContext(config, event, pathname, targetElement) {
|
|
237
|
+
let context = {
|
|
238
|
+
currentTarget: event.currentTarget ?? null,
|
|
239
|
+
event,
|
|
240
|
+
pathname: pathname || "",
|
|
241
|
+
point: { x: Number(event.clientX) || 0, y: Number(event.clientY) || 0 },
|
|
242
|
+
target: targetElement
|
|
243
|
+
};
|
|
244
|
+
if (config.payload !== void 0) context.payload = config.payload;
|
|
245
|
+
try {
|
|
246
|
+
const payload = config.resolvePayload?.(event, context);
|
|
247
|
+
if (payload !== void 0) context.payload = payload;
|
|
248
|
+
} catch {
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
const extra = config.resolveContext?.(event, context);
|
|
252
|
+
if (isObject(extra)) context = { ...context, ...extra };
|
|
253
|
+
} catch {
|
|
254
|
+
}
|
|
255
|
+
return context;
|
|
256
|
+
}
|
|
257
|
+
function isMenuOverlayTarget(element) {
|
|
258
|
+
return Boolean(
|
|
259
|
+
element?.closest?.(
|
|
260
|
+
'[data-context-menu-ignore], [data-context-menu-overlay], [role="menu"]'
|
|
261
|
+
) || element?.classList?.contains("context-menu-overlay")
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
function resolveEventTarget(event) {
|
|
265
|
+
const initialTarget = event.target instanceof Element ? event.target : null;
|
|
266
|
+
if (initialTarget && !isMenuOverlayTarget(initialTarget)) {
|
|
267
|
+
return initialTarget;
|
|
268
|
+
}
|
|
269
|
+
if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) {
|
|
270
|
+
return initialTarget;
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
const underneath = document.elementsFromPoint(event.clientX, event.clientY).find((element) => !isMenuOverlayTarget(element));
|
|
274
|
+
return underneath || initialTarget;
|
|
275
|
+
} catch {
|
|
276
|
+
return initialTarget;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function passesWhen(config, event, info) {
|
|
280
|
+
if (typeof config.when !== "function") return config.when !== false;
|
|
281
|
+
try {
|
|
282
|
+
return Boolean(config.when(event, info));
|
|
283
|
+
} catch {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function resolveContextMenu(registryMenus, pathname, event) {
|
|
288
|
+
const targetElement = resolveEventTarget(event);
|
|
289
|
+
let winner = null;
|
|
290
|
+
for (const { config, order, registryKey } of normalizeMenuCandidates(
|
|
291
|
+
registryMenus
|
|
292
|
+
)) {
|
|
293
|
+
if (!isPathAllowed(config, registryKey, pathname)) continue;
|
|
294
|
+
const context = buildMenuContext(config, event, pathname, targetElement);
|
|
295
|
+
if (!resolveAsBoolean(config.enabled, context, true)) continue;
|
|
296
|
+
if (!passesWhen(config, event, {
|
|
297
|
+
context,
|
|
298
|
+
pathname: pathname || "",
|
|
299
|
+
target: targetElement
|
|
300
|
+
})) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const items = resolveMenuItems(config, context);
|
|
304
|
+
if (!items.length) continue;
|
|
305
|
+
const targetScore = getTargetScore(config, targetElement);
|
|
306
|
+
if (targetScore === null) continue;
|
|
307
|
+
const priority = Number(config.priority);
|
|
308
|
+
const score = (Number.isFinite(priority) ? priority : 0) * 1e4 + getRouteScore(config, registryKey, pathname) * 100 + targetScore;
|
|
309
|
+
if (!winner || score > winner.score) {
|
|
310
|
+
winner = { config, context, items, order, score };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return winner;
|
|
314
|
+
}
|
|
315
|
+
function prepareMenu(config, context, event) {
|
|
316
|
+
const onOpenResult = safeInvoke(config.onOpen, event, context);
|
|
317
|
+
if (onOpenResult === false) return null;
|
|
318
|
+
const nextContext = isObject(onOpenResult) ? { ...context, ...onOpenResult } : context;
|
|
319
|
+
const items = resolveMenuItems(config, nextContext);
|
|
320
|
+
return items.length ? { context: nextContext, items } : null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/modules/context-menu/state.ts
|
|
324
|
+
function createInitialMenuState() {
|
|
325
|
+
return {
|
|
326
|
+
position: INITIAL_POSITION,
|
|
327
|
+
context: null,
|
|
328
|
+
isOpen: false,
|
|
329
|
+
config: null,
|
|
330
|
+
items: []
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function resolveNextOpenState({
|
|
334
|
+
config,
|
|
335
|
+
context,
|
|
336
|
+
items,
|
|
337
|
+
position
|
|
338
|
+
}) {
|
|
339
|
+
if (!config) return null;
|
|
340
|
+
return {
|
|
341
|
+
config,
|
|
342
|
+
context: context ?? null,
|
|
343
|
+
isOpen: true,
|
|
344
|
+
items: items ?? [],
|
|
345
|
+
position: {
|
|
346
|
+
x: Math.round(toFiniteNumber(position?.x, 0)),
|
|
347
|
+
y: Math.round(toFiniteNumber(position?.y, 0))
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function emitContextMenuVisibility(isOpen) {
|
|
352
|
+
if (!isBrowser) return;
|
|
353
|
+
window.dispatchEvent(
|
|
354
|
+
new CustomEvent(CONTEXT_MENU_VISIBILITY_EVENT, {
|
|
355
|
+
detail: { isOpen: Boolean(isOpen) }
|
|
356
|
+
})
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
var ContextMenuContext = getOrCreateGlobalContext(
|
|
360
|
+
"ContextMenuContext",
|
|
361
|
+
null
|
|
362
|
+
);
|
|
363
|
+
function selectIsOpen(state) {
|
|
364
|
+
return state.isOpen;
|
|
365
|
+
}
|
|
366
|
+
function ContextMenuProvider({ children }) {
|
|
367
|
+
const pathname = usePathname();
|
|
368
|
+
const [store] = useState(
|
|
369
|
+
() => createStore(createInitialMenuState(), {
|
|
370
|
+
freezeSnapshots: false
|
|
371
|
+
})
|
|
372
|
+
);
|
|
373
|
+
const isOpen = useStore(store, selectIsOpen);
|
|
374
|
+
const pathnameRef = useRef(pathname);
|
|
375
|
+
useIsomorphicLayoutEffect(() => {
|
|
376
|
+
pathnameRef.current = pathname;
|
|
377
|
+
}, [pathname]);
|
|
378
|
+
const actions = useMemo(() => {
|
|
379
|
+
const openMenu = (input) => {
|
|
380
|
+
const nextState = resolveNextOpenState(input);
|
|
381
|
+
if (nextState) store.publish(nextState);
|
|
382
|
+
};
|
|
383
|
+
const closeMenu = () => {
|
|
384
|
+
const current = store.getSnapshot();
|
|
385
|
+
if (!current.isOpen) return;
|
|
386
|
+
store.publish(createInitialMenuState());
|
|
387
|
+
safeInvoke(current.config?.onClose, current.context);
|
|
388
|
+
};
|
|
389
|
+
const bind = (payload, configOverride) => ({
|
|
390
|
+
onContextMenu: (event) => {
|
|
391
|
+
const config = configOverride || store.getSnapshot().config;
|
|
392
|
+
if (!config) return;
|
|
393
|
+
event.preventDefault?.();
|
|
394
|
+
event.stopPropagation?.();
|
|
395
|
+
const point = {
|
|
396
|
+
x: Number(event.clientX ?? 0),
|
|
397
|
+
y: Number(event.clientY ?? 0)
|
|
398
|
+
};
|
|
399
|
+
const context = {
|
|
400
|
+
currentTarget: event.currentTarget ?? null,
|
|
401
|
+
event,
|
|
402
|
+
pathname: pathnameRef.current || "",
|
|
403
|
+
payload,
|
|
404
|
+
point,
|
|
405
|
+
target: event.target ?? null
|
|
406
|
+
};
|
|
407
|
+
const prepared = prepareMenu(config, context, event);
|
|
408
|
+
if (!prepared) return;
|
|
409
|
+
openMenu({
|
|
410
|
+
config,
|
|
411
|
+
context: prepared.context,
|
|
412
|
+
items: prepared.items,
|
|
413
|
+
position: point
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
return { bind, closeMenu, openMenu };
|
|
418
|
+
}, [store]);
|
|
419
|
+
const contextValue = useMemo(
|
|
420
|
+
() => ({ actions, store }),
|
|
421
|
+
[actions, store]
|
|
422
|
+
);
|
|
423
|
+
useEffect(() => emitContextMenuVisibility(isOpen), [isOpen]);
|
|
424
|
+
return /* @__PURE__ */ jsx(ContextMenuContext, { value: contextValue, children });
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/modules/context-menu/dom.ts
|
|
428
|
+
function joinClassNames(...classes) {
|
|
429
|
+
return classes.filter(Boolean).join(" ");
|
|
430
|
+
}
|
|
431
|
+
var SCROLL_LOCK_KEYS = /* @__PURE__ */ new Set([
|
|
432
|
+
"ArrowDown",
|
|
433
|
+
"ArrowUp",
|
|
434
|
+
"End",
|
|
435
|
+
"Home",
|
|
436
|
+
"PageDown",
|
|
437
|
+
"PageUp",
|
|
438
|
+
" ",
|
|
439
|
+
"Spacebar"
|
|
440
|
+
]);
|
|
441
|
+
function isScrollLockKey(event) {
|
|
442
|
+
return SCROLL_LOCK_KEYS.has(event.key);
|
|
443
|
+
}
|
|
444
|
+
function positionMenu(menuElement, position) {
|
|
445
|
+
if (!menuElement) return;
|
|
446
|
+
const { height, width } = menuElement.getBoundingClientRect();
|
|
447
|
+
const maxX = window.innerWidth - width - MENU_SCREEN_MARGIN;
|
|
448
|
+
const maxY = window.innerHeight - height - MENU_SCREEN_MARGIN;
|
|
449
|
+
const x = Math.min(Number(position?.x) || 0, maxX);
|
|
450
|
+
const y = Math.min(Number(position?.y) || 0, maxY);
|
|
451
|
+
menuElement.style.left = `${Math.round(Math.max(MENU_SCREEN_MARGIN, x))}px`;
|
|
452
|
+
menuElement.style.top = `${Math.round(Math.max(MENU_SCREEN_MARGIN, y))}px`;
|
|
453
|
+
}
|
|
454
|
+
function getNextActiveIndex(items, current, direction) {
|
|
455
|
+
const count = items.length;
|
|
456
|
+
const start = current < 0 ? direction === 1 ? 0 : count - 1 : current + direction;
|
|
457
|
+
for (let step = 0; step < count; step++) {
|
|
458
|
+
const index = ((start + step * direction) % count + count) % count;
|
|
459
|
+
if (items[index].type === "action" && !items[index].disabled) return index;
|
|
460
|
+
}
|
|
461
|
+
return current;
|
|
462
|
+
}
|
|
463
|
+
function useContextMenuRegistration(config, options) {
|
|
464
|
+
useModuleRegistration("contextMenu", config, options);
|
|
465
|
+
}
|
|
466
|
+
function useContextMenu(config, options) {
|
|
467
|
+
useContextMenuRegistration(config || null, options);
|
|
468
|
+
const { actions, store } = useRequiredContext(
|
|
469
|
+
ContextMenuContext,
|
|
470
|
+
"useContextMenu",
|
|
471
|
+
"ContextMenuProvider"
|
|
472
|
+
);
|
|
473
|
+
const state = useStore(store);
|
|
474
|
+
const ctx = useMemo(
|
|
475
|
+
() => ({ ...state, ...actions }),
|
|
476
|
+
[actions, state]
|
|
477
|
+
);
|
|
478
|
+
return useMemo(() => {
|
|
479
|
+
if (!config) return ctx;
|
|
480
|
+
return {
|
|
481
|
+
...ctx,
|
|
482
|
+
bind: (payload, configOverride) => ctx.bind(payload, { ...config, ...configOverride })
|
|
483
|
+
};
|
|
484
|
+
}, [config, ctx]);
|
|
485
|
+
}
|
|
486
|
+
function useContextMenuActions() {
|
|
487
|
+
return useRequiredContext(
|
|
488
|
+
ContextMenuContext,
|
|
489
|
+
"useContextMenuActions",
|
|
490
|
+
"ContextMenuProvider"
|
|
491
|
+
).actions;
|
|
492
|
+
}
|
|
493
|
+
function useContextMenuState() {
|
|
494
|
+
const { store } = useRequiredContext(
|
|
495
|
+
ContextMenuContext,
|
|
496
|
+
"useContextMenuState",
|
|
497
|
+
"ContextMenuProvider"
|
|
498
|
+
);
|
|
499
|
+
return useStore(store);
|
|
500
|
+
}
|
|
501
|
+
function selectDockPageCard(dock) {
|
|
502
|
+
const card = dock.activeItem;
|
|
503
|
+
return card && !card.isSurface ? card : null;
|
|
504
|
+
}
|
|
505
|
+
function useContextMenuListener() {
|
|
506
|
+
const menus = useRegistryEntries(
|
|
507
|
+
"contextMenu"
|
|
508
|
+
);
|
|
509
|
+
const dockCard = useOptionalDockState(selectDockPageCard);
|
|
510
|
+
const { openMenu } = useContextMenuActions();
|
|
511
|
+
const pathname = usePathname();
|
|
512
|
+
useEffect(() => {
|
|
513
|
+
const handleContextMenu = (event) => {
|
|
514
|
+
const match = resolveContextMenu(menus, pathname || void 0, event);
|
|
515
|
+
if (!match) return;
|
|
516
|
+
event.preventDefault();
|
|
517
|
+
event.stopPropagation();
|
|
518
|
+
const page = resolveContextMenuPageMeta(dockCard, pathname || "");
|
|
519
|
+
const prepared = prepareMenu(
|
|
520
|
+
match.config,
|
|
521
|
+
page ? { ...match.context, page } : match.context,
|
|
522
|
+
event
|
|
523
|
+
);
|
|
524
|
+
if (!prepared) return;
|
|
525
|
+
openMenu({
|
|
526
|
+
config: match.config,
|
|
527
|
+
context: prepared.context,
|
|
528
|
+
items: prepared.items,
|
|
529
|
+
position: { x: event.clientX, y: event.clientY }
|
|
530
|
+
});
|
|
531
|
+
};
|
|
532
|
+
document.addEventListener("contextmenu", handleContextMenu, true);
|
|
533
|
+
return () => document.removeEventListener("contextmenu", handleContextMenu, true);
|
|
534
|
+
}, [dockCard, menus, openMenu, pathname]);
|
|
535
|
+
}
|
|
536
|
+
function useMenuDismissal(menuRef, onClose) {
|
|
537
|
+
useEffect(() => {
|
|
538
|
+
const preventScroll = (event) => event.preventDefault();
|
|
539
|
+
const listenerOptions = { capture: true, passive: false };
|
|
540
|
+
const handleInteraction = (event) => {
|
|
541
|
+
const isInside = menuRef.current?.contains(event.target);
|
|
542
|
+
if (event.type === "keydown") {
|
|
543
|
+
if (event.key === "Escape") onClose();
|
|
544
|
+
if (!isInside && isScrollLockKey(event)) {
|
|
545
|
+
event.preventDefault();
|
|
546
|
+
}
|
|
547
|
+
} else if (!isInside) {
|
|
548
|
+
onClose();
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
document.addEventListener("mousedown", handleInteraction, true);
|
|
552
|
+
document.addEventListener("keydown", handleInteraction, true);
|
|
553
|
+
window.addEventListener("wheel", preventScroll, listenerOptions);
|
|
554
|
+
window.addEventListener("touchmove", preventScroll, listenerOptions);
|
|
555
|
+
return () => {
|
|
556
|
+
document.removeEventListener("mousedown", handleInteraction, true);
|
|
557
|
+
document.removeEventListener("keydown", handleInteraction, true);
|
|
558
|
+
window.removeEventListener("wheel", preventScroll, true);
|
|
559
|
+
window.removeEventListener("touchmove", preventScroll, true);
|
|
560
|
+
};
|
|
561
|
+
}, [menuRef, onClose]);
|
|
562
|
+
}
|
|
563
|
+
function useContextMenuContentModel({
|
|
564
|
+
config,
|
|
565
|
+
items,
|
|
566
|
+
menuContext,
|
|
567
|
+
onClose,
|
|
568
|
+
position
|
|
569
|
+
}) {
|
|
570
|
+
const menuRef = useRef(null);
|
|
571
|
+
const itemRefs = useRef([]);
|
|
572
|
+
const theme = useTheme(contextMenuTheme);
|
|
573
|
+
const classes = useMemo(() => {
|
|
574
|
+
const overrides = config.classNames ?? {};
|
|
575
|
+
const merged = { ...theme.slots };
|
|
576
|
+
for (const slot of Object.keys(merged)) {
|
|
577
|
+
merged[slot] = joinClassNames(theme.slots[slot], overrides[slot]);
|
|
578
|
+
}
|
|
579
|
+
return merged;
|
|
580
|
+
}, [config.classNames, theme.slots]);
|
|
581
|
+
const header = useMemo(
|
|
582
|
+
() => resolveMenuHeader(config, menuContext),
|
|
583
|
+
[config, menuContext]
|
|
584
|
+
);
|
|
585
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
586
|
+
const [prevItems, setPrevItems] = useState(items);
|
|
587
|
+
if (items !== prevItems) {
|
|
588
|
+
setPrevItems(items);
|
|
589
|
+
setActiveIndex(-1);
|
|
590
|
+
}
|
|
591
|
+
useLayoutEffect(() => {
|
|
592
|
+
itemRefs.current = [];
|
|
593
|
+
positionMenu(menuRef.current, position);
|
|
594
|
+
}, [header, items, position]);
|
|
595
|
+
useEffect(() => {
|
|
596
|
+
const target = activeIndex < 0 ? menuRef.current : itemRefs.current[activeIndex];
|
|
597
|
+
target?.focus({ preventScroll: true });
|
|
598
|
+
}, [activeIndex]);
|
|
599
|
+
useMenuDismissal(menuRef, onClose);
|
|
600
|
+
const handleItemSelect = useCallback(
|
|
601
|
+
(item, event) => {
|
|
602
|
+
event.preventDefault();
|
|
603
|
+
event.stopPropagation();
|
|
604
|
+
if (item.disabled) return;
|
|
605
|
+
safeInvoke(item.onSelect || item.onClick, event, menuContext);
|
|
606
|
+
if (item.closeOnSelect !== false) onClose();
|
|
607
|
+
},
|
|
608
|
+
[menuContext, onClose]
|
|
609
|
+
);
|
|
610
|
+
const handleMenuKeyDown = (event) => {
|
|
611
|
+
if (event.key === "Escape") {
|
|
612
|
+
event.preventDefault();
|
|
613
|
+
onClose();
|
|
614
|
+
} else if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
615
|
+
event.preventDefault();
|
|
616
|
+
const direction = event.key === "ArrowDown" ? 1 : -1;
|
|
617
|
+
setActiveIndex(
|
|
618
|
+
(current) => getNextActiveIndex(items, current, direction)
|
|
619
|
+
);
|
|
620
|
+
} else if ((event.key === "Enter" || event.key === " ") && items[activeIndex]) {
|
|
621
|
+
event.preventDefault();
|
|
622
|
+
handleItemSelect(items[activeIndex], event);
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
return {
|
|
626
|
+
menuRef,
|
|
627
|
+
itemRefs,
|
|
628
|
+
classes,
|
|
629
|
+
styles: theme.styles,
|
|
630
|
+
header,
|
|
631
|
+
activeIndex,
|
|
632
|
+
setActiveIndex,
|
|
633
|
+
handleItemSelect,
|
|
634
|
+
handleMenuKeyDown
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/modules/context-menu/motion.ts
|
|
639
|
+
function useContextMenuMotion() {
|
|
640
|
+
return useModuleMotion(contextMenuTheme);
|
|
641
|
+
}
|
|
642
|
+
function ContextMenuHeaderIcon({
|
|
643
|
+
classes,
|
|
644
|
+
icon
|
|
645
|
+
}) {
|
|
646
|
+
const iconClassName = classes.headerIcon;
|
|
647
|
+
if (isImageIconSource(icon)) {
|
|
648
|
+
return /* @__PURE__ */ jsx(
|
|
649
|
+
"div",
|
|
650
|
+
{
|
|
651
|
+
className: iconClassName,
|
|
652
|
+
style: { backgroundImage: `url(${icon})` }
|
|
653
|
+
}
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
return /* @__PURE__ */ jsx("div", { className: iconClassName, children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { icon, size: 20 }) : icon });
|
|
657
|
+
}
|
|
658
|
+
function ContextMenuHeader({
|
|
659
|
+
classes,
|
|
660
|
+
header
|
|
661
|
+
}) {
|
|
662
|
+
if (!header) return null;
|
|
663
|
+
return /* @__PURE__ */ jsxs("div", { className: classes.header, children: [
|
|
664
|
+
header.icon && /* @__PURE__ */ jsx(ContextMenuHeaderIcon, { classes, icon: header.icon }),
|
|
665
|
+
/* @__PURE__ */ jsxs("div", { className: classes.headerText, children: [
|
|
666
|
+
header.eyebrow && /* @__PURE__ */ jsx("div", { className: classes.headerEyebrow, children: header.eyebrow }),
|
|
667
|
+
header.title && /* @__PURE__ */ jsx("div", { className: classes.headerTitle, children: header.title }),
|
|
668
|
+
header.description && /* @__PURE__ */ jsx("div", { className: classes.headerDescription, children: header.description })
|
|
669
|
+
] })
|
|
670
|
+
] });
|
|
671
|
+
}
|
|
672
|
+
function ContextMenuItem({
|
|
673
|
+
classes,
|
|
674
|
+
isActive,
|
|
675
|
+
item,
|
|
676
|
+
onHover,
|
|
677
|
+
onSelect,
|
|
678
|
+
setButtonRef
|
|
679
|
+
}) {
|
|
680
|
+
const menuMotion = useContextMenuMotion();
|
|
681
|
+
if (item.type === "separator") {
|
|
682
|
+
return /* @__PURE__ */ jsx("div", { className: classes.separator, role: "separator" });
|
|
683
|
+
}
|
|
684
|
+
const itemClassName = joinClassNames(classes.item, item.className);
|
|
685
|
+
const itemIconClassName = joinClassNames(
|
|
686
|
+
classes.itemIcon,
|
|
687
|
+
item.itemIconClassName
|
|
688
|
+
);
|
|
689
|
+
return /* @__PURE__ */ jsxs(
|
|
690
|
+
motion.button,
|
|
691
|
+
{
|
|
692
|
+
ref: setButtonRef,
|
|
693
|
+
className: itemClassName,
|
|
694
|
+
"data-active": isActive ? "true" : void 0,
|
|
695
|
+
"aria-disabled": item.disabled,
|
|
696
|
+
disabled: item.disabled,
|
|
697
|
+
role: "menuitem",
|
|
698
|
+
type: "button",
|
|
699
|
+
whileTap: menuMotion.itemTap,
|
|
700
|
+
transition: menuMotion.itemTransition,
|
|
701
|
+
onMouseEnter: onHover,
|
|
702
|
+
onClick: (event) => onSelect(item, event),
|
|
703
|
+
children: [
|
|
704
|
+
item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { icon: item.icon, className: itemIconClassName, size: 18 }) : item.icon),
|
|
705
|
+
/* @__PURE__ */ jsx("span", { className: classes.itemLabel, children: item.label }),
|
|
706
|
+
item.shortcut && /* @__PURE__ */ jsx("span", { className: classes.itemShortcut, children: item.shortcut })
|
|
707
|
+
]
|
|
708
|
+
}
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
var EMPTY_MENU_CONTEXT = Object.freeze({
|
|
712
|
+
pathname: "",
|
|
713
|
+
point: { x: 0, y: 0 }
|
|
714
|
+
});
|
|
715
|
+
function ContextMenuContent({
|
|
716
|
+
config,
|
|
717
|
+
items,
|
|
718
|
+
menuContext,
|
|
719
|
+
onClose,
|
|
720
|
+
position
|
|
721
|
+
}) {
|
|
722
|
+
const {
|
|
723
|
+
menuRef,
|
|
724
|
+
itemRefs,
|
|
725
|
+
classes,
|
|
726
|
+
styles,
|
|
727
|
+
header,
|
|
728
|
+
activeIndex,
|
|
729
|
+
setActiveIndex,
|
|
730
|
+
handleItemSelect,
|
|
731
|
+
handleMenuKeyDown
|
|
732
|
+
} = useContextMenuContentModel({
|
|
733
|
+
config,
|
|
734
|
+
items,
|
|
735
|
+
menuContext,
|
|
736
|
+
onClose,
|
|
737
|
+
position
|
|
738
|
+
});
|
|
739
|
+
const menuMotion = useContextMenuMotion();
|
|
740
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
741
|
+
/* @__PURE__ */ jsx(
|
|
742
|
+
"div",
|
|
743
|
+
{
|
|
744
|
+
"data-context-menu-overlay": true,
|
|
745
|
+
className: classes.backdrop,
|
|
746
|
+
onMouseDown: onClose,
|
|
747
|
+
style: styles.backdrop
|
|
748
|
+
}
|
|
749
|
+
),
|
|
750
|
+
/* @__PURE__ */ jsxs(
|
|
751
|
+
motion.div,
|
|
752
|
+
{
|
|
753
|
+
ref: menuRef,
|
|
754
|
+
"data-context-menu-ignore": true,
|
|
755
|
+
variants: menuMotion.pop,
|
|
756
|
+
initial: "hidden",
|
|
757
|
+
animate: "visible",
|
|
758
|
+
exit: "exit",
|
|
759
|
+
className: classes.menu,
|
|
760
|
+
role: "menu",
|
|
761
|
+
tabIndex: -1,
|
|
762
|
+
style: {
|
|
763
|
+
...styles.menu,
|
|
764
|
+
left: position.x || 0,
|
|
765
|
+
top: position.y || 0,
|
|
766
|
+
position: "fixed"
|
|
767
|
+
},
|
|
768
|
+
onMouseLeave: () => setActiveIndex(-1),
|
|
769
|
+
onKeyDown: handleMenuKeyDown,
|
|
770
|
+
children: [
|
|
771
|
+
/* @__PURE__ */ jsx(
|
|
772
|
+
motion.div,
|
|
773
|
+
{
|
|
774
|
+
variants: menuMotion.content,
|
|
775
|
+
initial: "hidden",
|
|
776
|
+
animate: "visible",
|
|
777
|
+
children: /* @__PURE__ */ jsx(ContextMenuHeader, { classes, header })
|
|
778
|
+
}
|
|
779
|
+
),
|
|
780
|
+
items.map((item, index) => /* @__PURE__ */ jsx(
|
|
781
|
+
motion.div,
|
|
782
|
+
{
|
|
783
|
+
variants: menuMotion.item,
|
|
784
|
+
custom: index,
|
|
785
|
+
initial: "hidden",
|
|
786
|
+
animate: "visible",
|
|
787
|
+
children: /* @__PURE__ */ jsx(
|
|
788
|
+
ContextMenuItem,
|
|
789
|
+
{
|
|
790
|
+
item,
|
|
791
|
+
classes,
|
|
792
|
+
isActive: index === activeIndex,
|
|
793
|
+
onHover: () => {
|
|
794
|
+
if (item.type === "action" && !item.disabled) {
|
|
795
|
+
setActiveIndex(index);
|
|
796
|
+
}
|
|
797
|
+
},
|
|
798
|
+
setButtonRef: (node) => {
|
|
799
|
+
itemRefs.current[index] = node;
|
|
800
|
+
},
|
|
801
|
+
onSelect: handleItemSelect
|
|
802
|
+
}
|
|
803
|
+
)
|
|
804
|
+
},
|
|
805
|
+
item.key || `menu-item-${index}`
|
|
806
|
+
))
|
|
807
|
+
]
|
|
808
|
+
}
|
|
809
|
+
)
|
|
810
|
+
] });
|
|
811
|
+
}
|
|
812
|
+
var emptySubscribe = () => () => {
|
|
813
|
+
};
|
|
814
|
+
function ContextMenuRenderer() {
|
|
815
|
+
const { closeMenu, config, context, isOpen, items, position } = useContextMenu();
|
|
816
|
+
const isMounted = useSyncExternalStore(
|
|
817
|
+
emptySubscribe,
|
|
818
|
+
() => true,
|
|
819
|
+
() => false
|
|
820
|
+
);
|
|
821
|
+
if (!isMounted) return null;
|
|
822
|
+
const menuContext = context ?? EMPTY_MENU_CONTEXT;
|
|
823
|
+
const resolvedItems = items.length ? items : resolveMenuItems(config, menuContext);
|
|
824
|
+
return createPortal(
|
|
825
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: isOpen && config && resolvedItems.length > 0 && /* @__PURE__ */ jsx(
|
|
826
|
+
ContextMenuContent,
|
|
827
|
+
{
|
|
828
|
+
config,
|
|
829
|
+
items: resolvedItems,
|
|
830
|
+
menuContext,
|
|
831
|
+
position,
|
|
832
|
+
onClose: closeMenu
|
|
833
|
+
},
|
|
834
|
+
"context-menu-content"
|
|
835
|
+
) }),
|
|
836
|
+
document.body
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
function ContextMenuGlobal() {
|
|
840
|
+
useContextMenuListener();
|
|
841
|
+
return /* @__PURE__ */ jsx(ContextMenuRenderer, {});
|
|
842
|
+
}
|
|
843
|
+
function selectPageContextMenu(config) {
|
|
844
|
+
return config.contextMenu ?? null;
|
|
845
|
+
}
|
|
846
|
+
function useContextMenuPage(_slice, page) {
|
|
847
|
+
const actions = useContextMenuActions();
|
|
848
|
+
return useMemo(
|
|
849
|
+
() => ({
|
|
850
|
+
...actions,
|
|
851
|
+
set: (contextMenu) => page.set({ contextMenu })
|
|
852
|
+
}),
|
|
853
|
+
[actions, page]
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
var contextMenuModule = defineModule({
|
|
857
|
+
id: "contextMenu",
|
|
858
|
+
context: ContextMenuContext,
|
|
859
|
+
Provider: ContextMenuProvider,
|
|
860
|
+
uses: ["dock"],
|
|
861
|
+
Overlay: ContextMenuGlobal,
|
|
862
|
+
registry: {
|
|
863
|
+
cleanupDelayMs: 600,
|
|
864
|
+
keyPolicy: "route",
|
|
865
|
+
lifecycle: "immediate",
|
|
866
|
+
reservedKeys: [CURRENT_PAGE_KEY, GLOBAL_MENU_KEY]
|
|
867
|
+
},
|
|
868
|
+
page: {
|
|
869
|
+
select: selectPageContextMenu,
|
|
870
|
+
entries: (slice, { pathname }) => [
|
|
871
|
+
{ key: pathname || CURRENT_PAGE_KEY, value: slice }
|
|
872
|
+
],
|
|
873
|
+
use: useContextMenuPage
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
function defineContextMenu(definition = {}) {
|
|
877
|
+
return Object.freeze({
|
|
878
|
+
config: definition,
|
|
879
|
+
id: definition.id ?? "context-menu",
|
|
880
|
+
use: function useDefinedContextMenu(options = {}) {
|
|
881
|
+
return useContextMenu({ ...definition, ...options });
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
export { CONTEXT_MENU_VISIBILITY_EVENT, CURRENT_PAGE_KEY, ContextMenuContext, ContextMenuGlobal, ContextMenuHeader, ContextMenuHeaderIcon, ContextMenuItem, ContextMenuProvider, ContextMenuRenderer, GLOBAL_MENU_KEY, INITIAL_POSITION, MENU_SCREEN_MARGIN, contextMenuModule, contextMenuTheme, createInitialMenuState, defineContextMenu, emitContextMenuVisibility, extractNodeText, getNextActiveIndex, isScrollLockKey, joinClassNames, positionMenu, prepareMenu, resolveAsBoolean, resolveAsValue, resolveContextMenu, resolveContextMenuPageMeta, resolveMenuHeader, resolveMenuItems, resolveNextOpenState, safeInvoke, useContextMenu, useContextMenuActions, useContextMenuContentModel, useContextMenuListener, useContextMenuRegistration, useContextMenuState, useMenuDismissal };
|
|
887
|
+
//# sourceMappingURL=chunk-65ZB4QTQ.js.map
|
|
888
|
+
//# sourceMappingURL=chunk-65ZB4QTQ.js.map
|