@stacksjs/defaults 0.75.136 → 0.75.137
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,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-dashboard
|
|
3
|
-
description: Use when building or customizing the Stacks admin dashboard, including dashboard pages, model management views, analytics widgets, commerce dashboards, content management, settings panels, deployment monitoring, job/queue management, or the
|
|
3
|
+
description: Use when building or customizing the Stacks admin dashboard, including dashboard pages, model management views, analytics widgets, commerce dashboards, content management, settings panels, deployment monitoring, job/queue management, or the 414 built-in dashboard components. Covers the dashboard system at storage/framework/defaults/.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -8,7 +8,7 @@ allowed-tools: Read Edit Write Bash Grep Glob
|
|
|
8
8
|
|
|
9
9
|
# Stacks Dashboard
|
|
10
10
|
|
|
11
|
-
The Stacks admin dashboard provides a full-featured admin panel with 100+ route views,
|
|
11
|
+
The Stacks admin dashboard provides a full-featured admin panel with 100+ route views, 414 components, and a multi-section layout.
|
|
12
12
|
|
|
13
13
|
## Key Paths
|
|
14
14
|
- Dashboard components: `storage/framework/defaults/resources/components/Dashboard/`
|
package/ide/vscode/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"publisher": "Stacks",
|
|
3
3
|
"name": "vscode-stacks",
|
|
4
4
|
"displayName": "Stacks",
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.137",
|
|
6
6
|
"description": "Stacks, built into VS Code: stx templates, pickier linting and formatting, encrypted env files, the dev server preview and every buddy command. No other extensions required.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"funding": "https://github.com/sponsors/chrisbbreuer",
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.137",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@iconify-json/f7": "^1.2.2",
|
|
57
57
|
"@iconify-json/hugeicons": "^1.2.35",
|
|
58
|
-
"@stacksjs/mobile": "0.75.
|
|
58
|
+
"@stacksjs/mobile": "0.75.137",
|
|
59
59
|
"@stacksjs/sanitizer": "^0.2.422",
|
|
60
60
|
"@xterm/addon-fit": "^0.11.0",
|
|
61
61
|
"@xterm/xterm": "^6.0.0",
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
interface NativeBarButtonProps {
|
|
3
|
+
/** Where the button goes, such as `/m/messages`. */
|
|
4
|
+
href: string
|
|
5
|
+
/** What a screen reader calls the button. */
|
|
6
|
+
label: string
|
|
7
|
+
/** The SF Symbol the native bar draws for it (`bubble.left`), drawn here with its Iconify stand-in. */
|
|
8
|
+
symbol?: string
|
|
9
|
+
/** An Iconify class for a symbol the stand-ins below don't cover. */
|
|
10
|
+
icon?: string
|
|
11
|
+
/** A picture (the account's own photo), drawn round inside the button. */
|
|
12
|
+
image?: string
|
|
13
|
+
/** A short title instead of the symbol: an account's initials. */
|
|
14
|
+
title?: string
|
|
15
|
+
/** A count on the button. Zero and empty draw nothing. */
|
|
16
|
+
badge?: string | number
|
|
17
|
+
/** `count` draws the number, `dot` a red dot (unread notifications). */
|
|
18
|
+
badgeStyle?: 'count' | 'dot'
|
|
19
|
+
/** `accent` sits a shade stronger in dark mode (chat); `account` is the default. */
|
|
20
|
+
variant?: 'accent' | 'account'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { symbol = '', icon = '' } = defineProps<NativeBarButtonProps>()
|
|
24
|
+
|
|
25
|
+
// The same names the native bar takes (mobileHeaderOptions-style rightButtons),
|
|
26
|
+
// so one list of buttons describes both. Written out so the stylesheet
|
|
27
|
+
// generator sees every class.
|
|
28
|
+
const standIns: Record<string, string> = {
|
|
29
|
+
'bubble.left': 'i-lucide-message-square',
|
|
30
|
+
'bubble.left.and.bubble.right': 'i-lucide-messages-square',
|
|
31
|
+
'person.crop.circle': 'i-lucide-circle-user-round',
|
|
32
|
+
'bell': 'i-lucide-bell',
|
|
33
|
+
'gearshape': 'i-lucide-settings',
|
|
34
|
+
'plus': 'i-lucide-plus',
|
|
35
|
+
'magnifyingglass': 'i-lucide-search',
|
|
36
|
+
'square.and.arrow.up': 'i-lucide-share',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const glyphClass = `${icon || standIns[symbol] || 'i-lucide-circle-user-round'} native-bar-glyph`
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<script client>
|
|
43
|
+
import { haptics } from '@stacksjs/mobile'
|
|
44
|
+
|
|
45
|
+
// A round button at the top right of a phone screen, drawn exactly as the
|
|
46
|
+
// app's native bar draws its buttons (Craft's accountNavigationButton in
|
|
47
|
+
// CraftNativeScreen.swift): a 44pt circle tinted with the accent, a 20pt
|
|
48
|
+
// glyph, a 36pt photo inset in the circle, a count badge or a red dot at its
|
|
49
|
+
// top right. The native screens (Today, Calendar) and the web screens of the
|
|
50
|
+
// same app then show one set of buttons, not two that almost match.
|
|
51
|
+
//
|
|
52
|
+
// Every prop through useReactiveProp, under names of its own: read as plain
|
|
53
|
+
// {{ label }}, a host's client signal of the same name would be shown instead.
|
|
54
|
+
// What shows (picture, title or glyph) is written in the template itself: a
|
|
55
|
+
// derived only the template reads is dropped by the bundler's tree-shake.
|
|
56
|
+
const barHref = useReactiveProp<string>('href', '')
|
|
57
|
+
const barLabel = useReactiveProp<string>('label', '')
|
|
58
|
+
const barImage = useReactiveProp<string>('image', '')
|
|
59
|
+
const barTitle = useReactiveProp<string>('title', '')
|
|
60
|
+
const barBadge = useReactiveProp<string | number>('badge', '')
|
|
61
|
+
const barBadgeStyle = useReactiveProp<string>('badgeStyle', 'count')
|
|
62
|
+
const barVariant = useReactiveProp<string>('variant', 'account')
|
|
63
|
+
|
|
64
|
+
const barCount = derived(() => {
|
|
65
|
+
const value = barBadge()
|
|
66
|
+
return value === '' || value === 0 || value === '0' || value == null ? '' : Number(value) > 99 ? '99+' : String(value)
|
|
67
|
+
})
|
|
68
|
+
const barDot = derived(() => barBadgeStyle() === 'dot')
|
|
69
|
+
const barPhoto = derived(() => (barImage() ? `background-image: url("${String(barImage()).replace(/"/g, '%22')}")` : ''))
|
|
70
|
+
|
|
71
|
+
function tapBarButton(): void {
|
|
72
|
+
void haptics.selection()
|
|
73
|
+
}
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<a :href="barHref()" class="native-bar-button" :class="barVariant() === 'accent' ? 'native-bar-accent' : ''" draggable="false" data-stx-link data-stx-prefetch="eager" @click="tapBarButton()">
|
|
77
|
+
<span class="native-bar-label">{{ barLabel() }}<span :if="barCount()">, {{ barCount() }} unread</span></span>
|
|
78
|
+
<span :if="barImage()" aria-hidden="true" class="native-bar-photo" :style="barPhoto()"></span>
|
|
79
|
+
<span :if="!barImage() && barTitle()" aria-hidden="true" class="native-bar-title">{{ barTitle() }}</span>
|
|
80
|
+
<!-- :show, not :if: an :if branch is re-rendered in the browser, where the
|
|
81
|
+
server's glyphClass doesn't exist, and the glyph silently never drew. -->
|
|
82
|
+
<i :show="!barImage() && !barTitle()" aria-hidden="true" class="{{ glyphClass }}"></i>
|
|
83
|
+
<span :if="barCount() && !barDot()" aria-hidden="true" class="native-bar-count">{{ barCount() }}</span>
|
|
84
|
+
<span :if="barCount() && barDot()" aria-hidden="true" class="native-bar-dot"></span>
|
|
85
|
+
</a>
|
|
86
|
+
|
|
87
|
+
<style>
|
|
88
|
+
/* The values are Craft's (CraftNativeScreen.swift, accountNavigationButton):
|
|
89
|
+
keep them in step, or the web and native screens drift apart again. */
|
|
90
|
+
.native-bar-button {
|
|
91
|
+
position: relative;
|
|
92
|
+
display: inline-flex;
|
|
93
|
+
flex: none;
|
|
94
|
+
align-items: center;
|
|
95
|
+
justify-content: center;
|
|
96
|
+
width: 2.75rem;
|
|
97
|
+
height: 2.75rem;
|
|
98
|
+
border-radius: 999px;
|
|
99
|
+
color: rgb(28 77 217);
|
|
100
|
+
background: rgb(38 99 235 / 0.08);
|
|
101
|
+
text-decoration: none;
|
|
102
|
+
-webkit-tap-highlight-color: transparent;
|
|
103
|
+
-webkit-touch-callout: none;
|
|
104
|
+
transition: transform 120ms ease, opacity 120ms ease;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.native-bar-button:active { transform: scale(0.94); opacity: 0.85; }
|
|
108
|
+
|
|
109
|
+
.native-bar-label {
|
|
110
|
+
position: absolute;
|
|
111
|
+
width: 1px;
|
|
112
|
+
height: 1px;
|
|
113
|
+
overflow: hidden;
|
|
114
|
+
clip: rect(0 0 0 0);
|
|
115
|
+
white-space: nowrap;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.native-bar-glyph { width: 1.25rem; height: 1.25rem; }
|
|
119
|
+
|
|
120
|
+
.native-bar-title {
|
|
121
|
+
font-size: 0.8125rem;
|
|
122
|
+
font-weight: 600;
|
|
123
|
+
letter-spacing: 0.01em;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.native-bar-photo {
|
|
127
|
+
width: 2.25rem;
|
|
128
|
+
height: 2.25rem;
|
|
129
|
+
border-radius: 999px;
|
|
130
|
+
background: center / cover no-repeat;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.native-bar-count {
|
|
134
|
+
position: absolute;
|
|
135
|
+
top: 0;
|
|
136
|
+
right: 0;
|
|
137
|
+
min-width: 1.125rem;
|
|
138
|
+
height: 1.125rem;
|
|
139
|
+
padding: 0 0.25rem;
|
|
140
|
+
border-radius: 999px;
|
|
141
|
+
color: white;
|
|
142
|
+
background: rgb(0 122 255);
|
|
143
|
+
font-size: 0.625rem;
|
|
144
|
+
font-weight: 700;
|
|
145
|
+
line-height: 1.125rem;
|
|
146
|
+
text-align: center;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.native-bar-dot {
|
|
150
|
+
position: absolute;
|
|
151
|
+
top: 0;
|
|
152
|
+
right: 0;
|
|
153
|
+
width: 0.5625rem;
|
|
154
|
+
height: 0.5625rem;
|
|
155
|
+
border-radius: 999px;
|
|
156
|
+
background: rgb(255 59 48);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.dark .native-bar-button {
|
|
160
|
+
color: rgb(148 196 252);
|
|
161
|
+
background: rgb(38 99 235 / 0.12);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.dark .native-bar-accent { background: rgb(38 99 235 / 0.24); }
|
|
165
|
+
|
|
166
|
+
@media (prefers-reduced-motion: reduce) {
|
|
167
|
+
.native-bar-button { transition: none; }
|
|
168
|
+
}
|
|
169
|
+
</style>
|
|
@@ -39,6 +39,19 @@ describe('Native components', () => {
|
|
|
39
39
|
expect(button).not.toContain('<img')
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
+
it('draws bar buttons as the native bar does, from the same SF Symbol names', () => {
|
|
43
|
+
const button = read('NativeBarButton')
|
|
44
|
+
// Craft's accountNavigationButton: a 44pt tinted circle, a 36pt photo inset.
|
|
45
|
+
expect(button).toContain('width: 2.75rem;')
|
|
46
|
+
expect(button).toContain('background: rgb(38 99 235 / 0.08);')
|
|
47
|
+
expect(button).toContain('width: 2.25rem;')
|
|
48
|
+
expect(button).toContain("'bubble.left': 'i-lucide-message-square'")
|
|
49
|
+
expect(button).toContain("useReactiveProp<string | number>('badge', '')")
|
|
50
|
+
expect(button).toContain('data-stx-link data-stx-prefetch="eager"')
|
|
51
|
+
// A photo is a background, not an <img>: apps check that every image goes through their delivery.
|
|
52
|
+
expect(button).not.toContain('<img')
|
|
53
|
+
})
|
|
54
|
+
|
|
42
55
|
it('lifts the launch splash once the first screen has painted', () => {
|
|
43
56
|
expect(read('NativeAppShell')).toContain('requestAnimationFrame(() => requestAnimationFrame(() => splash.hide()))')
|
|
44
57
|
})
|