@vuecs/navigation 1.0.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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -0
  3. package/core/index.cjs +1 -0
  4. package/core/index.d.ts +2 -0
  5. package/core/index.mjs +1 -0
  6. package/core/package.json +5 -0
  7. package/dist/components/index.d.ts +3 -0
  8. package/dist/components/index.d.ts.map +1 -0
  9. package/dist/components/item.d.ts +26 -0
  10. package/dist/components/item.d.ts.map +1 -0
  11. package/dist/components/items.d.ts +27 -0
  12. package/dist/components/items.d.ts.map +1 -0
  13. package/dist/constants.d.ts +13 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/core/build.d.ts +4 -0
  16. package/dist/core/build.d.ts.map +1 -0
  17. package/dist/core/check.d.ts +3 -0
  18. package/dist/core/check.d.ts.map +1 -0
  19. package/dist/core/expansion.d.ts +6 -0
  20. package/dist/core/expansion.d.ts.map +1 -0
  21. package/dist/core/flatten.d.ts +3 -0
  22. package/dist/core/flatten.d.ts.map +1 -0
  23. package/dist/core/index.d.ts +12 -0
  24. package/dist/core/index.d.ts.map +1 -0
  25. package/dist/core/match.d.ts +3 -0
  26. package/dist/core/match.d.ts.map +1 -0
  27. package/dist/core/reduce.d.ts +8 -0
  28. package/dist/core/reduce.d.ts.map +1 -0
  29. package/dist/core/refresh.d.ts +3 -0
  30. package/dist/core/refresh.d.ts.map +1 -0
  31. package/dist/core/replace.d.ts +6 -0
  32. package/dist/core/replace.d.ts.map +1 -0
  33. package/dist/core/reset.d.ts +3 -0
  34. package/dist/core/reset.d.ts.map +1 -0
  35. package/dist/core/select.d.ts +4 -0
  36. package/dist/core/select.d.ts.map +1 -0
  37. package/dist/core/tier.d.ts +7 -0
  38. package/dist/core/tier.d.ts.map +1 -0
  39. package/dist/core/toggle.d.ts +4 -0
  40. package/dist/core/toggle.d.ts.map +1 -0
  41. package/dist/index.cjs +593 -0
  42. package/dist/index.cjs.map +1 -0
  43. package/dist/index.d.ts +15 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.mjs +579 -0
  46. package/dist/index.mjs.map +1 -0
  47. package/dist/module.d.ts +3 -0
  48. package/dist/module.d.ts.map +1 -0
  49. package/dist/provider/index.d.ts +3 -0
  50. package/dist/provider/index.d.ts.map +1 -0
  51. package/dist/provider/module.d.ts +5 -0
  52. package/dist/provider/module.d.ts.map +1 -0
  53. package/dist/provider/type.d.ts +8 -0
  54. package/dist/provider/type.d.ts.map +1 -0
  55. package/dist/store/index.d.ts +3 -0
  56. package/dist/store/index.d.ts.map +1 -0
  57. package/dist/store/module.d.ts +7 -0
  58. package/dist/store/module.d.ts.map +1 -0
  59. package/dist/store/type.d.ts +16 -0
  60. package/dist/store/type.d.ts.map +1 -0
  61. package/dist/type.d.ts +26 -0
  62. package/dist/type.d.ts.map +1 -0
  63. package/dist/utils/index.d.ts +2 -0
  64. package/dist/utils/index.d.ts.map +1 -0
  65. package/dist/utils/url.d.ts +2 -0
  66. package/dist/utils/url.d.ts.map +1 -0
  67. package/dist/vue.d.ts +8 -0
  68. package/dist/vue.d.ts.map +1 -0
  69. package/package.json +74 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,593 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var core = require('@vuecs/core');
