@imengyu/vue3-context-menu 1.1.4 → 1.1.6

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/README.CN.md CHANGED
@@ -357,6 +357,45 @@ import '你的样式css文件路径.css'
357
357
 
358
358
  与 `ContextMenu.showContextMenu` 相同,但此函数注册到 Vue 全局属性中,可以在Vue实例中直接使用。
359
359
 
360
+ * `ContextMenu.transformMenuPosition`
361
+
362
+ 如果你的 `body` 元素处于缩放状态 (例如添加了样式 `transform: scale(0.5)`), 这可能会导致菜单显示位置不正确。
363
+ 你可以使用此函数来转换至正确的菜单显示位置:
364
+
365
+ ```ts
366
+ import ContextMenu from '@imengyu/vue3-context-menu'
367
+
368
+ function onContextMenu(e: MouseEvent) {
369
+ const scaledPosition = ContextMenu.transformMenuPosition(e.target as HTMLElement, e.offsetX, e.offsetY);
370
+ //完整示例代码位于 `/examples/views/InScaledBody.vue`
371
+ menuData.x = scaledPosition.x;
372
+ menuData.y = scaledPosition.y;
373
+ //显示菜单
374
+ ContextMenu.showContextMenu(menuData);
375
+ }
376
+ ```
377
+
378
+ #### 在函数模式下动态控制菜单
379
+
380
+ 你只需要将菜单数据声明为响应式数据,即可动态修改菜单:
381
+
382
+ ```ts
383
+ const menuData = reactive<MenuOptions>({
384
+ items: [
385
+ {
386
+ label: 'Simple item',
387
+ onClick: () => alert('Click Simple item'),
388
+ },
389
+ ]
390
+ });
391
+
392
+ ContextMenu.showContextMenu(menuData);
393
+
394
+ //可以在显示菜单后随时更改属性:
395
+ menuData.items[0].label = 'My label CHANGED!'; //更改文本
396
+ menuData.items[0].hidden = true; //更改是否隐藏
397
+ ```
398
+
360
399
  #### MenuOptions
361
400
 
362
401
  | 属性 | 描述 | 类型 | 可选值 | 默认值 |
@@ -384,6 +423,7 @@ import '你的样式css文件路径.css'
384
423
  | svgIcon | 菜单项图标 svg,仅在 icon 为空时有效 | `string` | — | — |
385
424
  | svgProps | 当使用 svg 图标时,自定义 svg 标签属性 | `SVGAttributes` | — | — |
386
425
  | disabled | 是否禁用菜单项 | `boolean` | — | `false` |
426
+ | hidden | 是否隐藏单项 | `boolean` | — | `false` |
387
427
  | adjustSubMenuPosition | 是否在子菜单超出屏幕后进行自动调整 | `boolean` | — | `true` |
388
428
  | clickableWhenHasChildren | 指定当本菜单下有子菜单时,点击当前菜单是否触发点击事件 | `boolean` | — | `false` |
389
429
  | clickClose | 点击当前菜单项是否自动关闭整个菜单 | `boolean` | — | `true` |
package/README.md CHANGED
@@ -359,6 +359,45 @@ Menu separator component.
359
359
 
360
360
  Same as `ContextMenu.showContextMenu` but this function is registered to vue global property.
361
361
 
