picocrank 1.12.5 → 1.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picocrank",
3
- "version": "1.12.5",
3
+ "version": "1.13.0",
4
4
  "description": "A set of zero dependency Vue components built on the Femtocrank global CSS theme.",
5
5
  "main": "vite.config.js",
6
6
  "scripts": {
@@ -11,6 +11,8 @@
11
11
  </button>
12
12
  </div>
13
13
 
14
+ <TopBar v-if="topBarEnabled" :navigation="navigation" />
15
+
14
16
  <Breadcrumbs v-if="breadcrumbs" />
15
17
 
16
18
  <slot name="toolbar" />
@@ -30,6 +32,7 @@
30
32
  import { Menu01Icon } from "@hugeicons/core-free-icons";
31
33
 
32
34
  import Breadcrumbs from "./Breadcrumbs.vue";
35
+ import TopBar from "./TopBar.vue";
33
36
 
34
37
  const emit = defineEmits(["toggleSidebar"]);
35
38
 
@@ -54,6 +57,14 @@
54
57
  type: Boolean,
55
58
  default: true,
56
59
  },
60
+ topBarEnabled: {
61
+ type: Boolean,
62
+ default: false,
63
+ },
64
+ navigation: {
65
+ type: Object,
66
+ default: null,
67
+ },
57
68
  });
58
69
  </script>
59
70
 
@@ -0,0 +1,132 @@
1
+ <template>
2
+ <nav class="topbar">
3
+ <div class="topbar-links flex-row">
4
+ <template v-for="link in navigationLinks" :key="link.name">
5
+ <!-- Render separator as a divider -->
6
+ <div v-if="link.type === 'separator'" class="separator"></div>
7
+ <!-- Render callback link -->
8
+ <a v-else-if="link.type === 'callback'" href="#" @click.prevent="handleLinkClick(() => link.callback())" class="button topbar-link">
9
+ <HugeiconsIcon :icon="link.icon" width="1em" height="1em" />
10
+ <span>{{ link.title }}</span>
11
+ </a>
12
+ <!-- Render HTML content -->
13
+ <div v-else-if="link.type === 'html'" v-html="link.html" class="topbar-html"></div>
14
+ <!-- Render router link -->
15
+ <router-link v-else v-bind="link.props || {}" :to="link.to || link.path" :class="['button', 'topbar-link', { active: isActive(link) }]">
16
+ <HugeiconsIcon :icon="link.icon" width="1em" height="1em" />
17
+ <span>{{ link.title }}</span>
18
+ </router-link>
19
+ </template>
20
+ </div>
21
+ </nav>
22
+ </template>
23
+
24
+ <script setup>
25
+ import { computed, inject } from 'vue'
26
+ import { HugeiconsIcon } from '@hugeicons/vue'
27
+
28
+ const props = defineProps({
29
+ navigation: {
30
+ type: Object,
31
+ default: null
32
+ }
33
+ })
34
+
35
+ // Try to get navigation from inject first, fall back to prop
36
+ const injectedNavigation = inject('navigation', null)
37
+
38
+ // Get navigation object - handle both component ref and direct object
39
+ const navigation = computed(() => {
40
+ const nav = props.navigation || injectedNavigation
41
+ if (!nav) return null
42
+
43
+ // If it's a component ref, access its exposed API
44
+ if (nav.value) {
45
+ return nav.value
46
+ }
47
+
48
+ // Otherwise, assume it's the navigation object directly
49
+ return nav
50
+ })
51
+
52
+ // Get navigation links and isActive function from navigation
53
+ // navigationLinks is a ref, so we return it directly (Vue will unwrap it in template)
54
+ const navigationLinks = computed(() => {
55
+ const nav = navigation.value
56
+ if (!nav || !nav.navigationLinks) {
57
+ return []
58
+ }
59
+ // Return the ref itself - Vue will unwrap it in the template
60
+ return nav.navigationLinks
61
+ })
62
+
63
+ // Get isActive function - create a function that accesses the navigation computed
64
+ function isActive(link) {
65
+ const nav = navigation.value
66
+ if (!nav || !nav.isActive) {
67
+ return false
68
+ }
69
+ return nav.isActive(link)
70
+ }
71
+
72
+ function handleLinkClick(callback = null) {
73
+ if (callback) {
74
+ callback()
75
+ }
76
+ }
77
+ </script>
78
+
79
+ <style scoped>
80
+ .topbar {
81
+ width: 100%;
82
+ }
83
+
84
+ .topbar-links {
85
+ display: flex;
86
+ align-items: center;
87
+ gap: 0.125rem;
88
+ flex-wrap: wrap;
89
+ }
90
+
91
+ .topbar-link {
92
+ display: flex;
93
+ align-items: center;
94
+ gap: 0.375rem;
95
+ padding: 0.5em 0.75em;
96
+ border-radius: 0.25em;
97
+ text-decoration: none;
98
+ color: inherit;
99
+ transition: background-color 0.15s ease;
100
+ white-space: nowrap;
101
+ border: 0;
102
+ font-weight: normal;
103
+ }
104
+
105
+ .topbar-link.active {
106
+ text-decoration: underline;
107
+ font-weight: 500;
108
+ }
109
+
110
+ .topbar-html {
111
+ display: flex;
112
+ align-items: center;
113
+ }
114
+
115
+ .separator {
116
+ width: 1px;
117
+ height: 1.5em;
118
+ background-color: currentColor;
119
+ opacity: 0.3;
120
+ margin: 0 0.125rem;
121
+ }
122
+
123
+ @media (prefers-color-scheme: dark) {
124
+ .topbar-link {
125
+ color: #f8f9fa;
126
+ }
127
+
128
+ .separator {
129
+ opacity: 0.5;
130
+ }
131
+ }
132
+ </style>
package/vue/views/App.vue CHANGED
@@ -1,7 +1,8 @@
1
1
  <template>
