@viibestack/ui 0.7.6 → 0.7.7

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.7",
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/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
+ }