362
+ * `ContextMenu.transformMenuPosition`
363
+
364
+ If your `body` element is in a scaled state (e.g. `transform: scale(0.5)`), this may lead to the wrong position of the menu display.
365
+ You can use this function to convert the correct menu display position:
366
+
367
+ ```ts
368
+ import ContextMenu from '@imengyu/vue3-context-menu'
369
+
370
+ function onContextMenu(e: MouseEvent) {
371
+ const scaledPosition = ContextMenu.transformMenuPosition(e.target as HTMLElement, e.offsetX, e.offsetY);
372
+ //Full code of menuData is in `/examples/views/InScaledBody.vue`
373
+ menuData.x = scaledPosition.x;
374
+ menuData.y = scaledPosition.y;
375
+ //shou menu
376
+ ContextMenu.showContextMenu(menuData);
377
+ }
378
+ ```
379
+
380
+ #### Dynamic change menu in function mode
381
+
382
+ You only need to declare the menu data as responsive data, so that you can dynamically modify the menu:
383
+
384
+ ```ts
385
+ const menuData = reactive<MenuOptions>({
386
+ items: [
387
+ {
388
+ label: 'Simple item',
389
+ onClick: () => alert('Click Simple item'),
390
+ },
391
+ ]
392
+ });
393
+
394
+ ContextMenu.showContextMenu(menuData);
395
+
396
+ //You can change properties at any time after the menu is displayed:
397
+ menuData.items[0].label = 'My label CHANGED!'; //Change label
398
+ menuData.items[0].hidden = true; //Change hidden
399
+ ```
400
+
362
401
  #### MenuOptions
363
402
 
364
403
  | Property | Description | Type | Optional value | Default |
@@ -386,6 +425,7 @@ Menu separator component.
386
425
  | svgIcon | Display icons use svg symbol (`<use xlink:href="...">`) , only valid when icon attribute is empty. | `string` | — | — |
387
426
  | svgProps | The user-defined attribute of the svg tag, which is valid when using `svgIcon`. | `SVGAttributes` | — | — |
388
427
  | disabled | Disable menu item? | `boolean` | — | `false` |
428
+ | hidden | Hide menu item? | `boolean` | — | `false` |
389
429
  | adjustSubMenuPosition | Specifies should submenu adjust it position when the menu exceeds the screen. | `boolean` | — | `true` |
390
430
  | clickableWhenHasChildren | When there are subitems in this item, is it allowed to trigger its own click event? | `boolean` | — | `false` |
391
431
  | clickClose | Should close menu when Click this menu item ? | `boolean` | — | `true` |
package/examples/App.vue CHANGED
@@ -7,6 +7,7 @@
7
7
  <router-link :to="{ name: 'BasicComponent' }">Component useage</router-link>
8
8
  <router-link :to="{ name: 'ComponentCustomize' }">Component customize</router-link>
9
9
  <router-link :to="{ name: 'Theme' }">Dark Theme</router-link>
10
+ <router-link :to="{ name: 'InScaledBody' }">Use In scaled</router-link>
10
11
  </div>
11
12
  <div class="test-host">
12
13
  <router-view />
@@ -4,6 +4,7 @@ import BasicCustomize from '../views/BasicCustomize.vue'
4
4
  import BasicUseage from '../views/BasicUseage.vue'
5
5
  import Theme from '../views/Theme.vue'
6
6
  import ComponentCustomize from '../views/ComponentCustomize.vue'
7
+ import InScaledBody from '../views/InScaledBody.vue'
7
8
 