2
- <Header username = "Guest" @toggleSidebar="toggleSidebar" title = "PicoCrank" :logoUrl="logoUrl" :sidebarEnabled="sidebarEnabled">
2
+ <Header username = "Guest" @toggleSidebar="toggleSidebar" title = "PicoCrank" :logoUrl="logoUrl" :sidebarEnabled="sidebarEnabled" :navigation="navigation" :topBarEnabled="topBarEnabled">
3
3
  <template #toolbar>
4
4
  <QuickSearch
5
+ v-if="quickSearchEnabled"
5
6
  ref="quickSearchRef"
6
7
  placeholder="Search items..."
7
8
  :search-fields="['title', 'name']"
@@ -42,6 +43,8 @@
42
43
  const sidebar = ref(null);
43
44
  const navigation = ref(null);
44
45
  const sidebarEnabled = ref(true);
46
+ const quickSearchEnabled = ref(true);
47
+ const topBarEnabled = ref(false);
45
48
 
46
49
  function toggleSidebar() {
47
50
  if (sidebar.value) {
@@ -53,9 +56,21 @@
53
56
  sidebarEnabled.value = !sidebarEnabled.value;
54
57
  }
55
58
 
56
- // Provide sidebar state and toggle function for child components
59
+ function toggleQuickSearchEnabled() {
60
+ quickSearchEnabled.value = !quickSearchEnabled.value;
61
+ }
62
+
63
+ function toggleTopBarEnabled() {
64
+ topBarEnabled.value = !topBarEnabled.value;
65
+ }
66
+
67
+ // Provide sidebar, QuickSearch, and TopBar state and toggle functions for child components
57
68
  provide('sidebarEnabled', sidebarEnabled);
58
69
  provide('toggleSidebarEnabled', toggleSidebarEnabled);
70
+ provide('quickSearchEnabled', quickSearchEnabled);
71
+ provide('toggleQuickSearchEnabled', toggleQuickSearchEnabled);
72
+ provide('topBarEnabled', topBarEnabled);
73
+ provide('toggleTopBarEnabled', toggleTopBarEnabled);
59
74
 
60
75
  onMounted(() => {
61
76
  if (navigation.value) {
@@ -84,15 +99,17 @@
84
99
  sidebar.value.stick();
85
100
  }
86
101
 
87
- quickSearchRef.value.addItem({
88
- id: 'hello-world',
89
- title: 'Hello World',
90
- name: 'hello-world',
91
- description: 'This is a test item',
92
- category: 'Actions',
93
- type: 'callback',
94
- callback: helloWorld
95
- })
102
+ if (quickSearchRef.value) {
103
+ quickSearchRef.value.addItem({
104
+ id: 'hello-world',
105
+ title: 'Hello World',
106
+ name: 'hello-world',
107
+ description: 'This is a test item',
108
+ category: 'Actions',
109
+ type: 'callback',
110
+ callback: helloWorld
111
+ })
112
+ }
96
113
  });
97
114
 
98
115
  function helloWorld() {
@@ -1,10 +1,20 @@
1
1
  <template>
2
2
  <Section title = "Welcome" classes = "">
3
3
  <template #toolbar>
4
- <button @click="toggleSidebarEnabled" :class="sidebarEnabled ? 'neutral' : 'good'">
5
- <HugeiconsIcon :icon="Menu01Icon" width="1em" height="1em" />
6
- {{ sidebarEnabled ? 'Disable Sidebar' : 'Enable Sidebar' }}
7
- </button>
4
+ <div style="display: flex; gap: 1rem; align-items: center;">
5
+ <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
6
+ <input type="checkbox" :checked="sidebarEnabled" @change="toggleSidebarEnabled" />
7
+ <span>Enable Sidebar</span>
8
+ </label>
9
+ <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
10
+ <input type="checkbox" :checked="quickSearchEnabled" @change="toggleQuickSearchEnabled" />
11
+ <span>Enable QuickSearch</span>
12
+ </label>
13
+ <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;">
14
+ <input type="checkbox" :checked="topBarEnabled" @change="toggleTopBarEnabled" />
15
+ <span>Enable TopBar</span>
16
+ </label>
17
+ </div>
8
18
  </template>
9
19
  <p>
10
20
  Welcome to Femtocrank, a web-based tool for analyzing and visualizing the performance of your code.
@@ -83,7 +93,11 @@
83
93
  import { HighlighterIcon } from '@hugeicons/core-free-icons';
84
94
  import { Menu01Icon } from '@hugeicons/core-free-icons';
85
95
 
86
- // Inject sidebar state and toggle function from App.vue
96
+ // Inject sidebar, QuickSearch, and TopBar state and toggle functions from App.vue
87
97
  const sidebarEnabled = inject('sidebarEnabled');
88
98
  const toggleSidebarEnabled = inject('toggleSidebarEnabled');
99
+ const quickSearchEnabled = inject('quickSearchEnabled');
100
+ const toggleQuickSearchEnabled = inject('toggleQuickSearchEnabled');
101
+ const topBarEnabled = inject('topBarEnabled');
102
+ const toggleTopBarEnabled = inject('toggleTopBarEnabled');
89
103
  </script>