wx-svelte-menu 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/license.txt +1 -1
- package/package.json +6 -4
- package/readme.md +7 -9
- package/src/components/ActionMenu.svelte +2 -2
- package/src/components/ContextMenu.svelte +1 -1
- package/src/components/DropDownMenu.svelte +5 -5
- package/src/components/Menu.svelte +26 -16
- package/src/components/MenuBar.svelte +18 -17
- package/src/components/{MenuItem.svelte → MenuOption.svelte} +18 -22
- package/src/helpers/index.js +3 -1
- package/types/index.d.ts +74 -0
- package/whatsnew.md +14 -0
package/license.txt
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wx-svelte-menu",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Svelte menu component for creating dropdown menus, context menus, or complex menu bars",
|
|
5
5
|
"productTag": "menu",
|
|
6
6
|
"productTrial": false,
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"svelte": "src/index.js",
|
|
19
19
|
"exports": {
|
|
20
20
|
".": {
|
|
21
|
-
"svelte": "./src/index.js"
|
|
21
|
+
"svelte": "./src/index.js",
|
|
22
|
+
"types": "./types/index.d.ts"
|
|
22
23
|
},
|
|
23
24
|
"./package.json": "./package.json"
|
|
24
25
|
},
|
|
@@ -32,11 +33,12 @@
|
|
|
32
33
|
},
|
|
33
34
|
"homepage": "https://svar.dev/svelte/core/",
|
|
34
35
|
"dependencies": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
36
|
+
"@svar-ui/svelte-core": "2.3.1",
|
|
37
|
+
"@svar-ui/lib-dom": "0.9.2"
|
|
37
38
|
},
|
|
38
39
|
"files": [
|
|
39
40
|
"src",
|
|
41
|
+
"types",
|
|
40
42
|
"readme.md",
|
|
41
43
|
"whatsnew.md",
|
|
42
44
|
"license.txt"
|
package/readme.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
# SVAR Svelte Menu
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@svar-ui/svelte-menu)
|
|
6
6
|
[](https://github.com/svar-widgets/menu/blob/main/license.txt)
|
|
7
|
-
[](https://www.npmjs.com/package/@svar-ui/svelte-menuu)
|
|
8
8
|
|
|
9
9
|
</div>
|
|
10
10
|
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
|
-
|
|
18
17
|
SVAR Menu is a ready to use Svelte component for creating context and popup menus. Easily customize each menu item with text, icons, and sub-items, and control the menu's position relative to its parent element.
|
|
19
18
|
|
|
20
19
|
### How to Use
|
|
@@ -23,11 +22,10 @@ To use SVAR Svelte Menu, simply import the package and include the component in
|
|
|
23
22
|
|
|
24
23
|
```svelte
|
|
25
24
|
<script>
|
|
26
|
-
import { Menu } from "
|
|
25
|
+
import { Menu } from "@svar-ui/svelte-menu";
|
|
27
26
|
|
|
28
|
-
function onClick(
|
|
29
|
-
|
|
30
|
-
message = action ? `clicked on ${action.id}` : "closed";
|
|
27
|
+
function onClick(ev) {
|
|
28
|
+
message = ev.option ? `clicked on ${ev.option.id}` : "closed";
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
const options = [
|
|
@@ -41,7 +39,7 @@ To use SVAR Svelte Menu, simply import the package and include the component in
|
|
|
41
39
|
];
|
|
42
40
|
</script>
|
|
43
41
|
|
|
44
|
-
<Menu {options}
|
|
42
|
+
<Menu {options} onclick={onClick} />
|
|
45
43
|
```
|
|
46
44
|
|
|
47
45
|
For more details, visit the [documentation](https://docs.svar.dev/svelte/core/category/menu).
|
|
@@ -68,4 +66,4 @@ To run the test:
|
|
|
68
66
|
|
|
69
67
|
### Need Help?
|
|
70
68
|
|
|
71
|
-
Join our [community forum](https://forum.svar.dev/) to get help or post feature requests.
|
|
69
|
+
Join our [community forum](https://forum.svar.dev/) to get help or post feature requests.
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { Portal } from "
|
|
2
|
+
import { Portal } from "@svar-ui/svelte-core";
|
|
3
3
|
import Menu from "./Menu.svelte";
|
|
4
4
|
|
|
5
5
|
let { options, at = "bottom", css = "", children, onclick } = $props();
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export function show(ev) {
|
|
8
8
|
parent = ev.target;
|
|
9
9
|
ev.preventDefault();
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
|
|
12
12
|
var parent = $state(null);
|
|
13
13
|
function onClick(ev) {
|
|
14
14
|
parent = null;
|
|
15
15
|
onclick && onclick(ev);
|
|
16
16
|
}
|
|
17
|
-
function
|
|
17
|
+
function showAt(ev) {
|
|
18
18
|
let target = ev.target;
|
|
19
19
|
while (!target.dataset.menuIgnore) {
|
|
20
20
|
parent = target;
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
27
27
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
28
|
-
<span onclick={
|
|
28
|
+
<span onclick={showAt} data-menu-ignore="true">
|
|
29
29
|
{@render children?.()}
|
|
30
30
|
</span>
|
|
31
31
|
{#if parent}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import Menu from "./Menu.svelte";
|
|
3
3
|
|
|
4
|
-
import { clickOutside, calculatePosition } from "
|
|
4
|
+
import { clickOutside, calculatePosition } from "@svar-ui/lib-dom";
|
|
5
5
|
import { onMount } from "svelte";
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import MenuOption from "./MenuOption.svelte";
|
|
8
8
|
import { prepareMenuData } from "../helpers";
|
|
9
9
|
|
|
10
10
|
let {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
let self = $state();
|
|
28
28
|
let showSub = $state(false);
|
|
29
|
-
let
|
|
29
|
+
let activeOption = $state(null);
|
|
30
30
|
|
|
31
31
|
function updatePosition() {
|
|
32
32
|
const result = calculatePosition(self, parent, at, left, top);
|
|
@@ -48,7 +48,12 @@
|
|
|
48
48
|
showSub = false;
|
|
49
49
|
}
|
|
50
50
|
function cancel() {
|
|
51
|
-
|
|
51
|
+
//[deprecated] action will be deprecated in 3.0
|
|
52
|
+
onclick && onclick({ action: null, option: null });
|
|
53
|
+
}
|
|
54
|
+
function onshow(id, el) {
|
|
55
|
+
showSub = id;
|
|
56
|
+
activeOption = el;
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
const finalOptions = $derived(prepareMenuData(options));
|
|
@@ -66,18 +71,23 @@
|
|
|
66
71
|
style="position:absolute;top:{y}px;left:{x}px;width:{width};z-index:{z}"
|
|
67
72
|
onmouseleave={onLeave}
|
|
68
73
|
>
|
|
69
|
-
{#each finalOptions as
|
|
70
|
-
{#if
|
|
74
|
+
{#each finalOptions as option (option.id)}
|
|
75
|
+
{#if option.comp === "separator"}
|
|
71
76
|
<div class="wx-separator"></div>
|
|
72
77
|
{:else}
|
|
73
|
-
<
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
bind:activeItem
|
|
78
|
+
<MenuOption
|
|
79
|
+
{option}
|
|
80
|
+
{onshow}
|
|
77
81
|
onclick={ev => {
|
|
78
|
-
if (!
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
if (!option.data && !ev.defaultPrevented) {
|
|
83
|
+
//[deprecated] action will be deprecated in 3.0
|
|
84
|
+
const pack = {
|
|
85
|
+
context,
|
|
86
|
+
action: option,
|
|
87
|
+
option,
|
|
88
|
+
event: ev,
|
|
89
|
+
};
|
|
90
|
+
if (option.handler) option.handler(pack);
|
|
81
91
|
onclick && onclick(pack);
|
|
82
92
|
|
|
83
93
|
// it is a rare case when we need to stop event bubbling
|
|
@@ -87,12 +97,12 @@
|
|
|
87
97
|
}}
|
|
88
98
|
/>
|
|
89
99
|
{/if}
|
|
90
|
-
{#if
|
|
100
|
+
{#if option.data && showSub === option.id}
|
|
91
101
|
<Menu
|
|
92
102
|
{css}
|
|
93
|
-
options={
|
|
103
|
+
options={option.data}
|
|
94
104
|
at="right-overlap"
|
|
95
|
-
parent={
|
|
105
|
+
parent={activeOption}
|
|
96
106
|
{context}
|
|
97
107
|
{onclick}
|
|
98
108
|
/>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const finalOptions = $derived(prepareMenuData(options));
|
|
9
9
|
|
|
10
|
-
let active = $state();
|
|
10
|
+
let active = $state(false);
|
|
11
11
|
let menuOptions = $state([]);
|
|
12
12
|
|
|
13
13
|
function doClick(ev) {
|
|
@@ -15,23 +15,24 @@
|
|
|
15
15
|
onclick && onclick(ev);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function setMenu(ev,
|
|
19
|
-
// if the
|
|
20
|
-
if (
|
|
18
|
+
function setMenu(ev, option, trigger) {
|
|
19
|
+
// if the option has a submenu, show it and enable hover mode
|
|
20
|
+
if (option.data && option.data.length) {
|
|
21
21
|
if (active && trigger) {
|
|
22
|
-
// second click on
|
|
22
|
+
// second click on option with submenu disables hover mode
|
|
23
23
|
active = null;
|
|
24
24
|
} else {
|
|
25
|
-
menuOptions =
|
|
26
|
-
active =
|
|
27
|
-
menu.show(ev,
|
|
25
|
+
menuOptions = option.data;
|
|
26
|
+
active = option.id;
|
|
27
|
+
menu.show(ev, option);
|
|
28
28
|
}
|
|
29
29
|
} else {
|
|
30
30
|
// hide the submenu
|
|
31
31
|
menu.show(null);
|
|
32
32
|
// if it was the click action, dispatch it and end hover mode
|
|
33
33
|
if (trigger) {
|
|
34
|
-
|
|
34
|
+
//[deprecated] action will be deprecated in 3.0
|
|
35
|
+
onclick && onclick({ action: option, option });
|
|
35
36
|
active = null;
|
|
36
37
|
} else {
|
|
37
38
|
// do not remove active flag, to preserve the hover mode
|
|
@@ -40,17 +41,17 @@
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
function onHover(ev,
|
|
44
|
-
if (active) setMenu(ev,
|
|
44
|
+
function onHover(ev, option) {
|
|
45
|
+
if (active) setMenu(ev, option);
|
|
45
46
|
}
|
|
46
47
|
</script>
|
|
47
48
|
|
|
48
49
|
<div class="wx-menubar {css}">
|
|
49
|
-
{#each finalOptions as
|
|
50
|
+
{#each finalOptions as option (option.id)}
|
|
50
51
|
<button
|
|
51
|
-
class="wx-
|
|
52
|
-
onmouseenter={ev => onHover(ev,
|
|
53
|
-
onclick={ev => setMenu(ev,
|
|
52
|
+
class="wx-option {active === option.id ? 'wx-active' : ''}"
|
|
53
|
+
onmouseenter={ev => onHover(ev, option)}
|
|
54
|
+
onclick={ev => setMenu(ev, option, true)}>{option.text}</button
|
|
54
55
|
>
|
|
55
56
|
{/each}
|
|
56
57
|
</div>
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
width: fit-content;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
.wx-
|
|
73
|
+
.wx-option {
|
|
73
74
|
background-color: transparent;
|
|
74
75
|
border: none;
|
|
75
76
|
color: var(--wx-color-font);
|
|
@@ -87,7 +88,7 @@
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
.wx-active,
|
|
90
|
-
.wx-
|
|
91
|
+
.wx-option:hover {
|
|
91
92
|
background-color: var(--wx-background-alt);
|
|
92
93
|
border-radius: var(--wx-button-border-radius);
|
|
93
94
|
}
|
|
@@ -1,39 +1,35 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { getItemHandler } from "../helpers";
|
|
3
3
|
|
|
4
|
-
let {
|
|
5
|
-
item,
|
|
6
|
-
showSub = $bindable(false),
|
|
7
|
-
activeItem = $bindable(null),
|
|
8
|
-
onclick,
|
|
9
|
-
} = $props();
|
|
4
|
+
let { option, onclick, onshow } = $props();
|
|
10
5
|
|
|
6
|
+
let element;
|
|
11
7
|
function onHover() {
|
|
12
|
-
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
14
|
-
activeItem = this;
|
|
8
|
+
onshow(option.data ? option.id : false, element);
|
|
15
9
|
}
|
|
16
10
|
</script>
|
|
17
11
|
|
|
18
12
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
19
13
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
20
14
|
<div
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
bind:this={element}
|
|
16
|
+
class="wx-option {option.css || ''}"
|
|
17
|
+
data-id={option.id}
|
|
23
18
|
onmouseenter={onHover}
|
|
24
19
|
{onclick}
|
|
25
20
|
>
|
|
26
|
-
{#if
|
|
27
|
-
{#if
|
|
28
|
-
{@const SvelteComponent = getItemHandler(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
{#if
|
|
21
|
+
{#if option.icon}<i class="wx-icon {option.icon}"></i>{/if}
|
|
22
|
+
{#if option.comp}
|
|
23
|
+
{@const SvelteComponent = getItemHandler(option.comp)}
|
|
24
|
+
<!-- [deprecated] item property will be deprecated in 3.0-->
|
|
25
|
+
<SvelteComponent item={option} {option} />
|
|
26
|
+
{:else}<span class="wx-value"> {option.text} </span>{/if}
|
|
27
|
+
{#if option.subtext}<span class="wx-subtext">{option.subtext}</span>{/if}
|
|
28
|
+
{#if option.data}<i class="wx-sub-icon wxi-angle-right"></i>{/if}
|
|
33
29
|
</div>
|
|
34
30
|
|
|
35
31
|
<style>
|
|
36
|
-
.wx-
|
|
32
|
+
.wx-option {
|
|
37
33
|
display: flex;
|
|
38
34
|
align-items: center;
|
|
39
35
|
box-sizing: border-box;
|
|
@@ -47,16 +43,16 @@
|
|
|
47
43
|
cursor: pointer;
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
.wx-
|
|
46
|
+
.wx-option:hover {
|
|
51
47
|
background: var(--wx-background-alt);
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
.wx-
|
|
50
|
+
.wx-option:first-child {
|
|
55
51
|
border-top-left-radius: inherit;
|
|
56
52
|
border-top-right-radius: inherit;
|
|
57
53
|
}
|
|
58
54
|
|
|
59
|
-
.wx-
|
|
55
|
+
.wx-option:last-child {
|
|
60
56
|
border-bottom-left-radius: inherit;
|
|
61
57
|
border-bottom-right-radius: inherit;
|
|
62
58
|
}
|
package/src/helpers/index.js
CHANGED
|
@@ -30,13 +30,15 @@ export function filterMenu(data, cb) {
|
|
|
30
30
|
let uid = 1;
|
|
31
31
|
export function prepareMenuData(data) {
|
|
32
32
|
return mapData(data, a => {
|
|
33
|
+
// [deprecated] option.type to be deprecated in 3.0
|
|
34
|
+
if (a.type) a.comp = a.type;
|
|
33
35
|
return { ...a, id: a.id || uid++ };
|
|
34
36
|
});
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
const handlers = {};
|
|
38
40
|
export function getItemHandler(type) {
|
|
39
|
-
return handlers[type];
|
|
41
|
+
return handlers[type] || type;
|
|
40
42
|
}
|
|
41
43
|
export function registerMenuItem(type, handler) {
|
|
42
44
|
handlers[type] = handler;
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Component } from "svelte";
|
|
2
|
+
|
|
3
|
+
export interface IMenuOption {
|
|
4
|
+
id?: string | number;
|
|
5
|
+
text?: string;
|
|
6
|
+
subtext?: string;
|
|
7
|
+
handler?: (ev: IMenuOptionClick) => void;
|
|
8
|
+
data?: IMenuOption[];
|
|
9
|
+
css?: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
type?: string | Component<any>; // @deprecated use `comp` instead. Will be removed in v3.0
|
|
12
|
+
comp?: string | Component<any>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IMenuOptionClick {
|
|
16
|
+
context?: any;
|
|
17
|
+
action: IMenuOption; // @deprecated use `option` instead. Will be removed in v3.0
|
|
18
|
+
option: IMenuOption;
|
|
19
|
+
event?: MouseEvent;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export declare const Menu: Component<{
|
|
23
|
+
options?: IMenuOption[];
|
|
24
|
+
left?: number;
|
|
25
|
+
top?: number;
|
|
26
|
+
at?: string;
|
|
27
|
+
parent?: HTMLElement;
|
|
28
|
+
mount?: (callback: () => void) => void;
|
|
29
|
+
context?: any;
|
|
30
|
+
css?: string;
|
|
31
|
+
onclick?: (ev: IMenuOptionClick) => void;
|
|
32
|
+
}>;
|
|
33
|
+
|
|
34
|
+
export declare const MenuBar: Component<{
|
|
35
|
+
css?: string;
|
|
36
|
+
menuCss?: string;
|
|
37
|
+
options?: IMenuOption[];
|
|
38
|
+
onclick?: (ev: IMenuOptionClick) => void;
|
|
39
|
+
}>;
|
|
40
|
+
|
|
41
|
+
export declare const DropDownMenu: Component<{
|
|
42
|
+
options?: IMenuOption[];
|
|
43
|
+
at?: string;
|
|
44
|
+
css?: string;
|
|
45
|
+
children?: () => any;
|
|
46
|
+
onclick?: (ev: IMenuOptionClick) => void;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
export declare const ContextMenu: Component<{
|
|
50
|
+
options?: IMenuOption[];
|
|
51
|
+
at?: string;
|
|
52
|
+
resolver?: (item: any, event: MouseEvent) => any;
|
|
53
|
+
dataKey?: string;
|
|
54
|
+
filter?: (option: IMenuOption, item: any) => boolean;
|
|
55
|
+
css?: string;
|
|
56
|
+
children?: () => any;
|
|
57
|
+
onclick?: (ev: IMenuOptionClick) => void;
|
|
58
|
+
}>;
|
|
59
|
+
|
|
60
|
+
export declare const ActionMenu: Component<{
|
|
61
|
+
options?: IMenuOption[];
|
|
62
|
+
at?: string;
|
|
63
|
+
resolver?: (item: any, event: MouseEvent) => any;
|
|
64
|
+
dataKey?: string;
|
|
65
|
+
filter?: (option: IMenuOption, item: any) => boolean;
|
|
66
|
+
css?: string;
|
|
67
|
+
children?: () => any;
|
|
68
|
+
onclick?: (ev: IMenuOptionClick) => void;
|
|
69
|
+
}>;
|
|
70
|
+
|
|
71
|
+
export declare function registerMenuItem(
|
|
72
|
+
type: string,
|
|
73
|
+
handler: Component<{ option?: any }>
|
|
74
|
+
): void;
|
package/whatsnew.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 2.3.0
|
|
2
|
+
|
|
3
|
+
### New features
|
|
4
|
+
|
|
5
|
+
- TypeScript definitions
|
|
6
|
+
|
|
7
|
+
### Updates
|
|
8
|
+
|
|
9
|
+
API changes with backward compatibility until 3.0
|
|
10
|
+
|
|
11
|
+
- `onclick` event of all menus: `action` parameter is renamed to `option`
|
|
12
|
+
- Option `type` property is renamed to `comp`
|
|
13
|
+
- Property `item` of a custom component, registered as menu option, is renamed to `option`
|
|
14
|
+
|
|
1
15
|
## 2.2.1
|
|
2
16
|
|
|
3
17
|
### Fixes
|