@viibestack/ui 0.7.6 → 0.7.8

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@viibestack/ui",
3
- "version": "0.7.6",
4
- "description": "Dependency-free React UI kit -- icons, core components (Button, Card, Input, Modal, etc.), gamification primitives (PointsDisplay, StreakCounter, Achievements, Leaderboard, fireReward), a real per-app data client for browser code (listRows/listRowsPage/getRow/upsertRow/deleteRow/captureClientError) plus server-safe variants for Route Handlers/Server Components (listRowsServer/listRowsPageServer/getRowServer/upsertRowServer/deleteRowServer/resolveAppIdByHostname), a real per-app end-user auth client (signUp/logIn/logOut/getCurrentUser/reportError), and a self-gating PoweredByBadge component -- backed by ViibeStack's own platform-managed data store.",
3
+ "version": "0.7.8",
4
+ "description": "Dependency-free React UI kit -- icons, core components (Button, Card, Input, Modal, etc.), a TeamGrid/TeamMemberCard people-grid pattern, gamification primitives (PointsDisplay, StreakCounter, Achievements, Leaderboard, fireReward), a real per-app data client for browser code (listRows/listRowsPage/getRow/upsertRow/deleteRow/captureClientError) plus server-safe variants for Route Handlers/Server Components (listRowsServer/listRowsPageServer/getRowServer/upsertRowServer/deleteRowServer/resolveAppIdByHostname), a real per-app end-user auth client (signUp/logIn/logOut/getCurrentUser/reportError), and a self-gating PoweredByBadge component -- backed by ViibeStack's own platform-managed data store.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
7
7
  "types": "./src/index.ts",
package/src/badge.tsx CHANGED
@@ -110,22 +110,43 @@ export function PoweredByBadge() {
110
110
  setDismissed(true);
111
111
  }}
112
112
  style={{
113
+ // Tap target is 2.75rem (44px, Apple's/Android's usual minimum
114
+ // recommendation) even though the visible glyph stays the same
115
+ // small size below -- was a literal 1rem (16px) box, reported as
116
+ // hard to hit on mobile. Negative margin cancels the extra size
117
+ // from the pill's own layout (this flex row centers on cross-axis
118
+ // with no fixed height, so a bigger child would otherwise stretch
119
+ // the whole badge taller) -- the button still visually reads as
120
+ // the same small pill, just easier to actually tap.
113
121
  display: "flex",
114
122
  alignItems: "center",
115
123
  justifyContent: "center",
116
- width: "1rem",
117
- height: "1rem",
124
+ width: "2.75rem",
125
+ height: "2.75rem",
126
+ margin: "-0.875rem",
118
127
  padding: 0,
119
128
  border: "none",
120
129
  borderRadius: "9999px",
121
- background: "rgba(255, 255, 255, 0.15)",
130
+ background: "transparent",
122
131
  color: "#fff",
123
132
  fontSize: "0.7rem",
124
133
  lineHeight: 1,
125
134
  cursor: "pointer",
126
135
  }}
127
136
  >
128
- ✕
137
+ <span
138
+ style={{
139
+ display: "flex",
140
+ alignItems: "center",
141
+ justifyContent: "center",
142
+ width: "1rem",
143
+ height: "1rem",
144
+ borderRadius: "9999px",
145
+ background: "rgba(255, 255, 255, 0.15)",
146
+ }}
147
+ >
148
+ &#x2715;
149
+ </span>
129
150
  </button>
130
151
  </span>
131
152
  );
package/src/index.ts CHANGED
@@ -8,3 +8,4 @@ export * from "./gamification";
8
8
  export * from "./reward";
9
9
  export * from "./dashboard";
10
10
  export * from "./data-table";
11
+ export * from "./team";
package/src/nav.tsx CHANGED
@@ -115,20 +115,22 @@ export interface AppShellProps {
115
115
  /** Rendered at the bottom of the sidebar/drawer -- e.g. a user menu, sign-out button. */
116
116
  footer?: ReactNode;
117
117
  /**
118
- * Set true to omit the built-in dark/light toggle row from the nav.
119
- * Shown by default -- a real nav-integrated toggle beats every app
120
- * reinventing one, and it doubles as the platform's recommended way to
121
- * suppress the floating fallback toggle every app otherwise gets
122
- * injected at build time (see template-patches.ts's THEME_TOGGLE_SCRIPT):
123
- * both share the same data-vs="viibestack-theme-toggle" marker, so the
124
- * fallback detects this one and backs off instead of adding a second,
125
- * floating, duplicate button.
118
+ * Set true to render a dark/light toggle as a normal row in the nav.
119
+ * Off by default -- AppShell doesn't presume every app wants this
120
+ * specific row in this specific place; the platform's own dark/light
121
+ * requirement (every app must support both and let the end user switch)
122
+ * is satisfied independently by the floating fallback toggle every app
123
+ * gets at build time (see template-patches.ts's THEME_TOGGLE_SCRIPT)
124
+ * unless the app supplies its own. Pass true here for a nav-integrated
125
+ * toggle instead of that floating button -- it shares the exact same
126
+ * data-vs="viibestack-theme-toggle" marker, so the fallback detects it
127
+ * and backs off rather than adding a second, floating, duplicate one.
126
128
  */
127
- hideThemeToggle?: boolean;
129
+ showThemeToggle?: boolean;
128
130
  children: ReactNode;
129
131
  }
130
132
 
