@stacksjs/defaults 0.75.93 → 0.75.95

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 409 built-in dashboard components. Covers the dashboard system at storage/framework/defaults/.
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 410 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, 409 components, and a multi-section layout.
11
+ The Stacks admin dashboard provides a full-featured admin panel with 100+ route views, 410 components, and a multi-section layout.
12
12
 
13
13
  ## Key Paths
14
14
  - Dashboard components: `storage/framework/defaults/resources/components/Dashboard/`
@@ -2,7 +2,7 @@
2
2
  "publisher": "Stacks",
3
3
  "name": "vscode-stacks",
4
4
  "displayName": "Stacks",
5
- "version": "0.75.93",
5
+ "version": "0.75.95",
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.93",
5
+ "version": "0.75.95",
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.93",
58
+ "@stacksjs/mobile": "^0.75.95",
59
59
  "@stacksjs/sanitizer": "^0.2.395",
60
60
  "@xterm/addon-fit": "^0.11.0",
61
61
  "@xterm/xterm": "^6.0.0",
@@ -0,0 +1,120 @@
1
+ <script>
2
+ interface NativeAccountButtonProps {
3
+ /** Where the account screen is, such as `/m/me`. */
4
+ href: string
5
+ /** The person's name; the button shows its initials. */
6
+ name?: string
7
+ /** A photo of them, shown instead of the initials. */
8
+ image?: string
9
+ /** A count on the button (unread notifications, say). Zero and empty draw nothing. */
10
+ badge?: string | number
11
+ /** What a screen reader calls the button. */
12
+ label?: string
13
+ }
14
+
15
+ defineProps<NativeAccountButtonProps>()
16
+ </script>
17
+
18
+ <script client>
19
+ import { haptics } from '@stacksjs/mobile'
20
+
21
+ // The account at the top right of a tab's screen, as iOS apps put it: the
22
+ // tab bar keeps its tabs for the places the app is used from. Reactive, so it
23
+ // follows the signed-in person and the count as they change.
24
+ // Every prop through useReactiveProp, under names of its own: read as plain
25
+ // {{ label }}, a host's client signal of the same name (a pull-to-refresh's
26
+ // "Refreshing") was shown instead of the button's own.
27
+ const accountHref = useReactiveProp<string>('href', '')
28
+ const accountLabel = useReactiveProp<string>('label', 'Account')
29
+ const accountName = useReactiveProp<string>('name', '')
30
+ const accountImage = useReactiveProp<string>('image', '')
31
+ const accountBadge = useReactiveProp<string | number>('badge', '')
32
+
33
+ const accountInitials = derived(() => {
34
+ const parts = String(accountName() || '').trim().split(/\s+/).filter(Boolean)
35
+ const first = parts[0]?.[0] ?? ''
36
+ const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? '') : ''
37
+ return (first + last).toUpperCase()
38
+ })
39
+ const accountCount = derived(() => {
40
+ const value = accountBadge()
41
+ return value === '' || value === 0 || value === '0' || value == null ? '' : String(value)
42
+ })
43
+ const accountPhoto = derived(() => (accountImage() ? `background-image: url("${String(accountImage()).replace(/"/g, '%22')}")` : ''))
44
+
45
+ function tapAccount(): void {
46
+ void haptics.selection()
47
+ }
48
+ </script>
49
+
50
+ <a :href="accountHref()" class="native-account-button" draggable="false" data-stx-link data-stx-prefetch="eager" @click="tapAccount()">
51
+ <span class="native-account-label">{{ accountLabel() }}<span :if="accountCount()">, {{ accountCount() }}</span></span>
52
+ <span aria-hidden="true" class="native-account-face" :style="accountPhoto()">
53
+ <span :if="!accountImage() && accountInitials()">{{ accountInitials() }}</span>
54
+ <i :if="!accountImage() && !accountInitials()" class="i-lucide-circle-user-round native-account-glyph"></i>
55
+ </span>
56
+ <span :if="accountCount()" aria-hidden="true" class="native-account-badge"></span>
57
+ </a>
58
+
59
+ <style>
60
+ .native-account-button {
61
+ position: relative;
62
+ display: inline-flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ min-width: 2.75rem;
66
+ min-height: 2.75rem;
67
+ text-decoration: none;
68
+ -webkit-tap-highlight-color: transparent;
69
+ -webkit-touch-callout: none;
70
+ transition: transform 120ms ease, opacity 120ms ease;
71
+ }
72
+
73
+ .native-account-button:active { transform: scale(0.94); opacity: 0.85; }
74
+
75
+ .native-account-label {
76
+ position: absolute;
77
+ width: 1px;
78
+ height: 1px;
79
+ overflow: hidden;
80
+ clip: rect(0 0 0 0);
81
+ white-space: nowrap;
82
+ }
83
+
84
+ .native-account-face {
85
+ display: inline-flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ width: 2rem;
89
+ height: 2rem;
90
+ overflow: hidden;
91
+ border-radius: 999px;
92
+ color: var(--native-accent, rgb(29 78 216));
93
+ background: rgb(219 234 254) center / cover no-repeat;
94
+ font-size: 0.8125rem;
95
+ font-weight: 600;
96
+ letter-spacing: 0.01em;
97
+ }
98
+
99
+ .native-account-glyph { width: 1.25rem; height: 1.25rem; }
100
+
101
+ .native-account-badge {
102
+ position: absolute;
103
+ top: 0.375rem;
104
+ right: 0.375rem;
105
+ width: 0.625rem;
106
+ height: 0.625rem;
107
+ border-radius: 999px;
108
+ background: rgb(225 29 72);
109
+ box-shadow: 0 0 0 2px var(--native-safe-area-background, white);
110
+ }
111
+
112
+ .dark .native-account-face {
113
+ color: var(--native-accent-dark, rgb(147 197 253));
114
+ background-color: rgb(30 58 138 / 0.45);
115
+ }
116
+
117
+ @media (prefers-reduced-motion: reduce) {
118
+ .native-account-button { transition: none; }
119
+ }
120
+ </style>
@@ -27,6 +27,17 @@ describe('Native components', () => {
27
27
  expect(shell).toContain('padding-bottom: calc(var(--craft-tab-bar-height, 6rem) + 0.75rem);')
28
28
  })
29
29
 
30
+ it('puts the account at the top right: initials or a photo, a count, a native press', () => {
31
+ const button = read('NativeAccountButton')
32
+ // Reactive, so it follows the signed-in person and the count.
33
+ expect(button).toContain("useReactiveProp<string>('name', '')")
34
+ expect(button).toContain("useReactiveProp<string | number>('badge', '')")
35
+ expect(button).toContain('data-stx-link data-stx-prefetch="eager"')
36
+ expect(button).toContain('void haptics.selection()')
37
+ // A photo is a background, not an <img>: apps check that every image goes through their delivery.
38
+ expect(button).not.toContain('<img')
39
+ })
40
+
30
41
  it('lifts the launch splash once the first screen has painted', () => {
31
42
  expect(read('NativeAppShell')).toContain('requestAnimationFrame(() => requestAnimationFrame(() => splash.hide()))')
32
43
  })