picocrank 1.24.0 → 1.25.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/AGENTS.md +2 -0
- package/docs/filter-query-v1.md +180 -0
- package/logo.svg +7 -7
- package/package.json +7 -5
- package/public/themes/README.md +19 -2
- package/public/themes/index.json +1 -5
- package/styles/dark-theme.css +16 -1
- package/supplemental-themes/README.md +9 -0
- package/supplemental-themes/catppuccin-latte-frappe/theme.css +276 -0
- package/supplemental-themes/dracula-alucard/theme.css +258 -0
- package/supplemental-themes/gruvbox-dark-light/theme.css +264 -0
- package/supplemental-themes/index.json +17 -0
- package/{public/themes → supplemental-themes}/waffles/theme.css +99 -0
- package/vite.config.js +91 -0
- package/vue/components/Header.vue +3 -3
- package/vue/components/Login.vue +1 -3
- package/vue/components/Section.vue +45 -14
- package/vue/components/Sidebar.vue +2 -1
- package/vue/components/SidebarNavLink.vue +6 -0
- package/vue/components/Table.vue +1113 -73
- package/vue/components/TableColumnFilterPopover.vue +263 -44
- package/vue/components/TableColumnOptionsPopover.vue +1116 -0
- package/vue/components/ThemeSwitcher.vue +119 -0
- package/vue/components/TopBar.vue +2 -2
- package/vue/composables/tableColumnPresets.js +296 -0
- package/vue/composables/tableFilterQuery.js +202 -0
- package/vue/composables/tableFilters.js +167 -3
- package/vue/composables/useCustomTheme.js +167 -24
- package/vue/data/examplePeople.js +2 -6
- package/vue/examples/App.vue +1 -1
- package/vue/examples/LoginExample.vue +1 -3
- package/vue/examples/TableExample.vue +329 -11
- package/vue/examples/TableRemoteExample.vue +112 -4
- package/vue/examples/ThemePreview.vue +17 -31
- package/public/themes/catppuccin/theme.css +0 -195
- package/public/themes/dracula/theme.css +0 -181
package/vite.config.js
CHANGED
|
@@ -4,9 +4,13 @@ import Components from 'unplugin-vue-components/vite'
|
|
|
4
4
|
import fs from 'node:fs'
|
|
5
5
|
import path from 'node:path'
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { createReadStream, existsSync } from 'node:fs'
|
|
8
|
+
import { stat } from 'node:fs/promises'
|
|
7
9
|
|
|
8
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
11
|
const themesDir = path.join(__dirname, 'public', 'themes')
|
|
12
|
+
const supplementalThemesDir = path.join(__dirname, 'supplemental-themes')
|
|
13
|
+
const includeSupplementalThemes = process.env.VITE_INCLUDE_SUPPLEMENTAL_THEMES !== 'false'
|
|
10
14
|
|
|
11
15
|
function scanThemeNames() {
|
|
12
16
|
if (!fs.existsSync(themesDir)) {
|
|
@@ -58,6 +62,92 @@ function themesIndexPlugin() {
|
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
|
|
65
|
+
function contentTypeFor(filePath) {
|
|
66
|
+
if (filePath.endsWith('.css')) {
|
|
67
|
+
return 'text/css; charset=utf-8'
|
|
68
|
+
}
|
|
69
|
+
if (filePath.endsWith('.json')) {
|
|
70
|
+
return 'application/json; charset=utf-8'
|
|
71
|
+
}
|
|
72
|
+
if (filePath.endsWith('.png')) {
|
|
73
|
+
return 'image/png'
|
|
74
|
+
}
|
|
75
|
+
if (filePath.endsWith('.jpg') || filePath.endsWith('.jpeg')) {
|
|
76
|
+
return 'image/jpeg'
|
|
77
|
+
}
|
|
78
|
+
if (filePath.endsWith('.webp')) {
|
|
79
|
+
return 'image/webp'
|
|
80
|
+
}
|
|
81
|
+
if (filePath.endsWith('.svg')) {
|
|
82
|
+
return 'image/svg+xml'
|
|
83
|
+
}
|
|
84
|
+
return 'application/octet-stream'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function copyDirectoryRecursive(sourceDir, targetDir) {
|
|
88
|
+
fs.mkdirSync(targetDir, { recursive: true })
|
|
89
|
+
|
|
90
|
+
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
91
|
+
const sourcePath = path.join(sourceDir, entry.name)
|
|
92
|
+
const targetPath = path.join(targetDir, entry.name)
|
|
93
|
+
|
|
94
|
+
if (entry.isDirectory()) {
|
|
95
|
+
copyDirectoryRecursive(sourcePath, targetPath)
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fs.copyFileSync(sourcePath, targetPath)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function supplementalThemesPlugin() {
|
|
104
|
+
return {
|
|
105
|
+
name: 'picocrank-supplemental-themes',
|
|
106
|
+
configureServer(server) {
|
|
107
|
+
if (!includeSupplementalThemes || !fs.existsSync(supplementalThemesDir)) {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
server.middlewares.use('/supplemental-themes', (req, res, next) => {
|
|
112
|
+
const requestPath = decodeURIComponent((req.url || '/').split('?')[0])
|
|
113
|
+
const relativePath = requestPath.replace(/^\/+/, '')
|
|
114
|
+
const filePath = path.resolve(supplementalThemesDir, relativePath)
|
|
115
|
+
|
|
116
|
+
if (!filePath.startsWith(path.resolve(supplementalThemesDir))) {
|
|
117
|
+
res.statusCode = 403
|
|
118
|
+
res.end('Forbidden')
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!existsSync(filePath)) {
|
|
123
|
+
next()
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
stat(filePath).then((fileStat) => {
|
|
128
|
+
if (!fileStat.isFile()) {
|
|
129
|
+
next()
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
res.setHeader('Content-Type', contentTypeFor(filePath))
|
|
134
|
+
createReadStream(filePath).pipe(res)
|
|
135
|
+
}).catch(() => {
|
|
136
|
+
next()
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
},
|
|
140
|
+
closeBundle() {
|
|
141
|
+
if (!includeSupplementalThemes || !fs.existsSync(supplementalThemesDir)) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const outDir = path.resolve(__dirname, 'dist', 'supplemental-themes')
|
|
146
|
+
copyDirectoryRecursive(supplementalThemesDir, outDir)
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
61
151
|
export default defineConfig({
|
|
62
152
|
base: '/',
|
|
63
153
|
build: {
|
|
@@ -93,6 +183,7 @@ export default defineConfig({
|
|
|
93
183
|
},
|
|
94
184
|
plugins: [
|
|
95
185
|
themesIndexPlugin(),
|
|
186
|
+
supplementalThemesPlugin(),
|
|
96
187
|
Components({
|
|
97
188
|
dirs: ['./vue/components/'],
|
|
98
189
|
extensions: ['vue'],
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
.header-actions :deep(button) {
|
|
206
206
|
border: 0;
|
|
207
207
|
border-radius: 0;
|
|
208
|
-
color: #fff;
|
|
208
|
+
color: var(--header-text-color, #fff);
|
|
209
209
|
background-color: transparent;
|
|
210
210
|
align-self: stretch;
|
|
211
211
|
display: inline-flex;
|
|
@@ -227,7 +227,7 @@
|
|
|
227
227
|
#sidebar-toggler-button {
|
|
228
228
|
border: 0;
|
|
229
229
|
border-radius: 0;
|
|
230
|
-
color: #fff;
|
|
230
|
+
color: var(--header-text-color, #fff);
|
|
231
231
|
background-color: transparent;
|
|
232
232
|
align-self: stretch;
|
|
233
233
|
display: inline-flex;
|
|
@@ -260,7 +260,7 @@ header .search-trigger,
|
|
|
260
260
|
header .search-trigger.neutral {
|
|
261
261
|
border: 0;
|
|
262
262
|
border-radius: 0;
|
|
263
|
-
color: #fff;
|
|
263
|
+
color: var(--header-text-color, #fff);
|
|
264
264
|
background-color: transparent;
|
|
265
265
|
align-self: stretch;
|
|
266
266
|
height: 100%;
|
package/vue/components/Login.vue
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<form @submit.prevent="handleLocalLogin" class="local-login-form">
|
|
8
8
|
<input
|
|
9
9
|
id="username"
|
|
10
|
+
type="text"
|
|
10
11
|
v-model="localLogin.username"
|
|
11
12
|
required
|
|
12
13
|
autocomplete="username"
|
|
@@ -240,11 +241,8 @@ defineExpose({
|
|
|
240
241
|
|
|
241
242
|
.local-login-form input {
|
|
242
243
|
padding: 0.75rem;
|
|
243
|
-
border: 1px solid var(--border-color, #ccc);
|
|
244
244
|
border-radius: 4px;
|
|
245
245
|
font-size: 1em;
|
|
246
|
-
background-color: var(--input-bg, #fff);
|
|
247
|
-
color: var(--text-color, #000);
|
|
248
246
|
width: 100%;
|
|
249
247
|
box-sizing: border-box;
|
|
250
248
|
}
|
|
@@ -1,27 +1,41 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<section :class
|
|
3
|
-
<div class
|
|
4
|
-
<div class
|
|
5
|
-
<h2 :title
|
|
6
|
-
<
|
|
2
|
+
<section :class="'with-header-and-content ' + classes" :id="id">
|
|
3
|
+
<div class="section-header flex-row">
|
|
4
|
+
<div class="fg1">
|
|
5
|
+
<h2 :title="subtitle">
|
|
6
|
+
<span v-if="icon" class="section-title-with-icon">
|
|
7
|
+
<HugeiconsIcon
|
|
8
|
+
:icon="icon"
|
|
9
|
+
class="section-title-icon"
|
|
10
|
+
width="1.1em"
|
|
11
|
+
height="1.1em"
|
|
12
|
+
:strokeWidth="2"
|
|
13
|
+
aria-hidden="true"
|
|
14
|
+
/>
|
|
15
|
+
<span class="section-title-text">
|
|
16
|
+
<slot name="title">{{ title }}</slot>
|
|
17
|
+
</span>
|
|
18
|
+
</span>
|
|
19
|
+
<slot v-else name="title">{{ title }}</slot>
|
|
7
20
|
</h2>
|
|
8
21
|
</div>
|
|
9
|
-
<div role
|
|
22
|
+
<div role="toolbar">
|
|
10
23
|
<slot name="toolbar" />
|
|
11
24
|
</div>
|
|
12
25
|
</div>
|
|
13
|
-
<div :class
|
|
26
|
+
<div :class="padding ? 'section-content padding' : 'section-content'">
|
|
14
27
|
<slot />
|
|
15
28
|
</div>
|
|
16
29
|
</section>
|
|
17
30
|
</template>
|
|
18
31
|
|
|
19
32
|
<script setup>
|
|
33
|
+
import { HugeiconsIcon } from '@hugeicons/vue'
|
|
20
34
|
|
|
21
|
-
|
|
35
|
+
defineProps({
|
|
22
36
|
title: {
|
|
23
37
|
type: String,
|
|
24
|
-
default:
|
|
38
|
+
default: 'Untitled Section',
|
|
25
39
|
},
|
|
26
40
|
padding: {
|
|
27
41
|
type: Boolean,
|
|
@@ -29,15 +43,32 @@ const props = defineProps({
|
|
|
29
43
|
},
|
|
30
44
|
subtitle: {
|
|
31
45
|
type: String,
|
|
32
|
-
default:
|
|
46
|
+
default: '',
|
|
33
47
|
},
|
|
34
48
|
id: {
|
|
35
49
|
type: String,
|
|
36
|
-
default:
|
|
50
|
+
default: '',
|
|
37
51
|
},
|
|
38
52
|
classes: {
|
|
39
53
|
type: String,
|
|
40
|
-
default:
|
|
54
|
+
default: '',
|
|
41
55
|
},
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
icon: {
|
|
57
|
+
type: [Object, Array, Function],
|
|
58
|
+
default: null,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<style scoped>
|
|
64
|
+
.section-title-with-icon {
|
|
65
|
+
display: inline-flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: 0.45em;
|
|
68
|
+
vertical-align: middle;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.section-title-icon {
|
|
72
|
+
flex: 0 0 auto;
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
@@ -154,6 +154,12 @@ html[data-theme="dark"] {
|
|
|
154
154
|
color: #f8f9fa;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
a:hover,
|
|
158
|
+
a:focus-visible {
|
|
159
|
+
background-color: var(--hover-background-color);
|
|
160
|
+
color: var(--hover-text-color, #fff);
|
|
161
|
+
}
|
|
162
|
+
|
|
157
163
|
.separator {
|
|
158
164
|
background-color: #444;
|
|
159
165
|
}
|