picocrank 1.0.0 → 1.1.1

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.md CHANGED
@@ -2,11 +2,11 @@
2
2
  <img alt = "project logo" src = "logo.png" width = "128" />
3
3
  <h1>PicoCrank</h1>
4
4
 
5
- CSS theme for high accessibility and zero dependencies
5
+ A set of zero dependency Vue components built on the [FemtoCrank](https://github.com/jamesread/femtocrank) global CSS theme.
6
6
 
7
7
  [![Maturity Badge](https://img.shields.io/badge/maturity-Production-brightgreen)](#none)
8
8
  [![Discord](https://img.shields.io/discord/846737624960860180?label=Discord%20Server)](https://discord.gg/jhYWWpNJ3v)
9
9
 
10
10
  </div>
11
11
 
12
- A set of zero dependency Vue components built on the FemtoCrank global CSS theme.
12
+ * npm: https://www.npmjs.com/package/picocrank
package/index.html CHANGED
@@ -1,6 +1,8 @@
1
1
  <html>
2
2
  <head>
3
3
  <title>PicoCrank</title>
4
+
5
+ <link rel = "shortcut icon" href = "logo.png" type = "image/png" />
4
6
  </head>
5
7
 
6
8
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picocrank",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
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": {
@@ -19,9 +19,9 @@
19
19
  "@hugeicons/core-free-icons": "^1.0.16",
20
20
  "@hugeicons/vue": "^1.0.3",
21
21
  "@vitejs/plugin-vue": "^6.0.1",
22
- "femtocrank": "^2.2.0",
22
+ "femtocrank": "^2.3.0",
23
23
  "unplugin-vue-components": "^29.0.0",
24
- "vite": "^6.3.5",
24
+ "vite": "^7.1.3",
25
25
  "vue": "^3.5.18",
26
26
  "vue-router": "^4.5.1"
27
27
  },
package/router.js CHANGED
@@ -5,8 +5,19 @@ import Welcome from './vue/views/Welcome.vue';
5
5
  const routes = [
6
6
  {
7
7
  path: '/',
8
- name: 'Home',
8
+ name: 'Welcome',
9
9
  component: Welcome,
10
+ },
11
+ {
12
+ path: '/table-example',
13
+ name: 'TableExample',
14
+ component: () => import('./vue/examples/TableExample.vue'),
15
+ },
16
+ {
17
+ path: '/view-item/:id',
18
+ name: 'ViewItem',
19
+ component: () => import('./vue/examples/ViewItem.vue'),
20
+ props: true,
10
21
  }
11
22
  ];
12
23
 
@@ -1,8 +1,29 @@
1
1
  <template>
2
2
  <header>
3
- <div class = "image-and-title flex-row">
3
+ <div class = "image-and-title flex-row" id = "sidebar-button">
4
4
  <img src = "/logo.png" alt = "Logo" class = "logo" />
5
5
  <h1>PicoCrank</h1>
6
6
  </div>
7
+
8
+ <Breadcrumbs v-if="breadcrumbs" />
9
+
10
+ <div class = "fg1"></div>
11
+
12
+ <div class = "user-info">
13
+ <span v-if="username">{{ username }}</span>
14
+ </div>
7
15
  </header>
8
16
  </template>
17
+
18
+ <script setup>
19
+ const props = defineProps({
20
+ breadcrumbs: {
21
+ type: Boolean,
22
+ default: false,
23
+ },
24
+ username: {
25
+ type: String,
26
+ default: "",
27
+ },
28
+ });
29
+ </script>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <section class = "with-header-and-content">
3
+ <div class = "section-header">
4
+ <h2>{{ title }}</h2>
5
+ </div>
6
+ <div :class = "padding ? 'section-content padding' : 'section-content'">
7
+ <slot />
8
+ </div>
9
+ </section>
10
+ </template>
11
+
12
+ <script setup>
13
+
14
+ const props = defineProps({
15
+ title: {
16
+ type: String,
17
+ default: "Untitled Section",
18
+ },
19
+ padding: {
20
+ type: Boolean,
21
+ default: true,
22
+ },
23
+ });
24
+ </script>
@@ -0,0 +1,129 @@
1
+ <template>
2
+ <table class = "row-hover">
3
+ <thead>
4
+ <th v-for="(header, index) in visibleHeaders" :key="index" @click="toggleSort(header)" :class="{ sortable: header.sortable }">
5
+ {{ header.label }}
6
+
7
+ <span v-if="header.sortable && sortBy === header.key">
8
+ <span v-if="sortDir === 'asc'">▲</span>
9
+ <span v-else>▼</span>
10
+ </span>
11
+ </th>
12
+ </thead>
13
+ <tbody>
14
+ <tr v-if="pagedItems.length === 0">
15
+ <td :colspan="visibleHeaders.length">No items found</td>
16
+ </tr>
17
+ <tr v-else v-for="(item, index) in pagedItems" :key="index">
18
+ <td v-for="(header, index) in visibleHeaders" :key="index" :class="{ hidden: header.hidden }">
19
+ <span v-if = "header.linkFunc">
20
+ <router-link :to="header.linkFunc(item)" class = "link">
21
+ {{ item[header.key] }}
22
+ </router-link>
23
+ </span>
24
+ <span v-else>
25
+ {{ item[header.key] }}
26
+ </span>
27
+ </td>
28
+ </tr>
29
+ </tbody>
30
+ </table>
31
+ <div class = "padding">
32
+ <Pagination :total = "total" v-model:page="page" v-model:page-size="pageSize" />
33
+ </div>
34
+ </template>
35
+
36
+ <script setup>
37
+ import { ref, computed, onMounted, watch } from 'vue';
38
+ import Pagination from './Pagination.vue';
39
+
40
+ const sortBy = ref(null);
41
+ const sortDir = ref('asc');
42
+ const page = ref(1);
43
+ const pageSize = ref(10);
44
+
45
+ const items = ref([])
46
+
47
+ const sortedItems = computed(() => {
48
+ if (!sortBy.value) return [...items.value];
49
+
50
+ const col = sortBy.value;
51
+
52
+ return [...items.value].sort((a, b) => {
53
+ const av = a[col];
54
+ const bv = b[col];
55
+ console.log("sort values", col, av, bv)
56
+
57
+ if (av === bv) return 0;
58
+ if (av === null || av === undefined) return 1;
59
+ if (bv === null || bv === undefined) return -1;
60
+ if (typeof av === 'string' && typeof bv === 'string') {
61
+ return sortDir.value === 'asc' ? av.localeCompare(bv) : bv.localeCompare(av);
62
+ }
63
+ if (typeof av === 'number' && typeof bv === 'number') {
64
+ return sortDir.value === 'asc' ? av - bv : bv - av;
65
+ }
66
+ if (typeof av === 'boolean' && typeof bv === 'boolean') {
67
+ return sortDir.value === 'asc' ? (av ? 1 : 0) - (bv ? 1 : 0) : (bv ? 1 : 0) - (av ? 1 : 0);
68
+ }
69
+ if (av < bv) return sortDir.value === 'asc' ? -1 : 1;
70
+ });
71
+ });
72
+
73
+ watch([sortedItems, pageSize], () => { page.value = 1 });
74
+
75
+ const pagedItems = computed(() => {
76
+ const start = (page.value - 1) * pageSize.value;
77
+ const ret = sortedItems.value.slice(start, start + pageSize.value);
78
+
79
+ return ret
80
+ });
81
+
82
+ const total = computed(() => sortedItems.value.length);
83
+ const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
84
+
85
+ const visibleHeaders = computed(() => {
86
+ return props.headers.map(header => ({
87
+ ...header,
88
+ hidden: header.hidden === true,
89
+ }));
90
+ });
91
+
92
+ const props = defineProps({
93
+ headers: {
94
+ type: Array,
95
+ default: () => ['id'],
96
+ },
97
+ data: {
98
+ type: Array,
99
+ default: () => [],
100
+ },
101
+ });
102
+
103
+ function toggleSort(header) {
104
+ if (sortBy.value === header.key) {
105
+ sortDir.value = sortDir.value === 'asc' ? 'desc' : 'asc';
106
+ } else {
107
+ sortBy.value = header.key;
108
+ sortDir.value = 'asc';
109
+ }
110
+
111
+ console.log(`Sorting by ${sortBy.value} in ${sortDir.value} order`);
112
+ }
113
+
114
+ onMounted(() => {
115
+ items.value = props.data;
116
+ })
117
+ </script>
118
+
119
+ <style scoped>
120
+ table thead th:hover {
121
+ cursor: pointer;
122
+ color: #0366d6;
123
+ }
124
+
125
+ td:first-child, th:first-child {
126
+ padding-left: 1rem;
127
+ }
128
+
129
+ </style>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <Section title="Table Example" :padding = "false">
3
+ <Table
4
+ :data = "data"
5
+ :headers = "headers"
6
+ />
7
+ </Section>
8
+ </template>
9
+
10
+ <script setup>
11
+ import { ref } from 'vue'
12
+
13
+ const headers = ref([
14
+ { key: "name", label: "Name", sortable: true, hidden: true, linkFunc: getLink },
15
+ { key: "age", label: "Age", sortable: true, hidden: false, linkFunc: null },
16
+ { key: "city", label: "City", sortable: true, hidden: false, link: null },
17
+ ])
18
+
19
+
20
+ function getLink(row) {
21
+ return {
22
+ name: "ViewItem",
23
+ params: { id: row.name.toLowerCase() }
24
+ }
25
+ }
26
+
27
+ const data = [
28
+ { name: "Alice", age: 30, city: "New York" },
29
+ { name: "Bob", age: 25, city: "Los Angeles" },
30
+ { name: "Charlie", age: 35, city: "Chicago" },
31
+ { name: "David", age: 28, city: "Houston" },
32
+ { name: "Eve", age: 22, city: "Phoenix" },
33
+ { name: "Frank", age: 40, city: "Philadelphia" },
34
+ { name: "Grace", age: 29, city: "San Antonio" },
35
+ { name: "Hannah", age: 32, city: "San Diego" },
36
+ { name: "Ian", age: 27, city: "Dallas" },
37
+ { name: "Jack", age: 31, city: "San Jose" },
38
+ { name: "Kathy", age: 26, city: "Austin" },
39
+ { name: "Leo", age: 33, city: "Jacksonville" },
40
+ { name: "Mia", age: 24, city: "Fort Worth" },
41
+ { name: "Nina", age: 38, city: "Columbus" },
42
+ { name: "Oscar", age: 36, city: "Charlotte" },
43
+ { name: "Paul", age: 34, city: "San Francisco" },
44
+ { name: "Quinn", age: 23, city: "Indianapolis" },
45
+ { name: "Rita", age: 37, city: "Seattle" },
46
+ { name: "Sam", age: 39, city: "Denver" },
47
+ { name: "Tina", age: 21, city: "Washington D.C." }
48
+ ]
49
+ </script>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <Section title="View Item" padding>
3
+ View Item
4
+ </Section>
5
+ </template>
package/vue/views/App.vue CHANGED
@@ -1,19 +1,18 @@
1
1
  <template>
2
- <Header />
2
+ <Header username = "Guest" breadcrumbs />
3
+ <div id = "layout">
4
+ <Sidebar ref = "sidebar" />
3
5
 
4
- <div id = "layout">
5
- <Sidebar ref = "sidebar" />
6
-
7
- <main>
8
6
  <div id = "content">
9
- <router-view />
7
+ <main>
8
+ <router-view />
9
+ </main>
10
10
 
11
11
  <footer>
12
12
  <span>PicoCrank</span>
13
13
  </footer>
14
14
  </div>
15
- </main>
16
- </div>
15
+ </div>
17
16
  </template>
18
17
 
19
18
  <script setup>
@@ -24,9 +23,16 @@
24
23
 
25
24
  onMounted(() => {
26
25
  sidebar.value.addNavigationLink({
26
+ id: 'welcome',
27
27
  title: "Welcome",
28
28
  path: "/",
29
29
  })
30
+ sidebar.value.addNavigationLink({
31
+ id: 'table-example',
32
+ title: "Table Example",
33
+ path: "/table-example",
34
+ })
35
+
30
36
  sidebar.value.open();
31
37
  sidebar.value.stick();
32
38
  });
@@ -1,16 +1,16 @@
1
1
  <template>
2
- <section class = "with-header-and-content">
3
- <div class = "section-header">
4
- Welcome
5
- </div>
6
- <div class = "section-content">
7
- <p>
8
- Welcome to Femtocrank, a web-based tool for analyzing and visualizing the performance of your code.
9
- To get started, you can either upload a file or paste your code directly into the editor.
10
- </p>
11
- <p>
12
- For more information on how to use Femtocrank, please refer to the <a href = "/docs">documentation</a>.
13
- </p>
14
- </div>
15
- </section>
2
+ <Section title = "Welcome">
3
+ <p>
4
+ Welcome to Femtocrank, a web-based tool for analyzing and visualizing the performance of your code.
5
+ To get started, you can either upload a file or paste your code directly into the editor.
6
+ </p>
7
+ <p>
8
+ For more information on how to use Femtocrank, please refer to the <a href = "/docs">documentation</a>.
9
+ </p>
10
+ </Section>
11
+
16
12
  </template>
13
+
14
+ <script setup>
15
+ import Section from '../components/Section.vue';
16
+ </script>