oceanhelm 0.0.7 → 0.0.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oceanhelm",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Oceanhelm's highly customizable library",
5
5
  "private": false,
6
6
  "type": "module",
@@ -1,228 +1,220 @@
1
1
  <template>
2
- <nav id="sidebar" :class="sidebarClasses">
3
- <!-- Logo Section -->
4
- <div class="logo d-flex align-items-center left" v-if="showLogo">
5
- <i :class="logoIcon" class="me-2"></i>
6
- <span>{{ brandName }}</span>
7
- </div>
8
-
9
- <!-- Navigation Items -->
10
- <ul class="list-unstyled components mt-4">
11
- <li
12
- v-for="(item, index) in filteredMenuItems"
13
- :key="index"
14
- :class="{ 'active': item.active, 'dropdown': item.type === 'dropdown' }"
15
- @click="handleItemClick(item)"
16
- >
17
- <!-- Regular Link -->
18
- <a
19
- v-if="item.type === 'link'"
20
- :href="item.href || '#'"
21
- @click.prevent="handleNavigation(item)"
22
- >
23
- <i :class="item.icon" v-if="item.icon"></i>
2
+ <nav id="sidebar" :class="sidebarClasses">
3
+ <!-- Logo Section -->
4
+ <div class="logo d-flex align-items-center left" v-if="showLogo">
5
+ <i :class="logoIcon" class="me-2"></i>
6
+ <span>{{ brandName }}</span>
7
+ </div>
8
+
9
+ <!-- Navigation Items -->
10
+ <ul class="list-unstyled components mt-4">
11
+ <li v-for="(item, index) in filteredMenuItems" :key="index"
12
+ :class="{ 'active': item.active, 'dropdown': item.type === 'dropdown' }" @click="handleItemClick(item)">
13
+ <!-- Regular Link -->
14
+ <a v-if="item.type === 'link'" :href="item.href || '#'" @click.prevent="handleNavigation(item)">
15
+ <i :class="item.icon" v-if="item.icon"></i>
16
+ {{ item.label }}
17
+ </a>
18
+
19
+ <!-- Button/Action -->
20
+ <a v-else-if="item.type === 'button'" @click.prevent="handleAction(item)" style="cursor: pointer;">
21
+ <i :class="item.icon" v-if="item.icon"></i>
22
+ {{ item.label }}
23
+ </a>
24
+
25
+ <!-- Dropdown -->
26
+ <template v-else-if="item.type === 'dropdown'">
27
+ <a class="dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
28
+ <i :class="item.icon" v-if="item.icon"></i>
24
29
  {{ item.label }}
25
30
  </a>
26
-
27
- <!-- Button/Action -->
28
- <a
29
- v-else-if="item.type === 'button'"
30
- @click.prevent="handleAction(item)"
31
- style="cursor: pointer;"
32
- >
33
- <i :class="item.icon" v-if="item.icon"></i>
34
- {{ item.label }}
35
- </a>
36
-
37
- <!-- Dropdown -->
38
- <template v-else-if="item.type === 'dropdown'">
39
- <a
40
- class="dropdown-toggle"
41
- type="button"
42
- data-bs-toggle="dropdown"
43
- aria-expanded="false"
44
- >
45
- <i :class="item.icon" v-if="item.icon"></i>
46
- {{ item.label }}
31
+ <div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
32
+ <a v-for="(subItem, subIndex) in getFilteredChildren(item)" :key="subIndex" class="dropdown-item black"
33
+ @click.prevent="handleAction(subItem)" style="cursor: pointer;">
34
+ {{ subItem.label }}
47
35
  </a>
