@enso-ui/menus 5.0.2 → 5.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018 laravel-enso
3
+ Copyright (c) 2026 laravel-enso
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -27,4 +27,4 @@ Thank you to all the people who already contributed to Enso!
27
27
 
28
28
  ## License
29
29
 
30
- [ISC](https://opensource.org/licenses/ISC)
30
+ [MIT](https://opensource.org/licenses/MIT)
package/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "@enso-ui/menus",
3
- "version": "5.0.2",
3
+ "version": "5.1.0",
4
4
  "description": "Basic menus package",
5
5
  "main": "src/bulma/pages/menus/Index.vue",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
6
  "repository": {
10
7
  "type": "git",
11
8
  "url": "git+https://github.com/enso-ui/menus.git"
@@ -15,35 +12,22 @@
15
12
  "vue"
16
13
  ],
17
14
  "author": "Adrian Ocneanu <aocneanu@gmail.com>",
18
- "license": "ISC",
15
+ "license": "MIT",
19
16
  "bugs": {
20
17
  "url": "https://github.com/enso-ui/menus/issues"
21
18
  },
22
19
  "homepage": "https://github.com/enso-ui/menus#readme",
23
20
  "dependencies": {
24
21
  "@enso-ui/dropdown-indicator": "^2.0",
25
- "@enso-ui/forms": "^3.0",
22
+ "@enso-ui/forms": "^4.0.0",
26
23
  "@enso-ui/switch": "^2.0",
27
- "@enso-ui/tables": "^3.0",
28
- "@enso-ui/themes": "^3.0",
29
- "@enso-ui/transitions": "^2.0",
30
- "@enso-ui/ui": "^6.0",
31
- "@fortawesome/fontawesome-svg-core": "^1.2.2",
32
- "@fortawesome/free-solid-svg-icons": "^5.11.2",
33
- "@fortawesome/vue-fontawesome": "3.0.0-5",
24
+ "@enso-ui/tables": "^4.0.0",
25
+ "@enso-ui/ui": "^7.0.0",
26
+ "@fortawesome/fontawesome-svg-core": "^7.2.0",
27
+ "@fortawesome/free-solid-svg-icons": "^7.2.0",
28
+ "@fortawesome/vue-fontawesome": "3.1.3",
29
+ "pinia": "^3.0.3",
34
30
  "vue": "^3.0",
35
- "vuedraggable": "^4.1.0",
36
- "vuex": "^4.0.0"
37
- },
38
- "devDependencies": {
39
- "@vue/cli-plugin-babel": "5.0.0-beta.6",
40
- "@vue/cli-plugin-eslint": "5.0.0-beta.6",
41
- "@vue/eslint-config-airbnb": "^5.0.0",
42
- "autoprefixer": "^9.6.1",
43
- "babel-eslint": "^10.0.1",
44
- "cross-env": "^6.0.0",
45
- "eslint": "^7.0.0",
46
- "eslint-import-resolver-alias": "^1.1.2",
47
- "eslint-plugin-vue": "^8.0.0"
31
+ "vuedraggable": "^4.1.0"
48
32
  }
49
33
  }
@@ -1,44 +1,37 @@
1
1
  <template>
2
2
  <core-menu-item>
3
3
  <template #default="{ menu, editable, expandedSidebar, hasActiveChild, menuEvents }">
4
- <div class="menu-item is-flex is-align-items-center"
5
- v-on="menuEvents"
6
- @mouseenter="dropdown = true"
7
- @mouseleave="dropdown = false">
8
- <div class="icon"
9
- :class="{ 'is-opaque': !menu.active && !hasActiveChild }">
10
- <fa class="handle"
11
- fixed-width
12
- icon="grip-lines"
13
- v-if="editable"/>
14
- <fa fixed-width
15
- :icon="menu.icon"
16
- v-else/>
17
- </div>
18
- <zoom :duration="300">
19
- <div class="ml-2 menu-hiding-label"
20
- :class="[
21
- { 'is-bold': menu.active },
22
- { 'is-opaque': !menu.active && !hasActiveChild }
23
- ]"
4
+ <div class="menu-item">
5
+ <a class="menu-item-link"
6
+ :class="{
7
+ 'is-active': menu.active,
8
+ 'is-collapsed': !expandedSidebar
9
+ }"
10
+ v-tooltip="collapsedTooltip(expandedSidebar, menu)"
11
+ v-on="menuEvents">
12
+ <span class="icon menu-item-icon"
13
+ :class="{ 'is-opaque': !menu.active && !hasActiveChild }">
14
+ <fa class="handle"
15
+ fixed-width
16
+ :icon="faGripLines"
17
+ v-if="editable"/>
18
+ <fa fixed-width
19
+ :icon="menu.icon"
20
+ v-else/>
21
+ </span>
22
+ <span class="menu-item-label"
23
+ :class="{ 'is-opaque': !menu.active && !hasActiveChild }"
24
24
  v-if="expandedSidebar">
