@hypericon/mico 0.5.11 → 0.5.13
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/cjs/components/menu/Menu.d.ts +26 -3
- package/dist/cjs/components/menu/Menu.js +9 -0
- package/dist/cjs/components/menu/MenuButton.js +4 -0
- package/dist/cjs/components/menu/MenuSeparator.js +2 -0
- package/dist/cjs/components/menu/menu-manager.d.ts +6 -0
- package/dist/cjs/components/menu/menu-manager.js +34 -1
- package/dist/esm/components/menu/Menu.d.ts +26 -3
- package/dist/esm/components/menu/Menu.js +9 -0
- package/dist/esm/components/menu/MenuButton.js +4 -0
- package/dist/esm/components/menu/MenuSeparator.js +2 -0
- package/dist/esm/components/menu/menu-manager.d.ts +6 -0
- package/dist/esm/components/menu/menu-manager.js +34 -1
- package/package.json +1 -1
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
import m from "mithril";
|
|
2
2
|
export interface MenuOptions {
|
|
3
|
+
/** Prevent vertical flipping when menu would go off-screen at the bottom */
|
|
3
4
|
noFlipV?: boolean;
|
|
5
|
+
/** Prevent horizontal flipping when menu would go off-screen at the right edge */
|
|
4
6
|
noFlipH?: boolean;
|
|
7
|
+
/** Force the menu to flip vertically regardless of available space */
|
|
5
8
|
forceFlipV?: boolean;
|
|
9
|
+
/** Force the menu to flip horizontally regardless of available space */
|
|
6
10
|
forceFlipH?: boolean;
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Add the menu directly to the element which raised the event, rather than
|
|
13
|
+
* to the global menu container
|
|
14
|
+
*/
|
|
8
15
|
asChild?: boolean;
|
|
9
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Display the menu using an HTMLDialogElement instead of a standard div.
|
|
18
|
+
* Useful for complex stacking contexts.
|
|
19
|
+
*/
|
|
10
20
|
asDialog?: boolean;
|
|
11
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Indicates the menu is within a dialog element and should be displayed with
|
|
23
|
+
* fixed positioning relative to the dialog
|
|
24
|
+
*/
|
|
12
25
|
isWithinDialog?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Keep menu open when clicking within it. Useful for menus with toggle switches
|
|
28
|
+
* or radio buttons where users want to try multiple options without reopening the menu.
|
|
29
|
+
* The menu will still close when clicking outside or pressing Escape.
|
|
30
|
+
*
|
|
31
|
+
* N.B.: the menu will not be redrawn automatically, as it is not mounted using Mithril.
|
|
32
|
+
* Use `menuMgr.redraw()` to redraw the menu.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
keepOpenOnClick?: boolean;
|
|
13
36
|
}
|
|
14
37
|
export declare const Menu: m.Component<{
|
|
15
38
|
x: number | undefined;
|
|
@@ -58,7 +58,16 @@ exports.Menu = {
|
|
|
58
58
|
key: Date.now(),
|
|
59
59
|
class: `mico-menu ${menu_settings_1.MENU_SETTINGS.MENU_CLASS}`,
|
|
60
60
|
style: `position: ${position}; overflow-y: auto; overflow-x: hidden; ${posH} ${posV} z-index: 100; min-width: ${menu_constants_1.MENU_CONSTANTS.MIN_WIDTH_PX}px; max-width: ${maxWidthPx}px; max-height: ${maxHeightPx}px;`,
|
|
61
|
+
role: "menu",
|
|
62
|
+
"aria-orientation": "vertical",
|
|
61
63
|
tabIndex: -1,
|
|
64
|
+
onclick: (ev) => {
|
|
65
|
+
var _a;
|
|
66
|
+
// Prevent clicks within the menu from propagating when keepOpenOnClick is enabled
|
|
67
|
+
if ((_a = attrs.options) === null || _a === void 0 ? void 0 : _a.keepOpenOnClick) {
|
|
68
|
+
ev.stopPropagation();
|
|
69
|
+
}
|
|
70
|
+
},
|
|
62
71
|
onkeydown: (ev) => {
|
|
63
72
|
var _a, _b, _c, _d, _e, _f;
|
|
64
73
|
const focussableElems = Array.from((_b = (_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelectorAll("button, input, select, textarea")) !== null && _b !== void 0 ? _b : [])
|
|
@@ -14,8 +14,10 @@ exports.MenuButton = {
|
|
|
14
14
|
(0, mithril_1.default)(mithril_1.default.route.Link, {
|
|
15
15
|
class: `mico-menu-button ${(_a = attrs.class) !== null && _a !== void 0 ? _a : ""} ${menu_settings_1.MENU_SETTINGS.MENU_BUTTON_CLASS}`,
|
|
16
16
|
style: `${(_b = attrs.style) !== null && _b !== void 0 ? _b : ""} width: 100%; height: 1.5rem; padding-right: 0.5rem; display: flex; align-items: center;`,
|
|
17
|
+
role: "menuitem",
|
|
17
18
|
href: attrs.href,
|
|
18
19
|
disabled: attrs.disabled,
|
|
20
|
+
"aria-disabled": attrs.disabled ? "true" : undefined,
|
|
19
21
|
onclick: (ev) => {
|
|
20
22
|
var _a;
|
|
21
23
|
(_a = attrs.onclick) === null || _a === void 0 ? void 0 : _a.call(attrs, ev);
|
|
@@ -31,7 +33,9 @@ exports.MenuButton = {
|
|
|
31
33
|
(0, mithril_1.default)("button", {
|
|
32
34
|
class: `mico-menu-button ${(_c = attrs.class) !== null && _c !== void 0 ? _c : ""} ${menu_settings_1.MENU_SETTINGS.MENU_BUTTON_CLASS}`,
|
|
33
35
|
style: `${(_d = attrs.style) !== null && _d !== void 0 ? _d : ""} width: 100%; height: 1.5rem; padding-right: 0.5rem; display: flex; align-items: center;`,
|
|
36
|
+
role: "menuitem",
|
|
34
37
|
disabled: attrs.disabled,
|
|
38
|
+
"aria-disabled": attrs.disabled ? "true" : undefined,
|
|
35
39
|
onclick: (ev) => {
|
|
36
40
|
var _a;
|
|
37
41
|
(_a = attrs.onclick) === null || _a === void 0 ? void 0 : _a.call(attrs, ev);
|
|
@@ -12,6 +12,8 @@ exports.MenuSeparator = {
|
|
|
12
12
|
return (0, mithril_1.default)("", {
|
|
13
13
|
class: `mico-menu-separator ${menu_settings_1.MENU_SETTINGS.MENU_SEPARATOR_CLASS}`,
|
|
14
14
|
style: "margin: 0.25rem 0; border-bottom-width: 1px; border-bottom-style: solid;",
|
|
15
|
+
role: "separator",
|
|
16
|
+
"aria-orientation": "horizontal",
|
|
15
17
|
});
|
|
16
18
|
}
|
|
17
19
|
};
|
|
@@ -8,6 +8,7 @@ export declare class MenuManager {
|
|
|
8
8
|
private _menuVnode;
|
|
9
9
|
menuVnode$: import("rxjs").Observable<m.Vnode<any, any> | undefined>;
|
|
10
10
|
get menuVnode(): m.Vnode<any, any> | undefined;
|
|
11
|
+
private currentOptions?;
|
|
11
12
|
get isOpen(): boolean;
|
|
12
13
|
constructor();
|
|
13
14
|
/**
|
|
@@ -20,6 +21,11 @@ export declare class MenuManager {
|
|
|
20
21
|
* @param menuContents
|
|
21
22
|
*/
|
|
22
23
|
show(ev: MouseEvent | undefined, menuContents: m.ChildArrayOrPrimitive, options?: MenuOptions): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Manually redraw the currently open menu. Useful when menu content needs to update
|
|
26
|
+
* while the menu remains open (e.g., when using keepOpenOnClick with changing state).
|
|
27
|
+
*/
|
|
28
|
+
redraw(): void;
|
|
23
29
|
hide(): void;
|
|
24
30
|
/**
|
|
25
31
|
* Set the default behavior for whether menus are added as children of the element that raised the event.
|
|
@@ -29,8 +29,18 @@ class MenuManager {
|
|
|
29
29
|
this.containerEl = document.createElement("div");
|
|
30
30
|
this.containerEl.classList.add("menu-container");
|
|
31
31
|
document.body.append(this.containerEl);
|
|
32
|
-
document.addEventListener("click", () => {
|
|
32
|
+
document.addEventListener("click", (ev) => {
|
|
33
|
+
var _a;
|
|
33
34
|
if (this.isOpen) {
|
|
35
|
+
// If keepOpenOnClick is enabled, check if the click is within the menu
|
|
36
|
+
if ((_a = this.currentOptions) === null || _a === void 0 ? void 0 : _a.keepOpenOnClick) {
|
|
37
|
+
const target = ev.target;
|
|
38
|
+
const clickedInMenu = target.closest(".mico-menu");
|
|
39
|
+
if (clickedInMenu) {
|
|
40
|
+
menu_settings_1.MENU_SETTINGS.DEBUG_MENU && console.log("Click within menu, keeping open (keepOpenOnClick=true)");
|
|
41
|
+
return; // Don't close the menu
|
|
42
|
+
}
|
|
43
|
+
}
|
|
34
44
|
this.hide();
|
|
35
45
|
}
|
|
36
46
|
});
|
|
@@ -106,6 +116,7 @@ class MenuManager {
|
|
|
106
116
|
menuContents,
|
|
107
117
|
]);
|
|
108
118
|
this._menuVnode.next(menuVnode);
|
|
119
|
+
this.currentOptions = options;
|
|
109
120
|
// If the menu is to be shown as a dialog, we need to mount it to a dialog element
|
|
110
121
|
if (options === null || options === void 0 ? void 0 : options.asDialog) {
|
|
111
122
|
// this.dialogEl = document.createElement("dialog");
|
|
@@ -149,6 +160,27 @@ class MenuManager {
|
|
|
149
160
|
menu_settings_1.MENU_SETTINGS.DEBUG_MENU && console.log("Added menu");
|
|
150
161
|
});
|
|
151
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Manually redraw the currently open menu. Useful when menu content needs to update
|
|
165
|
+
* while the menu remains open (e.g., when using keepOpenOnClick with changing state).
|
|
166
|
+
*/
|
|
167
|
+
redraw() {
|
|
168
|
+
var _a;
|
|
169
|
+
if (!this.isOpen || !this.menuVnode) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// Re-render the menu in whichever container it's currently mounted
|
|
173
|
+
if ((_a = this.currentOptions) === null || _a === void 0 ? void 0 : _a.asDialog) {
|
|
174
|
+
mithril_1.default.render(this.dialogEl, this.menuVnode);
|
|
175
|
+
}
|
|
176
|
+
else if (this.directParentWrapperEl) {
|
|
177
|
+
mithril_1.default.render(this.directParentWrapperEl, this.menuVnode);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
mithril_1.default.render(this.containerEl, this.menuVnode);
|
|
181
|
+
}
|
|
182
|
+
menu_settings_1.MENU_SETTINGS.DEBUG_MENU && console.log("Redrawn menu");
|
|
183
|
+
}
|
|
152
184
|
hide() {
|
|
153
185
|
var _a, _b;
|
|
154
186
|
this._menuVnode.next(undefined);
|
|
@@ -158,6 +190,7 @@ class MenuManager {
|
|
|
158
190
|
this.directParentEl = undefined;
|
|
159
191
|
(_b = this.dialogEl) === null || _b === void 0 ? void 0 : _b.close();
|
|
160
192
|
mithril_1.default.render(this.dialogEl, []);
|
|
193
|
+
this.currentOptions = undefined;
|
|
161
194
|
menu_settings_1.MENU_SETTINGS.DEBUG_MENU && console.log("Closed menu");
|
|
162
195
|
}
|
|
163
196
|
/**
|
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
import m from "mithril";
|
|
2
2
|
export interface MenuOptions {
|
|
3
|
+
/** Prevent vertical flipping when menu would go off-screen at the bottom */
|
|
3
4
|
noFlipV?: boolean;
|
|
5
|
+
/** Prevent horizontal flipping when menu would go off-screen at the right edge */
|
|
4
6
|
noFlipH?: boolean;
|
|
7
|
+
/** Force the menu to flip vertically regardless of available space */
|
|
5
8
|
forceFlipV?: boolean;
|
|
9
|
+
/** Force the menu to flip horizontally regardless of available space */
|
|
6
10
|
forceFlipH?: boolean;
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Add the menu directly to the element which raised the event, rather than
|
|
13
|
+
* to the global menu container
|
|
14
|
+
*/
|
|
8
15
|
asChild?: boolean;
|
|
9
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Display the menu using an HTMLDialogElement instead of a standard div.
|
|
18
|
+
* Useful for complex stacking contexts.
|
|
19
|
+
*/
|
|
10
20
|
asDialog?: boolean;
|
|
11
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Indicates the menu is within a dialog element and should be displayed with
|
|
23
|
+
* fixed positioning relative to the dialog
|
|
24
|
+
*/
|
|
12
25
|
isWithinDialog?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Keep menu open when clicking within it. Useful for menus with toggle switches
|
|
28
|
+
* or radio buttons where users want to try multiple options without reopening the menu.
|
|
29
|
+
* The menu will still close when clicking outside or pressing Escape.
|
|
30
|
+
*
|
|
31
|
+
* N.B.: the menu will not be redrawn automatically, as it is not mounted using Mithril.
|
|
32
|
+
* Use `menuMgr.redraw()` to redraw the menu.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
keepOpenOnClick?: boolean;
|
|
13
36
|
}
|
|
14
37
|
export declare const Menu: m.Component<{
|
|
15
38
|
x: number | undefined;
|
|
@@ -58,7 +58,16 @@ exports.Menu = {
|
|
|
58
58
|
key: Date.now(),
|
|
59
59
|
class: `mico-menu ${menu_settings_1.MENU_SETTINGS.MENU_CLASS}`,
|
|
60
60
|
style: `position: ${position}; overflow-y: auto; overflow-x: hidden; ${posH} ${posV} z-index: 100; min-width: ${menu_constants_1.MENU_CONSTANTS.MIN_WIDTH_PX}px; max-width: ${maxWidthPx}px; max-height: ${maxHeightPx}px;`,
|
|
61
|
+
role: "menu",
|
|
62
|
+
"aria-orientation": "vertical",
|
|
61
63
|
tabIndex: -1,
|
|
64
|
+
onclick: (ev) => {
|
|
65
|
+
var _a;
|
|
66
|
+
// Prevent clicks within the menu from propagating when keepOpenOnClick is enabled
|
|
67
|
+
if ((_a = attrs.options) === null || _a === void 0 ? void 0 : _a.keepOpenOnClick) {
|
|
68
|
+
ev.stopPropagation();
|
|
69
|
+
}
|
|
70
|
+
},
|
|
62
71
|
onkeydown: (ev) => {
|
|
63
72
|
var _a, _b, _c, _d, _e, _f;
|
|
64
73
|
const focussableElems = Array.from((_b = (_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelectorAll("button, input, select, textarea")) !== null && _b !== void 0 ? _b : [])
|
|
@@ -14,8 +14,10 @@ exports.MenuButton = {
|
|
|
14
14
|
(0, mithril_1.default)(mithril_1.default.route.Link, {
|
|
15
15
|
class: `mico-menu-button ${(_a = attrs.class) !== null && _a !== void 0 ? _a : ""} ${menu_settings_1.MENU_SETTINGS.MENU_BUTTON_CLASS}`,
|
|
16
16
|
style: `${(_b = attrs.style) !== null && _b !== void 0 ? _b : ""} width: 100%; height: 1.5rem; padding-right: 0.5rem; display: flex; align-items: center;`,
|
|
17
|
+
role: "menuitem",
|
|
17
18
|
href: attrs.href,
|
|
18
19
|
disabled: attrs.disabled,
|
|
20
|
+
"aria-disabled": attrs.disabled ? "true" : undefined,
|
|
19
21
|
onclick: (ev) => {
|
|
20
22
|
var _a;
|
|
21
23
|
(_a = attrs.onclick) === null || _a === void 0 ? void 0 : _a.call(attrs, ev);
|
|
@@ -31,7 +33,9 @@ exports.MenuButton = {
|
|
|
31
33
|
(0, mithril_1.default)("button", {
|
|
32
34
|
class: `mico-menu-button ${(_c = attrs.class) !== null && _c !== void 0 ? _c : ""} ${menu_settings_1.MENU_SETTINGS.MENU_BUTTON_CLASS}`,
|
|
33
35
|
style: `${(_d = attrs.style) !== null && _d !== void 0 ? _d : ""} width: 100%; height: 1.5rem; padding-right: 0.5rem; display: flex; align-items: center;`,
|
|
36
|
+
role: "menuitem",
|
|
34
37
|
disabled: attrs.disabled,
|
|
38
|
+
"aria-disabled": attrs.disabled ? "true" : undefined,
|
|
35
39
|
onclick: (ev) => {
|
|
36
40
|
var _a;
|
|
37
41
|
(_a = attrs.onclick) === null || _a === void 0 ? void 0 : _a.call(attrs, ev);
|
|
@@ -12,6 +12,8 @@ exports.MenuSeparator = {
|
|
|
12
12
|
return (0, mithril_1.default)("", {
|
|
13
13
|
class: `mico-menu-separator ${menu_settings_1.MENU_SETTINGS.MENU_SEPARATOR_CLASS}`,
|
|
14
14
|
style: "margin: 0.25rem 0; border-bottom-width: 1px; border-bottom-style: solid;",
|
|
15
|
+
role: "separator",
|
|
16
|
+
"aria-orientation": "horizontal",
|
|
15
17
|
});
|
|
16
18
|
}
|
|
17
19
|
};
|
|
@@ -8,6 +8,7 @@ export declare class MenuManager {
|
|
|
8
8
|
private _menuVnode;
|
|
9
9
|
menuVnode$: import("rxjs").Observable<m.Vnode<any, any> | undefined>;
|
|
10
10
|
get menuVnode(): m.Vnode<any, any> | undefined;
|
|
11
|
+
private currentOptions?;
|
|
11
12
|
get isOpen(): boolean;
|
|
12
13
|
constructor();
|
|
13
14
|
/**
|
|
@@ -20,6 +21,11 @@ export declare class MenuManager {
|
|
|
20
21
|
* @param menuContents
|
|
21
22
|
*/
|
|
22
23
|
show(ev: MouseEvent | undefined, menuContents: m.ChildArrayOrPrimitive, options?: MenuOptions): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Manually redraw the currently open menu. Useful when menu content needs to update
|
|
26
|
+
* while the menu remains open (e.g., when using keepOpenOnClick with changing state).
|
|
27
|
+
*/
|
|
28
|
+
redraw(): void;
|
|
23
29
|
hide(): void;
|
|
24
30
|
/**
|
|
25
31
|
* Set the default behavior for whether menus are added as children of the element that raised the event.
|
|
@@ -29,8 +29,18 @@ class MenuManager {
|
|
|
29
29
|
this.containerEl = document.createElement("div");
|
|
30
30
|
this.containerEl.classList.add("menu-container");
|
|
31
31
|
document.body.append(this.containerEl);
|
|
32
|
-
document.addEventListener("click", () => {
|
|
32
|
+
document.addEventListener("click", (ev) => {
|
|
33
|
+
var _a;
|
|
33
34
|
if (this.isOpen) {
|
|
35
|
+
// If keepOpenOnClick is enabled, check if the click is within the menu
|
|
36
|
+
if ((_a = this.currentOptions) === null || _a === void 0 ? void 0 : _a.keepOpenOnClick) {
|
|
37
|
+
const target = ev.target;
|
|
38
|
+
const clickedInMenu = target.closest(".mico-menu");
|
|
39
|
+
if (clickedInMenu) {
|
|
40
|
+
menu_settings_1.MENU_SETTINGS.DEBUG_MENU && console.log("Click within menu, keeping open (keepOpenOnClick=true)");
|
|
41
|
+
return; // Don't close the menu
|
|
42
|
+
}
|
|
43
|
+
}
|
|
34
44
|
this.hide();
|
|
35
45
|
}
|
|
36
46
|
});
|
|
@@ -106,6 +116,7 @@ class MenuManager {
|
|
|
106
116
|
menuContents,
|
|
107
117
|
]);
|
|
108
118
|
this._menuVnode.next(menuVnode);
|
|
119
|
+
this.currentOptions = options;
|
|
109
120
|
// If the menu is to be shown as a dialog, we need to mount it to a dialog element
|
|
110
121
|
if (options === null || options === void 0 ? void 0 : options.asDialog) {
|
|
111
122
|
// this.dialogEl = document.createElement("dialog");
|
|
@@ -149,6 +160,27 @@ class MenuManager {
|
|
|
149
160
|
menu_settings_1.MENU_SETTINGS.DEBUG_MENU && console.log("Added menu");
|
|
150
161
|
});
|
|
151
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Manually redraw the currently open menu. Useful when menu content needs to update
|
|
165
|
+
* while the menu remains open (e.g., when using keepOpenOnClick with changing state).
|
|
166
|
+
*/
|
|
167
|
+
redraw() {
|
|
168
|
+
var _a;
|
|
169
|
+
if (!this.isOpen || !this.menuVnode) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// Re-render the menu in whichever container it's currently mounted
|
|
173
|
+
if ((_a = this.currentOptions) === null || _a === void 0 ? void 0 : _a.asDialog) {
|
|
174
|
+
mithril_1.default.render(this.dialogEl, this.menuVnode);
|
|
175
|
+
}
|
|
176
|
+
else if (this.directParentWrapperEl) {
|
|
177
|
+
mithril_1.default.render(this.directParentWrapperEl, this.menuVnode);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
mithril_1.default.render(this.containerEl, this.menuVnode);
|
|
181
|
+
}
|
|
182
|
+
menu_settings_1.MENU_SETTINGS.DEBUG_MENU && console.log("Redrawn menu");
|
|
183
|
+
}
|
|
152
184
|
hide() {
|
|
153
185
|
var _a, _b;
|
|
154
186
|
this._menuVnode.next(undefined);
|
|
@@ -158,6 +190,7 @@ class MenuManager {
|
|
|
158
190
|
this.directParentEl = undefined;
|
|
159
191
|
(_b = this.dialogEl) === null || _b === void 0 ? void 0 : _b.close();
|
|
160
192
|
mithril_1.default.render(this.dialogEl, []);
|
|
193
|
+
this.currentOptions = undefined;
|
|
161
194
|
menu_settings_1.MENU_SETTINGS.DEBUG_MENU && console.log("Closed menu");
|
|
162
195
|
}
|
|
163
196
|
/**
|