@stonecrop/desktop 0.31.0 → 0.32.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.
@@ -1,297 +0,0 @@
1
- <template>
2
- <footer>
3
- <ul class="tabs">
4
- <li class="hidebreadcrumbs" @click="toggleBreadcrumbs" @keydown.enter="toggleBreadcrumbs">
5
- <a tabindex="0"><div :class="rotateHideTabIcon">×</div></a>
6
- </li>
7
- <li
8
- class="hometab"
9
- :style="{ display: breadcrumbsVisibile ? 'block' : 'none' }"
10
- @click="navigateHome"
11
- @keydown.enter="navigateHome">
12
- <router-link to="/" tabindex="0">
13
- <svg class="icon" aria-label="Home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
14
- <path d="M3 12l9-9 9 9" />
15
- <path d="M9 21V12h6v9" />
16
- </svg>
17
- </router-link>
18
- </li>
19
- <li
20
- :class="['searchtab', { 'search-active': searchVisible }]"
21
- :style="{ display: breadcrumbsVisibile ? 'block' : 'none' }">
22
- <a tabindex="0">
23
- <svg
24
- v-show="!searchVisible"
25
- class="icon search-icon"
26
- role="button"
27
- aria-label="Search"
28
- viewBox="0 0 24 24"
29
- fill="none"
30
- stroke="currentColor"
31
- stroke-width="2"
32
- @click="toggleSearch"
33
- @keydown.enter="toggleSearch">
34
- <circle cx="11" cy="11" r="7" />
35
- <path d="M21 21l-4.35-4.35" />
36
- </svg>
37
- <input
38
- v-show="searchVisible"
39
- ref="searchinput"
40
- v-model="searchText"
41
- type="text"
42
- placeholder="Search..."
43
- @click.stop
44
- @input="handleSearchInput($event)"
45
- @blur="handleSearch($event)"
46
- @keydown.enter="handleSearch($event)"
47
- @keydown.escape="toggleSearch" />
48
- </a>
49
- </li>
50
- <li
51
- v-for="breadcrumb in breadcrumbs"
52
- :key="breadcrumb.title"
53
- :style="{ display: breadcrumbsVisibile ? 'block' : 'none' }">
54
- <router-link tabindex="0" :to="breadcrumb.to"> {{ breadcrumb.title }} </router-link>
55
- </li>
56
- </ul>
57
- </footer>
58
- </template>
59
-
60
- <script setup lang="ts">
61
- import { computed, nextTick, ref, useTemplateRef } from 'vue'
62
-
63
- const { breadcrumbs = [] } = defineProps<{ breadcrumbs?: { title: string; to: string }[] }>()
64
-
65
- const breadcrumbsVisibile = ref(true)
66
- const searchVisible = ref(false)
67
- const searchText = ref('')
68
- const inputRef = useTemplateRef<HTMLInputElement>('searchinput')
69
-
70
- const rotateHideTabIcon = computed(() => {
71
- return breadcrumbsVisibile.value ? 'unrotated' : 'rotated'
72
- })
73
-
74
- const toggleBreadcrumbs = () => {
75
- breadcrumbsVisibile.value = !breadcrumbsVisibile.value
76
- }
77
-
78
- const toggleSearch = async () => {
79
- searchVisible.value = !searchVisible.value
80
- await nextTick(() => {
81
- inputRef.value?.focus()
82
- })
83
- }
84
-
85
- const handleSearchInput = (event: Event | MouseEvent) => {
86
- event.preventDefault()
87
- event.stopPropagation()
88
- }
89
-
90
- const handleSearch = async (event: FocusEvent | KeyboardEvent) => {
91
- event.preventDefault()
92
- event.stopPropagation()
93
- await toggleSearch()
94
- }
95
-
96
- const navigateHome = (/* event: MouseEvent | KeyboardEvent */) => {
97
- // navigate home
98
- }
99
- </script>
100
-
101
- <style scoped>
102
- footer {
103
- position: fixed;
104
- bottom: 0px;
105
- width: 100%;
106
- background-color: transparent;
107
- height: auto;
108
- min-height: 2.4rem;
109
- z-index: 100;
110
- text-align: left;
111
- font-size: 100%;
112
- display: flex;
113
- justify-content: right;
114
- padding: 0 0.75rem 0 0;
115
- box-sizing: border-box;
116
- }
117
- ul {
118
- display: flex;
119
- flex-direction: row-reverse;
120
- margin: 0;
121
- padding: 0;
122
- list-style: none;
123
- }
124
-
125
- .tabs li {
126
- float: left;
127
- list-style-type: none;
128
- position: relative;
129
- margin-left: -1px;
130
- }
131
-
132
- /* Base tab styling */
133
- .tabs a {
134
- float: left;
135
- padding: 0.5rem 1rem;
136
- height: 2.4rem;
137
- box-sizing: border-box;
138
- display: flex;
139
- align-items: center;
140
- justify-content: center;
141
- text-decoration: none;
142
- color: var(--sc-gray-60);
143
- background: var(--sc-btn-color);
144
- border: 1px solid var(--sc-btn-border);
145
- font-size: 0.85rem;
146
- font-family: var(--sc-font-family);
147
- transition: all 0.15s ease;
148
-
149
- /* Minimal ornamentation - subtle top radius only */
150
- border-top-left-radius: 2px;
151
- border-top-right-radius: 2px;
152
- }
153
-
154
- .tabs a:hover {
155
- background: var(--sc-btn-hover);
156
- }
157
-
158
- .tabs .router-link-active {
159
- z-index: 3;
160
- background: var(--sc-primary-color) !important;
161
- border-color: var(--sc-primary-color) !important;
162
- color: var(--sc-primary-text-color) !important;
163
- }
164
-
165
- /* Pseudo-elements removed - minimal ornamentation style */
166
-
167
- /* Hide breadcrumbs tab */
168
- .hidebreadcrumbs a {
169
- min-width: 2.4rem;
170
- width: 2.4rem;
171
- height: 2.4rem;
172
- padding: 0.5rem;
173
- background: var(--sc-btn-color);
174
- border: 1px solid var(--sc-btn-border);
175
- color: var(--sc-gray-60);
176
- }
177
-
178
- .hidebreadcrumbs a div {
179
- font-size: 1.2rem;
180
- }
181
-
182
- .rotated {
183
- transform: rotate(45deg);
184
- -webkit-transform: rotate(45deg);
185
- -moz-transform: rotate(45deg);
186
- -ms-transform: rotate(45deg);
187
- -o-transform: rotate(45deg);
188
- transition: transform 250ms ease;
189
- }
190
- .unrotated {
191
- transform: rotate(0deg);
192
- -webkit-transform: rotate(0deg);
193
- -moz-transform: rotate(0deg);
194
- -ms-transform: rotate(0deg);
195
- -o-transform: rotate(0deg);
196
- transition: transform 250ms ease;
197
- }
198
-
199
- li:active,
200
- li:hover,
201
- li:focus,
202
- li > a:active,
203
- li > a:hover,
204
- li > a:focus {
205
- z-index: 3;
206
- }
207
-
208
- a:active,
209
- a:hover,
210
- a:focus {
211
- outline: 2px solid var(--sc-input-active-border-color);
212
- z-index: 3;
213
- }
214
-
215
- /* Home tab */
216
- .hometab a {
217
- min-width: 2.4rem;
218
- width: 2.4rem;
219
- height: 2.4rem;
220
- padding: 0.5rem;
221
- background: var(--sc-btn-color);
222
- border: 1px solid var(--sc-btn-border);
223
- color: var(--sc-gray-60);
224
- }
225
-
226
- /* SVG icon styling */
227
- .icon {
228
- width: 1rem;
229
- height: 1rem;
230
- stroke: currentColor;
231
- flex-shrink: 0;
232
- }
233
-
234
- /* Search tab with animation */
235
- .searchtab {
236
- overflow: hidden;
237
- }
238
-
239
- .searchtab a {
240
- min-width: 2.4rem;
241
- height: 2.4rem;
242
- padding: 0.5rem;
243
- background: var(--sc-btn-color);
244
- border: 1px solid var(--sc-btn-border);
245
- color: var(--sc-gray-60);
246
- overflow: hidden;
247
- /* Animation for smooth expand/collapse */
248
- max-width: 2.4rem;
249
- transition:
250
- max-width 0.35s ease-in-out,
251
- padding 0.35s ease-in-out,
252
- background 0.2s ease;
253
- }
254
-
255
- .searchtab .search-icon {
256
- cursor: pointer;
257
- }
258
-
259
- .searchtab input {
260
- outline: none;
261
- border: 1px solid var(--sc-input-border-color);
262
- border-radius: 0.25rem;
263
- background-color: var(--sc-form-background);
264
- color: var(--sc-gray-80);
265
- text-align: left;
266
- font-size: 0.875rem;
267
- padding: 0.25rem 0.5rem;
268
- width: 180px;
269
- height: 1.5rem;
270
- flex-shrink: 0;
271
- opacity: 0;
272
- transition: opacity 0.2s ease-in-out;
273
- transition-delay: 0s;
274
- }
275
-
276
- .searchtab input:focus {
277
- border-color: var(--sc-input-active-border-color);
278
- outline: none;
279
- }
280
-
281
- .searchtab input::placeholder {
282
- color: var(--sc-input-label-color);
283
- }
284
-
285
- /* Search active state - expanded with animation */
286
- .searchtab.search-active a {
287
- max-width: 200px;
288
- min-width: auto;
289
- width: auto;
290
- padding: 0.5rem 0.75rem;
291
- }
292
-
293
- .searchtab.search-active input {
294
- opacity: 1;
295
- transition-delay: 0.15s;
296
- }
297
- </style>
package/src/index.ts DELETED
@@ -1,9 +0,0 @@
1
- import ActionSet from './components/ActionSet.vue'
2
- import CommandPalette from './components/CommandPalette.vue'
3
- import Desktop from './components/Desktop.vue'
4
- import SheetNav from './components/SheetNav.vue'
5
- import StonecropDesktop from './plugins'
6
- export type * from './types'
7
- export type { RouteAdapter, NavigationTarget, ActionEventPayload, RecordOpenEventPayload } from './types'
8
-
9
- export { ActionSet, CommandPalette, Desktop, SheetNav, StonecropDesktop }
@@ -1,21 +0,0 @@
1
- import { App, type Plugin } from 'vue'
2
-
3
- import ActionSet from '../components/ActionSet.vue'
4
- import CommandPalette from '../components/CommandPalette.vue'
5
- import Desktop from '../components/Desktop.vue'
6
- import SheetNav from '../components/SheetNav.vue'
7
-
8
- /**
9
- * This is the main plugin that will be used to register all the desktop components.
10
- * @public
11
- */
12
- const plugin: Plugin = {
13
- install: (app: App) => {
14
- app.component('ActionSet', ActionSet)
15
- app.component('CommandPalette', CommandPalette)
16
- app.component('Desktop', Desktop)
17
- app.component('SheetNav', SheetNav)
18
- },
19
- }
20
-
21
- export default plugin
@@ -1,5 +0,0 @@
1
- declare module '*.vue' {
2
- import { ComponentOptions } from 'vue'
3
- const Component: ComponentOptions
4
- export default Component
5
- }
@@ -1,107 +0,0 @@
1
- /**
2
- * Base type for elements in the Action Set
3
- * @public
4
- */
5
- export type BaseElement = {
6
- label: string
7
- show?: boolean
8
- }
9
-
10
- /**
11
- * Element actions
12
- * @public
13
- */
14
- export type ElementAction = BaseElement & {
15
- link?: string
16
- action?: () => void
17
- }
18
-
19
- /**
20
- * Button elements
21
- * @public
22
- */
23
- export type ButtonElement = BaseElement &
24
- ElementAction & {
25
- type: 'button'
26
- disabled?: boolean
27
- }
28
-
29
- /**
30
- * Dropdown elements
31
- * @public
32
- */
33
- export type DropdownElement = BaseElement & {
34
- type: 'dropdown'
35
- actions: ElementAction[]
36
- }
37
-
38
- /**
39
- * Superset of all element types in the Action Set
40
- * @public
41
- */
42
- export type ActionElements = ButtonElement | DropdownElement
43
-
44
- /**
45
- * Navigation target passed to RouteAdapter.navigate and emitted with the 'navigate' event
46
- * @public
47
- */
48
- export type NavigationTarget = {
49
- view: 'doctypes' | 'records' | 'record'
50
- doctype?: string
51
- recordId?: string
52
- }
53
-
54
- /**
55
- * Adapter that lets host applications (Nuxt, etc.) supply their own routing layer.
56
- * When provided as a prop, Desktop uses these functions instead of reaching into
57
- * the Vue Router instance baked into the Stonecrop registry.
58
- * @public
59
- */
60
- export type RouteAdapter = {
61
- /** Returns the active doctype key (e.g. 'plan', 'recipe'). Called inside computed — should read reactive state. */
62
- getCurrentDoctype: () => string
63
- /** Returns the active record ID, or '' when viewing a list. Called inside computed. */
64
- getCurrentRecordId: () => string
65
- /** Returns which of the three views is currently active. Called inside computed. */
66
- getCurrentView: () => 'doctypes' | 'records' | 'record'
67
- /** Perform the navigation. Called after the host app has handled any side effects. */
68
- navigate: (target: NavigationTarget) => void | Promise<void>
69
- }
70
-
71
- /**
72
- * Payload emitted with the 'action' event when the user triggers a declared action.
73
- *
74
- * Re-exported, not defined here: the shell emits it and `useClientAction` consumes it, and those
75
- * now live in different packages. It is declared in `@stonecrop/stonecrop` — which this package
76
- * already depends on — so the two cannot drift. Importing it from `@stonecrop/desktop` still
77
- * works and is still the natural place for a host to reach for it.
78
- *
79
- * @public
80
- */
81
- export type { ActionEventPayload } from '@stonecrop/stonecrop'
82
-
83
- /**
84
- * Payload emitted with the 'record:open' event
85
- * @public
86
- */
87
- export type RecordOpenEventPayload = {
88
- doctype: string
89
- recordId: string
90
- }
91
-
92
- /**
93
- * Payload emitted with the 'load-records' event when Desktop needs records for a list view
94
- * @public
95
- */
96
- export type LoadRecordsEventPayload = {
97
- doctype: string
98
- }
99
-
100
- /**
101
- * Payload emitted with the 'load-record' event when Desktop needs a single record
102
- * @public
103
- */
104
- export type LoadRecordEventPayload = {
105
- doctype: string
106
- recordId: string
107
- }