25
25
  {{ i18n(menu.name) }}
26
- </div>
27
- </zoom>
28
- <dropdown-indicator class="is-small"
29
- :open="menu.expanded"
30
- v-if="menu.children"/>
31
- <transition @enter="enter"
32
- @leave="leave">
33
- <div class="dropdown-content"
34
- ref="dropdown"
35
- v-if="!expandedSidebar && dropdown">
36
- <div class="dropdown-item"
37
- :class="{ 'is-bold': menu.active }">
38
- {{ i18n(menu.name) }}
39
- </div>
40
- </div>
41
- </transition>
26
+ </span>
27
+ <span class="menu-arrow"
28
+ :class="{ 'is-collapsed': !expandedSidebar }"
29
+ v-if="menu.children">
30
+ <dropdown-indicator
31
+ class="is-compact"
32
+ :open="menu.expanded"/>
33
+ </span>
34
+ </a>
42
35
  </div>
43
36
  </template>
44
37
  </core-menu-item>
@@ -46,96 +39,135 @@
46
39
 
47
40
  <script>
48
41
  import { FontAwesomeIcon as Fa } from '@fortawesome/vue-fontawesome';
49
- import { library } from '@fortawesome/fontawesome-svg-core';
50
42
  import { faGripLines } from '@fortawesome/free-solid-svg-icons';
51
- import { Zoom } from '@enso-ui/transitions';
52
43
  import DropdownIndicator from '@enso-ui/dropdown-indicator';
53
44
  import CoreMenuItem from '../../../core/components/menu/MenuItem.vue';
54
45
 