131
- export function AppShell({ brand, nav, activeHref, renderLink, footer, hideThemeToggle, children }: AppShellProps) {
133
+ export function AppShell({ brand, nav, activeHref, renderLink, footer, showThemeToggle, children }: AppShellProps) {
132
134
  const [mobileOpen, setMobileOpen] = useState(false);
133
135
  const [mounted, setMounted] = useState(false);
134
136
 
@@ -196,7 +198,7 @@ export function AppShell({ brand, nav, activeHref, renderLink, footer, hideTheme
196
198
  <div className="px-4 py-4 text-lg font-semibold text-gray-900 dark:text-gray-100">{brand}</div>
197
199
  <nav className="flex flex-col gap-1 overflow-y-auto px-3 pb-3">
198
200
  {nav.map((item, i) => renderItem(item, i))}
199
- {!hideThemeToggle && mounted && <ThemeToggleItem />}
201
+ {showThemeToggle && mounted && <ThemeToggleItem />}
200
202
  </nav>
201
203
  {footer && <div className="mt-auto border-t border-gray-200 p-3 dark:border-gray-800">{footer}</div>}
202
204
  </aside>
@@ -241,7 +243,7 @@ export function AppShell({ brand, nav, activeHref, renderLink, footer, hideTheme
241
243
  </div>
242
244
  <nav className="flex flex-col gap-1 px-3 pb-3">
243
245
  {nav.map((item, i) => renderItem(item, i, () => setMobileOpen(false)))}
244
- {!hideThemeToggle && <ThemeToggleItem />}
246
+ {showThemeToggle && <ThemeToggleItem />}
245
247
  </nav>
246
248
  {footer && <div className="mt-auto border-t border-gray-200 p-3 dark:border-gray-800">{footer}</div>}
247
249
  </div>
package/src/team.tsx ADDED
@@ -0,0 +1,91 @@
1
+ "use client";
2
+
3
+ // Team/people-grid building block -- TeamMemberCard, TeamGrid. A distinct,
4
+ // later-added vocabulary (same rationale as dashboard.tsx) for "who's on
5
+ // this" pages: about pages, staff directories, contributor/leaderboard-
6
+ // adjacent listings. Original implementation -- conceptually informed by
7
+ // the general "avatar + name + role + optional bio + optional links" shape
8
+ // common to team-showcase page sections, not copied markup/CSS from any
9
+ // one source (see packages/design-library's README for this platform's
10
+ // design-provenance conventions). Deliberately no baked-in social-network
11
+ // icon set -- `links` is a generic {label, href, icon?} array so this
12
+ // doesn't force which platforms (or how many) a member's links cover.
13
+ import type { ReactNode } from "react";
14
+ import { Avatar } from "./components";
15
+
16
+ function cx(...parts: (string | false | null | undefined)[]): string {
17
+ return parts.filter(Boolean).join(" ");
18
+ }
19
+
20
+ export interface TeamMemberLink {
21
+ label: string;
22
+ href: string;
23
+ icon?: ReactNode;
24
+ }
25
+
26
+ export interface TeamMember {
27
+ name: string;
28
+ role?: ReactNode;
29
+ avatarSrc?: string;
30
+ bio?: ReactNode;
31
+ links?: TeamMemberLink[];
32
+ }
33
+
34
+ export interface TeamMemberCardProps {
35
+ member: TeamMember;
36
+ className?: string;
37
+ }
38
+
39
+ export function TeamMemberCard({ member, className }: TeamMemberCardProps) {
40
+ return (
41
+ <div
42
+ className={cx(
43
+ "flex flex-col items-center gap-3 rounded-xl border border-gray-200 bg-white p-6 text-center dark:border-gray-800 dark:bg-gray-900",
44
+ className,
45
+ )}
46
+ >
47
+ <Avatar src={member.avatarSrc} name={member.name} size={72} />
48
+ <div>
49
+ <div className="text-base font-semibold text-gray-900 dark:text-gray-100">{member.name}</div>
50
+ {member.role && <div className="text-sm text-gray-500 dark:text-gray-400">{member.role}</div>}
51
+ </div>
52
+ {member.bio && <p className="text-sm text-gray-600 dark:text-gray-300">{member.bio}</p>}
53
+ {member.links && member.links.length > 0 && (
54
+ <div className="flex items-center gap-2">
55
+ {member.links.map((link, i) => (
56
+ <a
57
+ key={i}
58
+ href={link.href}
59
+ target="_blank"
60
+ rel="noopener noreferrer"
61
+ aria-label={link.label}
62
+ title={link.label}
63
+ className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 text-gray-600 hover:bg-primary/10 hover:text-primary dark:bg-gray-800 dark:text-gray-300"
64
+ >
65
+ {link.icon ?? link.label[0]?.toUpperCase()}
66
+ </a>
67
+ ))}
68
+ </div>
69
+ )}
70
+ </div>
71
+ );
72
+ }
73
+
74
+ export interface TeamGridProps {
75
+ members: TeamMember[];
76
+ className?: string;
77
+ }
78
+
79
+ // 1/2/3-column responsive grid -- the columns step follows this package's
80
+ // existing breakpoint convention (md for tablet, lg for desktop) rather
81
+ // than a 4th column, since a wider member card reads better than a
82
+ // cramped one on very large screens.
83
+ export function TeamGrid({ members, className }: TeamGridProps) {
84
+ return (
85
+ <div className={cx("grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3", className)}>
86
+ {members.map((member, i) => (
87
+ <TeamMemberCard key={i} member={member} />
88
+ ))}
89
+ </div>
90
+ );
91
+ }