8
9
  const routes: Array<RouteRecordRaw> = [
9
10
  {
@@ -31,6 +32,11 @@ const routes: Array<RouteRecordRaw> = [
31
32
  name: 'Theme',
32
33
  component: Theme,
33
34
  },
35
+ {
36
+ path: '/InScaledBody',
37
+ name: 'InScaledBody',
38
+ component: InScaledBody,
39
+ },
34
40
  ]
35
41
 
36
42
  const router = createRouter({
@@ -4,8 +4,8 @@
4
4
  <div class="box1" style="flex:1" @contextmenu="onContextMenu($event)">
5
5
  Right click here to show contextmenu !
6
6
  </div>
7
- <div class="box1" style="flex:1;padding: 50px" @contextmenu="onContextMenu1($event)" @click.stop="test">
8
- Contextmenu In parent
7
+ <div class="box1" style="flex:1;padding: 50px" @contextmenu="onContextMenu1($event)">
8
+ Test nested menus: Contextmenu In parent
9
9
  <div class="box2" style="width: 200px; height: 100px" @contextmenu="onContextMenu2($event)">
10
10
  Contextmenu In child
11
11
  </div>
@@ -81,197 +81,217 @@ createApp(App)
81
81
  </div>
82
82
  </template>
83
83
 
84
- <script lang="ts">
85
- import { defineComponent } from 'vue'
84
+ <script setup lang="ts">
85
+ import { onMounted, reactive } from 'vue'
86
86
  import { MenuOptions } from '../../src/ContextMenuDefine';
87
+ import ContextMenu from '../../src/ContextMenuInstance';
87
88
 
88
- export default defineComponent({
89
- mounted() {
90
- (window as unknown as {
89
+ onMounted(() => {
90
+ //highlight demo code
91
+ (window as unknown as {
91
92
  hljs: {
92
93
  highlightAll: () => void
93
94
  }
94
95
  }).hljs?.highlightAll?.();
95
- },
96
- methods: {
97
- test() {
98
- console.log('test');
99
-
96
+ });
97
+
98
+ const menuData = reactive<MenuOptions>({
99
+ items: [
100
+ {
101
+ label: 'Simple item',
102
+ onClick: () => alert('Click Simple item'),
103
+ },
104
+ {
105
+ label: "Sub menu Example",
106
+ children: [
107
+ {
108
+ label: "Back",
109
+ onClick: () => {
110
+ console.log("You click Back");
111
+ }
112
+ },
113
+ { label: "Forward", disabled: true },
114
+ {
115
+ label: "Reload",
116
+ divided: true,
117
+ icon: "icon-reload-1",
118
+ onClick: () => {
119
+ alert("You click Reload");
120
+ }
121
+ },
122
+ {
123
+ label: "Save as...",
124
+ icon: "icon-save",
125
+ onClick: () => {
126
+ alert("You click Save as");
127
+ }
128
+ },
129
+ {
130
+ label: "Print...",
131
+ icon: "icon-print",
132
+ onClick: () => {
133
+ alert("You click Print");
134
+ }
135
+ },
136
+ { label: "View source", icon: "icon-terminal" },
137
+ { label: "Inspect" }
138
+ ],
100
139
  },
101
- onContextMenu(e : MouseEvent) {
102
- //prevent the browser's default menu
103
- e.preventDefault();
104
- //shou our menu
105
- this.$contextmenu({
106
- items: [
107
- {
108
- label: 'Simple item',
109
- onClick: () => alert('Click Simple item'),
110
- },
111
- {
112
- label: "Sub menu Example",
113
- children: [
114
- {
115
- label: "Back",
116
- onClick: () => {
117
- console.log("You click Back");
118
- }
119
- },
120
- { label: "Forward", disabled: true },
121
- {
122
- label: "Reload",
123
- divided: true,
124
- icon: "icon-reload-1",
125
- onClick: () => {
126
- alert("You click Reload");
127
- }
128
- },
129
- {
130
- label: "Save as...",
131
- icon: "icon-save",
132
- onClick: () => {
133
- alert("You click Save as");
134
- }
135
- },
136
- {
137
- label: "Print...",
138
- icon: "icon-print",
139
- onClick: () => {
140
- alert("You click Print");
141
- }
142
- },
143
- { label: "View source", icon: "icon-terminal" },
144
- { label: "Inspect" }
145
- ],
146
- },
147
- {
148
- label: "Submenu with Submenu",
149
- children: [
150
- {
151
- label: "Very long submenu",
152
- divided: true,
153
- children: [
154
- { label: "Test1" },
155
- { label: "Test2" },
156
- { label: "Test3" },
157
- { label: "Test4" },
158
- { label: "Test5" },
159
- { label: "Test6" },
160
- { label: "Test7" },
161
- { label: "Test8" },
162
- { label: "Test9" },
163
- { label: "Test10" },
164
- { label: "Test11" },
165
- { label: "Test12" },
166
- { label: "Test13" },
167
- { label: "Test14" },
168
- { label: "Test15" },
169
- { label: "Test16" },
170
- { label: "Test17" },
171
- { label: "Test18" },
172
- { label: "Test19" },
173
- { label: "Test20" },
174
- { label: "Test21" },
175
- { label: "Test22" },
176
- { label: "Test23" },
177
- { label: "Test24" },
178
- { label: "Test25" },
179
- { label: "Test26" },
180
- ]
181
- },
182
- {
183
- label: "A submenu",
184
- children: [
185
- { label: "Item1" },
186
- { label: "Item2" },
187
- { label: "Item3" },
188
- ]
189
- },
190
- {
191
- label: "A submenu2",
192
- children: [
193
- { label: "Item1" },
194
- { label: "Item2" },
195
- { label: "Item3" },
196
- {
197
- label: "A submenu",
198
- children: [
199
- { label: "Item1" },
200
- { label: "Item2" },
201
- { label: "Item3" },
202
- ]
203
- },
204
- ]
205
- },
206
- ]
207
- },
208
- {
209
- label: 'Item with icon',
210
- icon: "icon-reload-1",
211
- },
212
- {
213
- label: "Item with svg icon",
214
- svgIcon: "#icon-clock",
215
- },
216
- {
217
- label: "Item with svg icon",
218
- svgIcon: "#icon-multiply",
219
- svgProps: {
220
- fill: '#f60',
140
+ {
141
+ label: "Submenu with Submenu",
142
+ children: [
143
+ {
144
+ label: "Very long submenu",
145
+ divided: true,
146
+ children: [
147
+ { label: "Test1" },
148
+ { label: "Test2" },
149
+ { label: "Test3" },
150
+ { label: "Test4" },
151
+ { label: "Test5" },
152
+ { label: "Test6" },
153
+ { label: "Test7" },
154
+ { label: "Test8" },
155
+ { label: "Test9" },
156
+ { label: "Test10" },
157
+ { label: "Test11" },
158
+ { label: "Test12" },
159
+ { label: "Test13" },
160
+ { label: "Test14" },
161
+ { label: "Test15" },
162
+ { label: "Test16" },
163
+ { label: "Test17" },
164
+ { label: "Test18" },
165
+ { label: "Test19" },
166
+ { label: "Test20" },
167
+ { label: "Test21" },
168
+ { label: "Test22" },
169
+ { label: "Test23" },
170
+ { label: "Test24" },
171
+ { label: "Test25" },
172
+ { label: "Test26" },
173
+ ]
174
+ },
175
+ {
176
+ label: "A submenu",
177
+ children: [
178
+ { label: "Item1" },
179
+ { label: "Item2" },
180
+ { label: "Item3" },
181
+ ]
182
+ },
183
+ {
184
+ label: "A submenu2",
185
+ children: [
186
+ { label: "Item1" },
187
+ { label: "Item2" },
188
+ { label: "Item3" },
189
+ {
190
+ label: "A submenu",
191
+ children: [
192
+ { label: "Item1" },
193
+ { label: "Item2" },
194
+ { label: "Item3" },
195
+ ]
221
196
  },
222
- },
223
- ],
224
- iconFontClass: 'iconfont',
225
- customClass: "class-a",
226
- zIndex: 3,
227
- minWidth: 230,
228
- x: e.x,
229
- y: e.y
230
- } as MenuOptions);
197
+ ]
198
+ },
199
+ ]
231
200
  },
232
- onContextMenu1(e : MouseEvent) {
233
- //prevent the browser's default menu
234
- e.preventDefault();
235
- //shou our menu
236
- this.$contextmenu({
237
- items: [
238
- {
239
- label: 'This is menu in parent box',
240
- },
241
- {
242
- label: 'Simple item',
243
- },
244
- ],
245
- iconFontClass: 'iconfont',
246
- customClass: "class-a",
247
- zIndex: 3,
248
- minWidth: 230,
249
- x: e.x,
250
- y: e.y
251
- } as MenuOptions);
201
+ {
202
+ label: 'Test item dynamic show and hide',
203
+ clickClose: false,
204
+ onClick: () => {
205
+ menuData.items[4].hidden = !menuData.items[4].hidden;
206
+ },
252
207
  },
253
- onContextMenu2(e : MouseEvent) {
254
- //prevent the browser's default menu
255
- e.preventDefault();
256
- e.stopPropagation();
257
- //shou our menu
258
- this.$contextmenu({
259
- items: [
260
- {
261
- label: 'This is menu in child box',
262
- },
263
- {
264
- label: 'Simple item',
265
- },
266
- ],
267
- iconFontClass: 'iconfont',
268
- customClass: "class-a",
269
- zIndex: 3,
270
- minWidth: 230,
271
- x: e.x,
272
- y: e.y
273
- } as MenuOptions);
208
+ {
209
+ label: 'Click the item above to show/hide me',
274
210
  },
275
- }
211
+ {
212
+ label: 'Test item dynamic change the label',
213
+ clickClose: false,
214
+ onClick: () => {
215
+ if (menuData.items[5].label === 'Test item dynamic change the label')
216
+ menuData.items[5].label = 'My label CHANGED!';
217
+ else
218
+ menuData.items[5].label = 'Test item dynamic change the label';
219
+ },
220
+ },
221
+ {
222
+ label: 'Item with icon',
223
+ icon: "icon-reload-1",
224
+ },
225
+ {
226
+ label: "Item with svg icon",
227
+ svgIcon: "#icon-clock",
228
+ },
229
+ {
230
+ label: "Item with svg icon",
231
+ svgIcon: "#icon-multiply",
232
+ svgProps: {
233
+ fill: '#f60',
234
+ },
235
+ },
236
+ ],
237
+ iconFontClass: 'iconfont',
238
+ customClass: "class-a",
239
+ zIndex: 3,
240
+ minWidth: 230,
241
+ x: 0,
242
+ y: 0,
276
243
  });
244
+
245
+ function onContextMenu(e : MouseEvent) {
246
+ //prevent the browser's default menu
247
+ e.preventDefault();
248
+ menuData.x = e.x;
249
+ menuData.y = e.y;
250
+ //shou our menu
251
+ ContextMenu.showContextMenu(menuData);
252
+ }
253
+
254
+ function onContextMenu1(e : MouseEvent) {
255
+ //prevent the browser's default menu
256
+ e.preventDefault();
257
+ //shou our menu
258
+ ContextMenu.showContextMenu({
259
+ items: [
260
+ {
261
+ label: 'This is menu in parent box',
262
+ },
263
+ {
264
+ label: 'Simple item',
265
+ },
266
+ ],
267
+ iconFontClass: 'iconfont',
268
+ customClass: "class-a",
269
+ zIndex: 3,
270
+ minWidth: 230,
271
+ x: e.x,
272
+ y: e.y
273
+ } as MenuOptions);
274
+ }
275
+ function onContextMenu2(e : MouseEvent) {
276
+ //prevent the browser's default menu
277
+ e.preventDefault();
278
+ e.stopPropagation();
279
+ //shou our menu
280
+ ContextMenu.showContextMenu({
281
+ items: [
282
+ {
283
+ label: 'This is menu in child box',
284
+ },
285
+ {
286
+ label: 'Simple item',
287
+ },
288
+ ],
289
+ iconFontClass: 'iconfont',
290
+ customClass: "class-a",
291
+ zIndex: 3,
292
+ minWidth: 230,
293
+ x: e.x,
294
+ y: e.y
295
+ } as MenuOptions);
296
+ }
277
297
  </script>