55
- library.add(faGripLines);
56
-
57
46
  export default {
58
47
  name: 'MenuItem',
59
48
 
60
49
  components: {
61
- CoreMenuItem, DropdownIndicator, Fa, Zoom,
50
+ CoreMenuItem, DropdownIndicator, Fa,
62
51
  },
63
52
 
64
53
  inject: ['i18n'],
65
54
 
66
55
  data: () => ({
67
- dropdown: false,
56
+ faGripLines,
68
57
  }),
69
58
 
70
- computed: {
71
- sidebar() {
72
- return document.querySelector('.aside.sidebar');
73
- },
74
- },
75
-
76
59
  methods: {
77
- adjust() {
78
- this.$refs.dropdown.style['margin-top'] = `-${this.sidebar.scrollTop + 2}px`;
79
- },
80
- enter() {
81
- this.adjust();
82
- this.sidebar.addEventListener('scroll', this.adjust);
83
- },
84
- leave() {
85
- this.sidebar.removeEventListener('scroll', this.adjust);
60
+ collapsedTooltip(expandedSidebar, menu) {
61
+ if (expandedSidebar) {
62
+ return null;
63
+ }
64
+
65
+ return {
66
+ content: this.i18n(menu.name),
67
+ placement: 'right',
68
+ offset: [0, 10],
69
+ delay: {
70
+ show: 80,
71
+ hide: 0,
72
+ },
73
+ };
86
74
  },
87
75
  },
88
76
  };
89
77
  </script>
90
78
 
91
79
  <style lang="scss">
92
- @import '@enso-ui/themes/bulma/variables';
93
-
94
80
  .menu-list {
95
81
  .menu-item {
82
+ position: relative;
83
+ min-width: 0;
96
84
 
97
85
  .is-opaque {
98
- opacity: 0.8;
86
+ opacity: 0.7;
99
87
 
100
88
  &:hover {
101
- font-weight: 800;
89
+ font-weight: inherit;
102
90
  }
103
91
  }
104
92
 
105
- display: flex;
106
- padding: 0.3em 0.3em;
107
- cursor: pointer;
93
+ .menu-item-link {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 0.3rem;
97
+ font-size: 0.95rem;
98
+ line-height: 1.3;
99
+ width: 100%;
100
+ min-width: 0;
101
+ padding-block: 0.35rem;
102
+ padding-inline-start: 0.25rem;
103
+ padding-inline-end: 0.6rem;
104
+ color: var(--bulma-text);
105
+ transition: background-color .2s ease, color .2s ease;
106
+ text-decoration: none;
108
107
 
109
- .menu-hiding-label {
110
- white-space: nowrap;
111
- }
108
+ &:hover {
109
+ background-color: var(--bulma-scheme-main-ter);
110
+ color: var(--bulma-text-strong);
111
+ }
112
112
 
113
- .dropdown-content {
114
- display: block;
115
- white-space: nowrap;
116
- padding-bottom: 0;
117
- padding-top: 0;
118
- position: fixed;
113
+ &.is-active {
114
+ background-color: var(--bulma-scheme-main-ter);
115
+ color: var(--bulma-text-strong);
116
+ }
119
117
 
120
- .dropdown-item.is-bold {
121
- font-weight: 800;
118
+ &::before {
119
+ content: '';
120
+ position: absolute;
121
+ top: 0.45rem;
122
+ bottom: 0.45rem;
123
+ left: -0.35rem;
124
+ width: 0.2rem;
125
+ border-radius: 9999px;
126
+ background-color: transparent;
127
+ transition: background-color .2s ease, opacity .2s ease;
128
+ opacity: 0;
122
129
  }
123
130
 
124
- [dir='ltr'] & {
125
- left: $sidebar-collapsed-width;
131
+ &:hover::before {
132
+ background-color: var(--bulma-border-strong);
133
+ opacity: 0.45;
126
134
  }
127
135
 
128
- [dir='rtl'] & {
129
- right: $sidebar-collapsed-width;
136
+ &.is-active::before {
137
+ background-color: var(--bulma-primary);
138
+ opacity: 1;
130
139
  }
131
- }
132
140
 
133
- .icon.angle.is-small {
134
- [dir='ltr'] & {
135
- margin-left: auto;
141
+ &.is-collapsed {
142
+ justify-content: flex-start;
143
+ gap: 0;
144
+ padding-inline-end: 0.72rem;
145
+ padding-inline-start: 0.45rem;
136
146
  }
137
- [dir='rtl'] & {
138
- margin-right: auto;
147
+ }
148
+
149
+ .menu-item-label {
150
+ flex: 1 1 auto;
151
+ min-width: 0;
152
+ overflow: hidden;
153
+ text-overflow: ellipsis;
154
+ white-space: nowrap;
155
+ font-weight: 500;
156
+ }
157
+
158
+ .menu-item-icon {
159
+ flex: 0 0 auto;
160
+ }
161
+
162
+ .menu-arrow {
163
+ display: inline-flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ margin-inline-start: auto;
167
+ min-width: 0.5rem;
168
+
169
+ &.is-collapsed {
170
+ margin-inline-start: 0.25rem;
139
171
  }
140
172
  }
141
173
  }
@@ -38,15 +38,16 @@ export default {
38
38
  transition: height .333s ease;
39
39
  display: block;
40
40
  overflow-y: hidden;
41
- overflow-x: hidden;
41
+ overflow-x: visible;
42
+ margin: 0;
42
43
 
43
44
  li > ul {
44
45
  [dir='ltr'] & {
45
- margin: 0 0 0 .5rem;
46
+ margin: 0 0 0 .75rem;
46
47
  padding-left: 0;
47
48
  }
48
49
  [dir='rtl'] & {
49
- margin: 0 .5rem 0 0;
50
+ margin: 0 .75rem 0 0;
50
51
  padding-right: 0;
51
52
  }
52
53
  }
@@ -10,8 +10,7 @@
10
10
  <div class="level-item">
11
11
  <core-menu-organizer>
12
12
  <template #default="{ bindings, events }">
13
- <vue-switch class="is-medium"
14
- v-bind="bindings"
13
+ <vue-switch v-bind="bindings"
15
14
  v-on="events"/>
16
15
  </template>
17
16
  </core-menu-organizer>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="columns is-centered">
3
3
  <div class="column is-three-quarters-desktop is-full-touch">
4
- <enso-form class="box has-background-light raises-on-hover"/>
4
+ <enso-form class="box"/>
5
5
  </div>
6
6
  </div>
7
7
  </template>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="columns is-centered">
3
3
  <div class="column is-three-quarters-desktop is-full-touch">
4
- <enso-form class="box has-background-light raises-on-hover"
4
+ <enso-form class="box"
5
5
  @ready="ready = true"
6
6
  ref="form">
7
7
  <template #actions
@@ -16,7 +16,7 @@
16
16
  {{ i18n('Configure') }}
17
17
  </span>
18
18
  <span class="icon">
19
- <fa icon="sliders-h"/>
19
+ <fa :icon="faSliders"/>
20
20
  </span>
21
21
  <span class="is-hidden-mobile"/>
22
22
  </a>
@@ -28,7 +28,7 @@
28
28
  {{ i18n('File') }}
29
29
  </span>
30
30
  <span class="icon">
31
- <fa icon="save"/>
31
+ <fa :icon="faSave"/>
32
32
  </span>
33
33
  <span class="is-hidden-mobile"/>
34
34
  </a>
@@ -41,12 +41,9 @@
41
41
 
42
42
  <script>
43
43
  import { FontAwesomeIcon as Fa } from '@fortawesome/vue-fontawesome';
44
- import { library } from '@fortawesome/fontawesome-svg-core';
45
- import { faSave, faSlidersH } from '@fortawesome/free-solid-svg-icons';
44
+ import { faSave, faSliders } from '@fortawesome/free-solid-svg-icons';
46
45
  import { EnsoForm } from '@enso-ui/forms/bulma';
47
46
 
48
- library.add(faSave, faSlidersH);
49
-
50
47
  export default {
51
48
  name: 'Edit',
52
49
 
@@ -57,6 +54,8 @@ export default {
57
54
  ],
58
55
 
59
56
  data: () => ({
57
+ faSave,
58
+ faSliders,
60
59
  ready: false,
61
60
  }),
62
61
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <enso-table class="box is-paddingless raises-on-hover"
2
+ <enso-table class="box p-0"
3
3
  id="menus"/>
4
4
  </template>
5
5
 
@@ -1,4 +1,5 @@
1
- import App from '@enso-ui/ui/src/core/app';
2
1
  import MenuOrganizer from './components/settings/MenuOrganizer.vue';
3
2
 
4
- App.registerSettingsItem('menu-organizer', MenuOrganizer, 400, 'system.menus.organize');
3
+ export default App => {
4
+ App.registerSettingsItem('menu-organizer', MenuOrganizer, 400, 'system.menus.organize');
5
+ };
@@ -1,6 +1,6 @@
1
1
  import routeImporter from '@enso-ui/ui/src/modules/importers/routeImporter';
2
2
 
3
- const routes = routeImporter(require.context('./menus', false, /.*\.js$/));
3
+ const routes = routeImporter.fromGlob(import.meta.glob('./menus/*.js', { eager: true }));
4
4
  const Router = () => import('@enso-ui/ui/src/bulma/pages/Router.vue');
5
5
 
6
6
  export default {
@@ -1,6 +1,6 @@
1
1
  import routeImporter from '@enso-ui/ui/src/modules/importers/routeImporter';
2
2
 
3
- const routes = routeImporter(require.context('./system', false, /.*\.js$/));
3
+ const routes = routeImporter.fromGlob(import.meta.glob('./system/*.js', { eager: true }));
4
4
 
5
5
  export default {
6
6
  path: '/system',
@@ -1,7 +1,5 @@
1
1
  <script>
2
- import {
3
- mapState, mapGetters, mapMutations, mapActions,
4
- } from 'vuex';
2
+ import { useStore } from '../../utils/pinia';
5
3
 
6
4
  export default {
7
5
  name: 'MenuItem',
@@ -16,10 +14,15 @@ export default {
16
14
  },
17
15
 
18
16
  computed: {
19
- ...mapState('menu', ['editable']),
20
- ...mapState('layout', ['isTouch']),
21
- ...mapState('layout/sidebar', ['isExpanded']),
22
- ...mapGetters('menu', ['hasActiveChild']),
17
+ editable() {
18
+ return useStore('menu').editable;
19
+ },
20
+ isTouch() {
21
+ return useStore('layout').isTouch;
22
+ },
23
+ isExpanded() {
24
+ return useStore('layout').sidebar.isExpanded;
25
+ },
23
26
  active() {
24
27
  return this.menu.route !== null
25
28
  && (this.matchesName || this.matchesPath);
@@ -53,9 +56,21 @@ export default {
53
56
  },
54
57
 
55
58
  methods: {
56
- ...mapMutations('layout/sidebar', ['hide']),
57
- ...mapMutations('menu', ['activate', 'toggle']),
58
- ...mapActions('menu', ['refresh']),
59
+ hasActiveChild(menu) {
60
+ return useStore('menu').hasActiveChild(menu);
61
+ },
62
+ hide() {
63
+ useStore('layout').hideSidebar();
64
+ },
65
+ activate(payload) {
66
+ useStore('menu').activate(payload);
67
+ },
68
+ toggle(menu) {
69
+ useStore('menu').toggle(menu);
70
+ },
71
+ refresh() {
72
+ useStore('menu').refresh();
73
+ },
59
74
  select() {
60
75
  if (this.menu.children) {
61
76
  this.toggle(this.menu);
@@ -1,5 +1,5 @@
1
1
  <script>
2
- import { mapState, mapMutations } from 'vuex';
2
+ import { useStore } from '../../utils/pinia';
3
3
 
4
4
  export default {
5
5
  name: 'Menus',
@@ -26,7 +26,9 @@ export default {
26
26
  }),
27
27
 
28
28
  computed: {
29
- ...mapState('menu', ['editable']),
29
+ editable() {
30
+ return useStore('menu').editable;
31
+ },
30
32
  disabled() {
31
33
  return !this.editable;
32
34
  },
@@ -46,7 +48,9 @@ export default {
46
48
  },
47
49
 
48
50
  methods: {
49
- ...mapMutations('menu', ['organize']),
51
+ organize(payload) {
52
+ useStore('menu').organizeMenus(payload);
53
+ },
50
54
  shrink(height) {
51
55
  this.el.style.height = `${parseInt(this.el.style.height, 10) - height}px`;
52
56
  this.$emit('shrink', height);
@@ -1,16 +1,12 @@
1
1
  <script>
2
- import { mapState } from 'vuex';
2
+ import { useStore } from '../../utils/pinia';
3
3
 
4
4
  export default {
5
5
  name: 'Sidebar',
6
6
 
7
- computed: {
8
- ...mapState('menu', ['menus']),
9
- },
10
-
11
7
  render() {
12
8
  return this.$slots.default({
13
- menus: this.menus,
9
+ menus: useStore('menu').menus,
14
10
  });
15
11
  },
16
12
  };
@@ -1,21 +1,19 @@
1
1
  <script>
2
- import { mapState, mapMutations } from 'vuex';
2
+ import { useStore } from '../../utils/pinia';
3
3
 
4
4
  export default {
5
5
  name: 'MenuOrganizer',
6
6
 
7
- computed: {
8
- ...mapState('menu', ['editable']),
9
- },
10
-
11
7
  methods: {
12
- ...mapMutations('menu', ['edit']),
8
+ edit(value) {
9
+ useStore('menu').edit(value);
10
+ },
13
11
  },
14
12
 
15
13
  render() {
16
14
  return this.$slots.default({
17
15
  bindings: {
18
- modelValue: this.editable,
16
+ modelValue: useStore('menu').editable,
19
17
  },
20
18
  events: {
21
19
  'update:modelValue': this.edit,
@@ -0,0 +1,51 @@
1
+ import { defineStore } from 'pinia';
2
+ import { hasActiveChild, organize } from '../plugins/utils';
3
+
4
+ export const menu = defineStore('menu', {
5
+ state: () => ({
6
+ menus: [],
7
+ editable: false,
8
+ }),
9
+
10
+ actions: {
11
+ set(menus) {
12
+ this.menus = menus;
13
+ },
14
+ activate({ menu, active }) {
15
+ menu.active = active;
16
+ },
17
+ toggle(menu) {
18
+ menu.expanded = !menu.expanded;
19
+ },
20
+ expand(menu) {
21
+ menu.expanded = true;
22
+ },
23
+ collapse(menu) {
24
+ menu.expanded = false;
25
+ },
26
+ edit(status) {
27
+ this.editable = status;
28
+ },
29
+ organizeMenus(organizedMenus) {
30
+ this.children = this.menus;
31
+ organize(this, organizedMenus);
32
+ this.menus = this.children;
33
+ delete this.children;
34
+ },
35
+ refresh(menus = null) {
36
+ const shouldRefresh = menus || this.menus;
37
+
38
+ shouldRefresh.filter(menu => menu.children)
39
+ .forEach(menu => {
40
+ this.refresh(menu.children);
41
+
42
+ if (!menu.expanded && hasActiveChild(menu)) {
43
+ this.expand(menu);
44
+ }
45
+ });
46
+ },
47
+ hasActiveChild(menu) {
48
+ return hasActiveChild(menu);
49
+ },
50
+ },
51
+ });
@@ -0,0 +1,11 @@
1
+ import { getActivePinia } from 'pinia';
2
+
3
+ export const useStore = id => {
4
+ const store = getActivePinia()?._s?.get(id);
5
+
6
+ if (!store) {
7
+ throw new Error(`Missing Pinia store: ${id}`);
8
+ }
9
+
10
+ return store;
11
+ };
package/src/store/menu.js DELETED
@@ -1,42 +0,0 @@
1
- import { hasActiveChild, organize } from '../plugins/menuOrganizer';
2
-
3
- export const state = {
4
- menus: [],
5
- editable: false,
6
- };
7
-
8
- export const getters = {
9
- hasActiveChild: () => menu => hasActiveChild(menu),
10
- };
11
-
12
- export const mutations = {
13
- set: (state, menus) => (state.menus = menus),
14
- activate: (state, { menu, active }) => (menu.active = active),
15
- toggle: (state, menu) => (menu.expanded = !menu.expanded),
16
- expand: (state, menu) => (menu.expanded = true),
17
- collapse: (state, menu) => (menu.expanded = false),
18
- edit: (state, status) => (state.editable = status),
19
- organize: (state, organizedMenus) => {
20
- state.children = state.menus;
21
- organize(state, organizedMenus);
22
- state.menus = state.children;
23
- delete state.children;
24
- },
25
- };
26
-
27
- export const actions = {
28
- refresh: ({
29
- state, getters, commit, dispatch,
30
- }, menus = null) => {
31
- const shouldRefresh = menus || state.menus;
32
-
33
- shouldRefresh.filter(menu => menu.children)
34
- .forEach(menu => {
35
- dispatch('refresh', menu.children);
36
-
37
- if (!menu.expanded && getters.hasActiveChild(menu)) {
38
- commit('expand', menu);
39
- }
40
- });
41
- },
42
- };
File without changes