48
- <div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
49
- <a
50
- v-for="(subItem, subIndex) in item.children"
51
- :key="subIndex"
52
- class="dropdown-item black"
53
- @click.prevent="handleAction(subItem)"
54
- style="cursor: pointer;"
55
- >
56
- {{ subItem.label }}
57
- </a>
58
- </div>
59
- </template>
60
-
61
- <!-- Separator -->
62
- <div
63
- v-else-if="item.type === 'separator'"
64
- class="dropdown-divider"
65
- ></div>
66
-
67
- <!-- Static Text -->
68
- <a v-else-if="item.type === 'text'">
69
- {{ item.label }}
70
- </a>
71
- </li>
72
- </ul>
73
-
74
- <!-- Custom Slot for Additional Content -->
75
- <slot name="footer"></slot>
76
- </nav>
77
- </template>
78
-
79
- <script>
80
- export default {
81
- name: 'ConfigurableSidebar',
82
-
83
- props: {
84
- // Brand configuration
85
- brandName: {
86
- type: String,
87
- default: 'OceanHelm'
88
- },
89
- logoIcon: {
90
- type: String,
91
- default: 'bi bi-water'
92
- },
93
- showLogo: {
94
- type: Boolean,
95
- default: true
96
- },
97
-
98
- // Menu items configuration
99
- menuItems: {
100
- type: Array,
101
- required: true,
102
- default: () => []
103
- },
104
-
105
- // User profile for role-based filtering
106
- userProfile: {
107
- type: Object,
108
- default: () => ({})
109
- },
110
-
111
- // Sidebar behavior
112
- responsive: {
113
- type: Boolean,
114
- default: true
115
- },
116
-
117
- // Custom classes
118
- customClasses: {
119
- type: String,
120
- default: ''
121
- },
122
-
123
- // Permission checker function
124
- permissionChecker: {
125
- type: Function,
126
- default: null
127
- }
36
+ </div>
37
+ </template>
38
+
39
+ <!-- Separator -->
40
+ <div v-else-if="item.type === 'separator'" class="dropdown-divider"></div>
41
+
42
+ <!-- Static Text -->
43
+ <a v-else-if="item.type === 'text'">
44
+ {{ item.label }}
45
+ </a>
46
+ </li>
47
+ </ul>
48
+
49
+ <!-- Custom Slot for Additional Content -->
50
+ <slot name="footer"></slot>
51
+ </nav>
52
+ </template>
53
+
54
+ <script>
55
+ export default {
56
+ name: 'ConfigurableSidebar',
57
+
58
+ props: {
59
+ // Brand configuration
60
+ brandName: {
61
+ type: String,
62
+ default: 'OceanHelm'
128
63
  },
129
-
130
- computed: {
131
- sidebarClasses() {
132
- return this.customClasses;
133
- },
134
-
135
- filteredMenuItems() {
136
- return this.menuItems.filter(item => this.hasPermission(item));
137
- }
64
+ logoIcon: {
65
+ type: String,
66
+ default: 'bi bi-water'
138
67
  },
139
-
140
- mounted() {
141
- if (this.responsive) {
142
- this.initializeResponsiveBehavior();
143
- }
68
+ showLogo: {
69
+ type: Boolean,
70
+ default: true
144
71
  },
145
-
146
- methods: {
147
- // Permission checking
148
- hasPermission(item) {
149
- // Use custom permission checker if provided
150
- if (this.permissionChecker) {
151
- return this.permissionChecker(item, this.userProfile);
152
- }
153
-
154
- // Default role-based checking
155
- if (!item.roles || item.roles.length === 0) {
156
- return true; // No role restriction
157
- }
158
-
159
- return item.roles.includes(this.userProfile.role);
160
- },
161
-
162
- // Navigation handling
163
- handleNavigation(item) {
164
- this.$emit('navigate', item);
165
-
166
- // Default behavior if no custom handler
167
- if (item.href && !item.preventDefault) {
168
- if (item.external) {
169
- window.open(item.href, '_blank');
170
- } else {
171
- this.$router?.push(item.href);
172
- }
72
+
73
+ // Menu items configuration
74
+ menuItems: {
75
+ type: Array,
76
+ required: true,
77
+ default: () => []
78
+ },
79
+
80
+ // User profile for role-based filtering
81
+ userProfile: {
82
+ type: Object,
83
+ default: () => ({})
84
+ },
85
+
86
+ // Sidebar behavior
87
+ responsive: {
88
+ type: Boolean,
89
+ default: true
90
+ },
91
+
92
+ // Custom classes
93
+ customClasses: {
94
+ type: String,
95
+ default: ''
96
+ },
97
+
98
+ // Permission checker function
99
+ permissionChecker: {
100
+ type: Function,
101
+ default: null
102
+ }
103
+ },
104
+
105
+ computed: {
106
+ sidebarClasses() {
107
+ return this.customClasses;
108
+ },
109
+
110
+ filteredMenuItems() {
111
+ return this.menuItems.filter(item => {
112
+ // Check if the item itself has permission
113
+ if (!this.hasPermission(item)) {
114
+ return false;
173
115
  }
174
- },
175
-
176
- // Action handling
177
- handleAction(item) {
178
- this.$emit('action', item);
179
- },
180
-
181
- // Item click handling
182
- handleItemClick(item) {
183
- this.$emit('item-click', item);
184
-
185
- if (item.type === 'dropdown') {
186
- // Handle dropdown specific logic if needed
116
+
117
+ // For dropdown items, check if any children have permission
118
+ if (item.type === 'dropdown' && item.children) {
119
+ return item.children.some(child => this.hasPermission(child));
187
120
  }
188
- },
189
-
190
- // Responsive behavior
191
- initializeResponsiveBehavior() {
192
- const sidebarToggle = document.getElementById('sidebarToggle');
193
- const sidebar = document.getElementById('sidebar');
194
- const content = document.getElementById('content');
195
-
196
- if (!sidebarToggle || !sidebar || !content) return;
197
-
198
- // Initial state for desktop
199
- if (window.innerWidth >= 768) {
200
- sidebar.classList.toggle('active');
201
- content.classList.toggle('active');
121
+
122
+ return true;
123
+ });
124
+ }
125
+ },
126
+
127
+ mounted() {
128
+ if (this.responsive) {
129
+ this.initializeResponsiveBehavior();
130
+ }
131
+ },
132
+
133
+ methods: {
134
+ // Get filtered children for dropdown items
135
+ getFilteredChildren(item) {
136
+ if (!item.children) return [];
137
+ return item.children.filter(child => this.hasPermission(child));
138
+ },
139
+
140
+ // Permission checking
141
+ hasPermission(item) {
142
+ // Use custom permission checker if provided
143
+ if (this.permissionChecker) {
144
+ return this.permissionChecker(item, this.userProfile);
145
+ }
146
+
147
+ // Default role-based checking
148
+ if (!item.roles || item.roles.length === 0) {
149
+ return true; // No role restriction
150
+ }
151
+
152
+ return item.roles.includes(this.userProfile.role);
153
+ },
154
+
155
+ // Navigation handling
156
+ handleNavigation(item) {
157
+ this.$emit('navigate', item);
158
+
159
+ // Default behavior if no custom handler
160
+ if (item.href && !item.preventDefault) {
161
+ if (item.external) {
162
+ window.open(item.href, '_blank');
163
+ } else {
164
+ this.$router?.push(item.href);
202
165
  }
203
-
204
- // Toggle functionality
205
- sidebarToggle.addEventListener('click', () => {
206
- sidebar.classList.toggle('active');
207
- content.classList.toggle('active');
208
- });
209
-
210
- // Close sidebar on outside click (mobile)
211
- document.addEventListener('click', (event) => {
212
- const isClickInsideSidebar = sidebar.contains(event.target);
213
- const isClickOnToggleBtn = sidebarToggle.contains(event.target);
214
-
215
- if (!isClickInsideSidebar && !isClickOnToggleBtn &&
216
- window.innerWidth < 768 &&
217
- sidebar.classList.contains('active')) {
218
- sidebar.classList.remove('active');
219
- content.classList.remove('active');
220
- }
221
- });
222
166
  }
223
167
  },
224
-
225
- emits: ['navigate', 'action', 'item-click']
226
- }
227
- </script>
228
-
168
+
169
+ // Action handling
170
+ handleAction(item) {
171
+ this.$emit('action', item);
172
+ },
173
+
174
+ // Item click handling
175
+ handleItemClick(item) {
176
+ this.$emit('item-click', item);
177
+
178
+ if (item.type === 'dropdown') {
179
+ // Handle dropdown specific logic if needed
180
+ }
181
+ },
182
+
183
+ // Responsive behavior
184
+ initializeResponsiveBehavior() {
185
+ const sidebarToggle = document.getElementById('sidebarToggle');
186
+ const sidebar = document.getElementById('sidebar');
187
+ const content = document.getElementById('content');
188
+
189
+ if (!sidebarToggle || !sidebar || !content) return;
190
+
191
+ // Initial state for desktop
192
+ if (window.innerWidth >= 768) {
193
+ sidebar.classList.toggle('active');
194
+ content.classList.toggle('active');
195
+ }
196
+
197
+ // Toggle functionality
198
+ sidebarToggle.addEventListener('click', () => {
199
+ sidebar.classList.toggle('active');
200
+ content.classList.toggle('active');
201
+ });
202
+
203
+ // Close sidebar on outside click (mobile)
204
+ document.addEventListener('click', (event) => {
205
+ const isClickInsideSidebar = sidebar.contains(event.target);
206
+ const isClickOnToggleBtn = sidebarToggle.contains(event.target);
207
+
208
+ if (!isClickInsideSidebar && !isClickOnToggleBtn &&
209
+ window.innerWidth < 768 &&
210
+ sidebar.classList.contains('active')) {
211
+ sidebar.classList.remove('active');
212
+ content.classList.remove('active');
213
+ }
214
+ });
215
+ }
216
+ },
217
+
218
+ emits: ['navigate', 'action', 'item-click']
219
+ }
220
+ </script>