@stonecrop/desktop 0.4.16 → 0.4.17
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/dist/desktop.css +1 -1
- package/dist/desktop.js +2097 -2003
- package/dist/desktop.js.map +1 -1
- package/dist/desktop.umd.cjs +10 -10
- package/dist/desktop.umd.cjs.map +1 -1
- package/package.json +5 -5
- package/src/components/CommandPalette.vue +232 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/desktop",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.17",
|
|
4
4
|
"description": "Desktop-specific components for Stonecrop UI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"vue": "^3.5.13",
|
|
32
|
-
"@stonecrop/aform": "0.4.
|
|
33
|
-
"@stonecrop/
|
|
34
|
-
"@stonecrop/
|
|
35
|
-
"@stonecrop/
|
|
32
|
+
"@stonecrop/aform": "0.4.17",
|
|
33
|
+
"@stonecrop/atable": "0.4.17",
|
|
34
|
+
"@stonecrop/themes": "0.4.17",
|
|
35
|
+
"@stonecrop/stonecrop": "0.4.17"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@microsoft/api-documenter": "^7.26.10",
|
|
@@ -1,5 +1,235 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<Teleport to="body">
|
|
3
|
+
<Transition name="fade">
|
|
4
|
+
<div v-if="isOpen" class="command-palette-overlay" @click="closeModal">
|
|
5
|
+
<div class="command-palette" @click.stop>
|
|
6
|
+
<div class="command-palette-header">
|
|
7
|
+
<input
|
|
8
|
+
ref="input"
|
|
9
|
+
v-model="query"
|
|
10
|
+
type="text"
|
|
11
|
+
class="command-palette-input"
|
|
12
|
+
:placeholder="placeholder"
|
|
13
|
+
autofocus
|
|
14
|
+
@keydown="handleKeydown" />
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div v-if="results.length" class="command-palette-results">
|
|
18
|
+
<div
|
|
19
|
+
v-for="(result, index) in results"
|
|
20
|
+
:key="index"
|
|
21
|
+
class="command-palette-result"
|
|
22
|
+
:class="{ selected: index === selectedIndex }"
|
|
23
|
+
@click="selectResult(result)"
|
|
24
|
+
@mouseover="selectedIndex = index">
|
|
25
|
+
<div class="result-title">
|
|
26
|
+
<slot name="title" :result="result" />
|
|
27
|
+
</div>
|
|
28
|
+
<div class="result-content">
|
|
29
|
+
<slot name="content" :result="result" />
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
<div v-else-if="query && !results.length" class="command-palette-no-results">
|
|
34
|
+
<slot name="empty"> No results found for "{{ query }}" </slot>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</Transition>
|
|
39
|
+
</Teleport>
|
|
3
40
|
</template>
|
|
4
41
|
|
|
5
|
-
<script setup lang="ts"
|
|
42
|
+
<script setup lang="ts" generic="T">
|
|
43
|
+
import { ref, computed, watch, nextTick, useTemplateRef } from 'vue'
|
|
44
|
+
|
|
45
|
+
defineSlots<{
|
|
46
|
+
title?: { result: T }
|
|
47
|
+
content?: { result: T }
|
|
48
|
+
empty?: null
|
|
49
|
+
}>()
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
search,
|
|
53
|
+
isOpen = false,
|
|
54
|
+
placeholder = 'Type a command or search...',
|
|
55
|
+
maxResults = 10,
|
|
56
|
+
} = defineProps<{
|
|
57
|
+
search: (query: string) => T[]
|
|
58
|
+
isOpen?: boolean
|
|
59
|
+
placeholder?: string
|
|
60
|
+
maxResults?: number
|
|
61
|
+
}>()
|
|
62
|
+
|
|
63
|
+
const emit = defineEmits<{
|
|
64
|
+
select: [T]
|
|
65
|
+
close: []
|
|
66
|
+
}>()
|
|
67
|
+
|
|
68
|
+
const query = ref('')
|
|
69
|
+
const selectedIndex = ref(0)
|
|
70
|
+
const inputRef = useTemplateRef('input')
|
|
71
|
+
|
|
72
|
+
const results = computed(() => {
|
|
73
|
+
if (!query.value) return []
|
|
74
|
+
const results = search(query.value)
|
|
75
|
+
return results.slice(0, maxResults)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// reset search query when modal opens
|
|
79
|
+
watch(
|
|
80
|
+
() => isOpen,
|
|
81
|
+
async isOpen => {
|
|
82
|
+
if (isOpen) {
|
|
83
|
+
query.value = ''
|
|
84
|
+
selectedIndex.value = 0
|
|
85
|
+
await nextTick()
|
|
86
|
+
;(inputRef.value as HTMLInputElement)?.focus()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
// reset selected index when results change
|
|
92
|
+
watch(results, () => {
|
|
93
|
+
selectedIndex.value = 0
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const closeModal = () => {
|
|
97
|
+
emit('close')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const handleKeydown = (e: KeyboardEvent) => {
|
|
101
|
+
switch (e.key) {
|
|
102
|
+
case 'Escape':
|
|
103
|
+
closeModal()
|
|
104
|
+
break
|
|
105
|
+
case 'ArrowDown':
|
|
106
|
+
e.preventDefault()
|
|
107
|
+
if (results.value.length) {
|
|
108
|
+
selectedIndex.value = (selectedIndex.value + 1) % results.value.length
|
|
109
|
+
}
|
|
110
|
+
break
|
|
111
|
+
case 'ArrowUp':
|
|
112
|
+
e.preventDefault()
|
|
113
|
+
if (results.value.length) {
|
|
114
|
+
selectedIndex.value = (selectedIndex.value - 1 + results.value.length) % results.value.length
|
|
115
|
+
}
|
|
116
|
+
break
|
|
117
|
+
case 'Enter':
|
|
118
|
+
if (results.value.length && selectedIndex.value >= 0) {
|
|
119
|
+
selectResult(results.value[selectedIndex.value])
|
|
120
|
+
}
|
|
121
|
+
break
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const selectResult = (result: T) => {
|
|
126
|
+
emit('select', result)
|
|
127
|
+
closeModal()
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<style>
|
|
132
|
+
.fade-enter-active,
|
|
133
|
+
.fade-leave-active {
|
|
134
|
+
transition: opacity 0.2s ease;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.fade-enter-from,
|
|
138
|
+
.fade-leave-to {
|
|
139
|
+
opacity: 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.command-palette-overlay {
|
|
143
|
+
position: fixed;
|
|
144
|
+
top: 0;
|
|
145
|
+
left: 0;
|
|
146
|
+
width: 100%;
|
|
147
|
+
height: 100%;
|
|
148
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: flex-start;
|
|
151
|
+
justify-content: center;
|
|
152
|
+
z-index: 9999;
|
|
153
|
+
padding-top: 100px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.command-palette {
|
|
157
|
+
width: 600px;
|
|
158
|
+
max-width: 90%;
|
|
159
|
+
background-color: white;
|
|
160
|
+
border-radius: 8px;
|
|
161
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
162
|
+
overflow: hidden;
|
|
163
|
+
max-height: 80vh;
|
|
164
|
+
display: flex;
|
|
165
|
+
flex-direction: column;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.command-palette-header {
|
|
169
|
+
display: flex;
|
|
170
|
+
border-bottom: 1px solid #eaeaea;
|
|
171
|
+
padding: 12px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.command-palette-input {
|
|
175
|
+
flex: 1;
|
|
176
|
+
border: none;
|
|
177
|
+
outline: none;
|
|
178
|
+
font-size: 16px;
|
|
179
|
+
padding: 8px 12px;
|
|
180
|
+
background-color: transparent;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.command-palette-close {
|
|
184
|
+
background: transparent;
|
|
185
|
+
border: none;
|
|
186
|
+
font-size: 24px;
|
|
187
|
+
cursor: pointer;
|
|
188
|
+
color: #666;
|
|
189
|
+
padding: 0 8px;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.command-palette-close:hover {
|
|
193
|
+
color: #333;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.command-palette-results {
|
|
197
|
+
overflow-y: auto;
|
|
198
|
+
max-height: 60vh;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.command-palette-result {
|
|
202
|
+
padding: 12px 16px;
|
|
203
|
+
cursor: pointer;
|
|
204
|
+
border-bottom: 1px solid #f0f0f0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.command-palette-result:hover,
|
|
208
|
+
.command-palette-result.selected {
|
|
209
|
+
background-color: #f5f5f5;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.command-palette-result.selected {
|
|
213
|
+
background-color: rgba(132, 60, 3, 0.1);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.result-title {
|
|
217
|
+
font-weight: 500;
|
|
218
|
+
margin-bottom: 4px;
|
|
219
|
+
color: #333;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.result-content {
|
|
223
|
+
font-size: 14px;
|
|
224
|
+
color: #666;
|
|
225
|
+
white-space: nowrap;
|
|
226
|
+
overflow: hidden;
|
|
227
|
+
text-overflow: ellipsis;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.command-palette-no-results {
|
|
231
|
+
padding: 20px 16px;
|
|
232
|
+
text-align: center;
|
|
233
|
+
color: #666;
|
|
234
|
+
}
|
|
235
|
+
</style>
|