6
+ var vue = require('vue');
7
+ var link = require('@vuecs/link');
8
+
9
+ let instance;
10
+ function useNavigationProvider(module) {
11
+ if (typeof instance !== 'undefined') {
12
+ return instance;
13
+ }
14
+ if (typeof module === 'undefined') {
15
+ throw new Error('A Navigation Provider must be set!');
16
+ }
17
+ instance = module;
18
+ return instance;
19
+ }
20
+ function setNavigationProvider(module) {
21
+ instance = module;
22
+ }
23
+ function createNavigationProvider(input) {
24
+ return input;
25
+ }
26
+
27
+ const StoreSymbol = Symbol.for('VLNavigationStore');
28
+ function isStoreInjected() {
29
+ if (!vue.hasInjectionContext()) {
30
+ return false;
31
+ }
32
+ const instance = vue.inject(StoreSymbol);
33
+ return !!instance;
34
+ }
35
+ function setupStore(app) {
36
+ const store = {
37
+ items: vue.ref([]),
38
+ itemsActive: vue.ref([])
39
+ };
40
+ if (typeof app === 'undefined') {
41
+ if (isStoreInjected()) {
42
+ return;
43
+ }
44
+ vue.provide(StoreSymbol, store);
45
+ return;
46
+ }
47
+ if (app._context && app._context.provides && app._context.provides[StoreSymbol]) {
48
+ return;
49
+ }
50
+ app.provide(StoreSymbol, store);
51
+ }
52
+ function injectStore() {
53
+ const instance = vue.inject(StoreSymbol);
54
+ if (!instance) {
55
+ throw new Error('The Store is not set.');
56
+ }
57
+ return instance;
58
+ }
59
+
60
+ function isNavigationItemMatch(one, two) {
61
+ if (!one || !two) {
62
+ return false;
63
+ }
64
+ if (one.id && two.id && one.id === two.id) {
65
+ return true;
66
+ }
67
+ if (one.name && two.name && one.name === two.name) {
68
+ return true;
69
+ }
70
+ if (one.url && two.url && one.url === two.url) {
71
+ return true;
72
+ }
73
+ if (one.children && two.children && one.children.length === two.children.length) {
74
+ for(let i = 0; i < one.children.length; i++){
75
+ if (!isNavigationItemMatch(one.children[i], two.children[i])) {
76
+ return false;
77
+ }
78
+ }
79
+ return true;
80
+ }
81
+ return false;
82
+ }
83
+
84
+ // --------------------------------------------------
85
+ function setNavigationExpansion(items, item, parentMatch = false) {
86
+ let matchInIteration = false;
87
+ for(let i = 0; i < items.length; i++){
88
+ const isMatch = isNavigationItemMatch(items[i], item);
89
+ let isChildMatch = false;
90
+ const { children } = items[i];
91
+ if (typeof children !== 'undefined') {
92
+ const { items: childItems, match: childMatch } = setNavigationExpansion(children, item, isMatch);
93
+ items[i].children = childItems;
94
+ isChildMatch = childMatch;
95
+ }
96
+ if (isMatch) {
97
+ items[i].active = true;
98
+ }
99
+ if (isMatch || isChildMatch) {
100
+ items[i].display = true;
101
+ items[i].displayChildren = true;
102
+ }
103
+ if (parentMatch) {
104
+ items[i].display = true;
105
+ }
106
+ if (!matchInIteration) {
107
+ matchInIteration = isMatch || isChildMatch;
108
+ }
109
+ }
110
+ if (matchInIteration) {
111
+ for(let i = 0; i < items.length; i++){
112
+ items[i].display = true;
113
+ }
114
+ }
115
+ return {
116
+ items,
117
+ match: matchInIteration
118
+ };
119
+ }
120
+
121
+ function resetNavigationItem(items, root = true) {
122
+ for(let i = 0; i < items.length; i++){
123
+ items[i].display = root;
124
+ items[i].displayChildren = false;
125
+ items[i].active = false;
126
+ const { children } = items[i];
127
+ if (typeof children !== 'undefined') {
128
+ items[i].children = resetNavigationItem(children, false);
129
+ }
130
+ }
131
+ return items;
132
+ }
133
+
134
+ function findNavigationItemsForTier(items, tier) {
135
+ const filterFn = (component)=>typeof component.tier !== 'undefined' && component.tier === tier;
136
+ return items.filter(filterFn);
137
+ }
138
+ function findNavigationItemForTier(items, tier) {
139
+ const data = findNavigationItemsForTier(items, tier);
140
+ if (data.length >= 1) {
141
+ return data[0];
142
+ }
143
+ return undefined;
144
+ }
145
+ function setTierForNavigationItems(items, tier) {
146
+ const mapFn = (component)=>{
147
+ component.tier = tier;
148
+ return component;
149
+ };
150
+ if (vue.isRef(items)) {
151
+ return items.value.map(mapFn);
152
+ }
153
+ return items.map(mapFn);
154
+ }
155
+ function removeTierFromNavigationItems(items, tier) {
156
+ const filterFn = (items)=>typeof items.tier === 'undefined' || items.tier !== tier;
157
+ if (vue.isRef(items)) {
158
+ return items.value.filter(filterFn);
159
+ }
160
+ return items.filter(filterFn);
161
+ }
162
+
163
+ function replaceNavigationTierItemActive(store, tier, item) {
164
+ const items = removeTierFromNavigationItems(store.itemsActive.value, tier);
165
+ if (item) {
166
+ item.tier = tier;
167
+ store.itemsActive.value = [
168
+ ...items,
169
+ item
170
+ ];
171
+ } else {
172
+ store.itemsActive.value = items;
173
+ }
174
+ }
175
+ function replaceNavigationTierItems(store, tier, items) {
176
+ const componentsExisting = removeTierFromNavigationItems(store.items.value, tier);
177
+ store.items.value = [
178
+ ...componentsExisting,
179
+ ...setTierForNavigationItems(items, tier)
180
+ ];
181
+ }
182
+
183
+ function refreshNavigationTierItems(store, tier) {
184
+ const components = resetNavigationItem(findNavigationItemsForTier(store.items.value, tier));
185
+ const component = findNavigationItemForTier(store.itemsActive.value, tier);
186
+ if (component) {
187
+ const { items } = setNavigationExpansion(components, component);
188
+ replaceNavigationTierItems(store, tier, items);
189
+ return;
190
+ }
191
+ replaceNavigationTierItems(store, tier, components);
192
+ }
193
+
194
+ async function buildNavigationForTier(store, tier, itemsActive) {
195
+ if (typeof itemsActive === 'undefined' || itemsActive.length === 0) {
196
+ let tierStartIndex = 0;
197
+ const tierEndIndex = tier;
198
+ itemsActive = [];
199
+ while(tierStartIndex <= tierEndIndex){
200
+ const component = findNavigationItemForTier(store.itemsActive.value, tierStartIndex);
201
+ if (!component) {
202
+ break;
203
+ }
204
+ itemsActive.push(component);
205
+ tierStartIndex++;
206
+ }
207
+ }
208
+ const items = await useNavigationProvider().getItems(tier, itemsActive);
209
+ if (typeof items === 'undefined') {
210
+ return false;
211
+ }
212
+ replaceNavigationTierItems(store, tier, items);
213
+ refreshNavigationTierItems(store, tier);
214
+ return true;
215
+ }
216
+
217
+ function flattenNestedNavigationItems(items) {
218
+ const output = [];
219
+ for(let i = 0; i < items.length; i++){
220
+ const { children, ...data } = items[i];
221
+ output.push(data);
222
+ if (children && children.length > 0) {
223
+ output.push(...flattenNestedNavigationItems([
224
+ ...children
225
+ ]));
226
+ }
227
+ }
228
+ return output;
229
+ }
230
+
231
+ async function selectNavigationTierItem(store, tier, component) {
232
+ const isMatch = isNavigationItemMatch(findNavigationItemForTier(store.itemsActive.value, tier), component);
233
+ if (isMatch) {
234
+ return;
235
+ }
236
+ replaceNavigationTierItemActive(store, tier, component);
237
+ refreshNavigationTierItems(store, tier);
238
+ tier++;
239
+ // eslint-disable-next-line no-constant-condition
240
+ while(true){
241
+ const built = await buildNavigationForTier(store, tier);
242
+ if (!built) {
243
+ break;
244
+ }
245
+ tier++;
246
+ }
247
+ }
248
+
249
+ function toggleNavigation(store, tier, component) {
250
+ const isMatch = component.displayChildren || isNavigationItemMatch(findNavigationItemForTier(store.itemsActive.value, tier), component);
251
+ if (isMatch) {
252
+ replaceNavigationTierItemActive(store, tier, undefined);
253
+ } else {
254
+ replaceNavigationTierItemActive(store, tier, component);
255
+ }
256
+ refreshNavigationTierItems(store, tier);
257
+ }
258
+
259
+ function isAbsoluteURL(str) {
260
+ return str.substring(0, 7) === 'http://' || str.substring(0, 8) === 'https://';
261
+ }
262
+
263
+ var SlotName;
264
+ (function(SlotName) {
265
+ SlotName["ITEM"] = "item";
266
+ SlotName["SEPARATOR"] = "separator";
267
+ SlotName["LINK"] = "link";
268
+ SlotName["SUB"] = "sub";
269
+ SlotName["SUB_TITLE"] = "sub-title";
270
+ SlotName["SUB_ITEMS"] = "sub-items";
271
+ })(SlotName || (SlotName = {}));
272
+ var ElementType;
273
+ (function(ElementType) {
274
+ ElementType["LINK"] = "link";
275
+ ElementType["SEPARATOR"] = "separator";
276
+ })(ElementType || (ElementType = {}));
277
+
278
+ const VCNavItem = vue.defineComponent({
279
+ props: {
280
+ tier: {
281
+ type: Number,
282
+ default: 0
283
+ },
284
+ component: {
285
+ type: Object,
286
+ required: true
287
+ }
288
+ },
289
+ setup (props, { slots }) {
290
+ const store = injectStore();
291
+ const component = vue.toRef(props, 'component');
292
+ const selectComponent = async (value)=>{
293
+ await selectNavigationTierItem(store, props.tier, value);
294
+ };
295
+ const toggleComponentExpansion = async (value)=>toggleNavigation(store, props.tier, value);
296
+ return ()=>{
297
+ const buildItem = ()=>{
298
+ let item;
299
+ switch(component.value.type){
300
+ case ElementType.SEPARATOR:
301
+ {
302
+ const hasSlot = core.hasNormalizedSlot(SlotName.SEPARATOR, slots);
303
+ if (hasSlot) {
304
+ item = core.normalizeSlot(SlotName.SEPARATOR, {
305
+ component: component.value
306
+ }, slots);
307
+ } else {
308
+ item = vue.h('div', {
309
+ class: 'nav-separator'
310
+ }, component.value.name);
311
+ }
312
+ break;
313
+ }
314
+ default:
315
+ {
316
+ if (typeof component.value.children === 'undefined') {
317
+ const hasSlot = core.hasNormalizedSlot(SlotName.LINK, slots);
318
+ if (hasSlot) {
319
+ item = core.normalizeSlot(SlotName.LINK, {
320
+ component: component.value,
321
+ selectComponent,
322
+ isActive: component.value.active
323
+ }, slots);
324
+ } else {
325
+ const linkProps = {
326
+ active: component.value.active,
327
+ disabled: false,
328
+ prefetch: true
329
+ };
330
+ if (component.value.url) {
331
+ if (isAbsoluteURL(component.value.url) || component.value.url.startsWith('#')) {
332
+ linkProps.href = component.value.url;
333
+ if (component.value.urlTarget) {
334
+ linkProps.target = component.value.urlTarget;
335
+ }
336
+ } else {
337
+ linkProps.to = component.value.url;
338
+ }
339
+ }
340
+ item = vue.h(link.VCLink, {
341
+ class: [
342
+ 'nav-link',
343
+ {
344
+ 'root-link': component.value.root
345
+ }
346
+ ],
347
+ ...linkProps,
348
+ onClicked () {
349
+ if (!component.value.url) {
350
+ return selectComponent.call(null, component.value);
351
+ }
352
+ return undefined;
353
+ },
354
+ onClick () {
355
+ return selectComponent.call(null, component.value);
356
+ }
357
+ }, {
358
+ default: ()=>[
359
+ ...component.value.icon ? [
360
+ vue.h('i', {
361
+ class: component.value.icon
362
+ })
363
+ ] : [],
364
+ vue.h('span', {
365
+ class: 'nav-link-text'
366
+ }, [
367
+ component.value.name
368
+ ])
369
+ ]
370
+ });
371
+ }
372
+ } else if (core.hasNormalizedSlot(SlotName.SUB, slots)) {
373
+ item = core.normalizeSlot(SlotName.SUB, {
374
+ component: component.value,
375
+ selectComponent,
376
+ toggleComponentExpansion
377
+ }, slots);
378
+ } else {
379
+ let title;
380
+ if (core.hasNormalizedSlot(SlotName.SUB_TITLE, slots)) {
381
+ title = core.normalizeSlot(SlotName.SUB_TITLE, {
382
+ component: component.value,
383
+ selectComponent,
384
+ toggleComponentExpansion
385
+ });
386
+ } else {
387
+ title = vue.h('div', {
388
+ class: 'nav-sub-title',
389
+ onClick ($event) {
390
+ $event.preventDefault();
391
+ return toggleComponentExpansion.call(null, component.value);
392
+ }
393
+ }, [
394
+ [
395
+ ...component.value.icon ? [
396
+ vue.h('i', {
397
+ class: component.value.icon
398
+ })
399
+ ] : [],
400
+ vue.h('span', {
401
+ class: 'nav-link-text'
402
+ }, [
403
+ component.value.name
404
+ ])
405
+ ]
406
+ ]);
407
+ }
408
+ let items;
409
+ if (core.hasNormalizedSlot(SlotName.SUB_ITEMS, slots)) {
410
+ items = core.normalizeSlot(SlotName.SUB_ITEMS, {
411
+ component: component.value,
412
+ selectComponent,
413
+ toggleComponentExpansion
414
+ });
415
+ } else if (component.value.displayChildren) {
416
+ const navigationComponents = vue.resolveComponent('VCNavItems');
417
+ items = vue.h(navigationComponents, {
418
+ class: 'list-unstyled nav-sub-items',
419
+ tier: props.tier,
420
+ entities: component.value.children
421
+ });
422
+ }
423
+ item = [
424
+ title,
425
+ items
426
+ ];
427
+ }
428
+ break;
429
+ }
430
+ }
431
+ return item;
432
+ };
433
+ return vue.h('div', {
434
+ class: [
435
+ 'nav-item',
436
+ {
437
+ active: component.value.active || component.value.displayChildren
438
+ }
439
+ ]
440
+ }, [
441
+ buildItem()
442
+ ]);
443
+ };
444
+ }
445
+ });
446
+
447
+ const VCNavItems = vue.defineComponent({
448
+ props: {
449
+ tier: {
450
+ type: Number,
451
+ default: 0
452
+ },
453
+ entities: {
454
+ type: Array,
455
+ default: undefined
456
+ }
457
+ },
458
+ setup (props, { slots }) {
459
+ const store = injectStore();
460
+ const items = vue.computed(()=>{
461
+ if (typeof props.entities !== 'undefined') {
462
+ return props.entities;
463
+ }
464
+ return findNavigationItemsForTier(store.items.value, props.tier);
465
+ });
466
+ const buildChild = (context)=>{
467
+ if (core.hasNormalizedSlot(SlotName.ITEM, slots)) {
468
+ return core.normalizeSlot(SlotName.ITEM, context, slots);
469
+ }
470
+ return vue.h(VCNavItem, context);
471
+ };
472
+ const buildChildren = ()=>{
473
+ const entities = [];
474
+ if (items.value) {
475
+ for(let i = 0; i < items.value.length; i++){
476
+ if (items.value[i].display) {
477
+ entities.push(vue.h('li', {
478
+ key: i
479
+ }, [
480
+ buildChild({
481
+ tier: props.tier,
482
+ component: items.value[i]
483
+ })
484
+ ]));
485
+ }
486
+ }
487
+ }
488
+ return entities;
489
+ };
490
+ return ()=>vue.h('ul', {
491
+ class: 'nav-items'
492
+ }, [
493
+ buildChildren()
494
+ ]);
495
+ }
496
+ });
497
+
498
+ async function buildNavigation(context = {}) {
499
+ const store = injectStore();
500
+ const navigationProvider = useNavigationProvider();
501
+ let itemsActive = [];
502
+ if (typeof context.itemsActive !== 'undefined') {
503
+ itemsActive = context.itemsActive;
504
+ } else if (context.route) {
505
+ if (typeof navigationProvider.getItemsActiveByRoute !== 'undefined') {
506
+ itemsActive = await navigationProvider.getItemsActiveByRoute(context.route);
507
+ } else if (typeof navigationProvider.getItemsActiveByURL !== 'undefined') {
508
+ itemsActive = await navigationProvider.getItemsActiveByURL(context.route.fullPath);
509
+ }
510
+ } else if (context.url && typeof navigationProvider.getItemsActiveByURL !== 'undefined') {
511
+ itemsActive = await navigationProvider.getItemsActiveByURL(context.url);
512
+ }
513
+ if (itemsActive.length > 0) {
514
+ for(let i = 0; i < itemsActive.length; i++){
515
+ if (typeof itemsActive[i].tier === 'undefined') {
516
+ itemsActive[i].tier = i;
517
+ }
518
+ }
519
+ }
520
+ let tierIndex = 0;
521
+ let url;
522
+ if (typeof context.url === 'string') {
523
+ url = context.url;
524
+ } else if (typeof context.route !== 'undefined') {
525
+ url = context.route.fullPath;
526
+ }
527
+ // eslint-disable-next-line no-constant-condition
528
+ while(true){
529
+ let items = await navigationProvider.getItems(tierIndex, itemsActive);
530
+ if (!items || items.length === 0) {
531
+ break;
532
+ }
533
+ // ensure tier property
534
+ items = setTierForNavigationItems(items, tierIndex);
535
+ let currentItem = findNavigationItemForTier(itemsActive, tierIndex);
536
+ if (!currentItem) {
537
+ if (url) {
538
+ const urlMatches = items.filter((item)=>isNavigationItemMatch(item, {
539
+ url
540
+ }));
541
+ if (urlMatches.length > 0) {
542
+ [currentItem] = urlMatches;
543
+ }
544
+ }
545
+ if (!currentItem) {
546
+ const defaultItem = items.filter((item)=>item.default);
547
+ if (defaultItem.length > 0) {
548
+ currentItem = defaultItem;
549
+ } else {
550
+ [currentItem] = items;
551
+ }
552
+ }
553
+ currentItem.tier = tierIndex;
554
+ itemsActive.push(currentItem);
555
+ }
556
+ if (!currentItem) {
557
+ continue;
558
+ }
559
+ replaceNavigationTierItemActive(store, tierIndex, currentItem);
560
+ await buildNavigationForTier(store, tierIndex, itemsActive);
561
+ tierIndex++;
562
+ }
563
+ }
564
+
565
+ function install(instance, options) {
566
+ if (options.provider) {
567
+ setNavigationProvider(options.provider);
568
+ }
569
+ setupStore(instance);
570
+ core.applyPluginBaseOptions(instance, options);
571
+ Object.entries({
572
+ VCNavItem,
573
+ VCNavItems
574
+ }).forEach(([componentName, component])=>{
575
+ instance.component(componentName, component);
576
+ });
577
+ }
578
+ var index = {
579
+ install
580
+ };
581
+
582
+ exports.VCNavItem = VCNavItem;
583
+ exports.VCNavItems = VCNavItems;
584
+ exports.buildNavigation = buildNavigation;
585
+ exports.buildNavigationForTier = buildNavigationForTier;
586
+ exports.createNavigationProvider = createNavigationProvider;
587
+ exports.default = index;
588
+ exports.flattenNestedNavigationItems = flattenNestedNavigationItems;
589
+ exports.install = install;
590
+ exports.setNavigationProvider = setNavigationProvider;
591
+ exports.useNavigationProvider = useNavigationProvider;
592
+ module.exports = Object.assign(exports.default, exports);
593
+ //# sourceMappingURL=index.cjs.map