@skipleague/design 0.6.0 → 0.7.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.
package/dist/AppLogo.d.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  /** A SkipLeague app's logo glyph (monoline, drawn on the brand tile). */
2
2
  export type AppGlyph = "lists" | "racquetball" | "trips" | "gifts" | "reading" | "today" | "guide" | "flow";
3
+ /** Every app that has a glyph, for slug→glyph resolution and fallbacks. */
4
+ export declare const APP_GLYPHS: readonly AppGlyph[];
5
+ /**
6
+ * Resolve a platform app slug (e.g. `"skipracquetball"`) to its {@link AppGlyph}
7
+ * by dropping the `skip` prefix, or `null` when no glyph exists for it (so a
8
+ * caller can fall back to a letter {@link AppBadge}). Case-insensitive.
9
+ */
10
+ export declare function appGlyphForSlug(slug: string): AppGlyph | null;
3
11
  /**
4
12
  * The per-app logo mark — a brand-green rounded tile holding a white monoline
5
13
  * glyph. Replaces the single-letter {@link AppBadge}; gives each SkipLeague app a
package/dist/AppLogo.js CHANGED
@@ -1,4 +1,24 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** Every app that has a glyph, for slug→glyph resolution and fallbacks. */
3
+ export const APP_GLYPHS = [
4
+ "lists",
5
+ "racquetball",
6
+ "trips",
7
+ "gifts",
8
+ "reading",
9
+ "today",
10
+ "guide",
11
+ "flow",
12
+ ];
13
+ /**
14
+ * Resolve a platform app slug (e.g. `"skipracquetball"`) to its {@link AppGlyph}
15
+ * by dropping the `skip` prefix, or `null` when no glyph exists for it (so a
16
+ * caller can fall back to a letter {@link AppBadge}). Case-insensitive.
17
+ */
18
+ export function appGlyphForSlug(slug) {
19
+ const key = slug.replace(/^skip/i, "").toLowerCase();
20
+ return APP_GLYPHS.includes(key) ? key : null;
21
+ }
2
22
  // Exact 24×24 glyph paths (design_handoff_app_glyphs — the finalized round that
3
23
  // fixed racquetball, trips, and flow and added the guide mark). `color` only
4
24
  // matters for filled sub-shapes (e.g. the racquetball ball), which are filled
@@ -2,6 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { CircleUser, LogIn, LogOut, Settings } from "lucide-react";
4
4
  import { AppBadge } from "./AppBadge.js";
5
+ import { AppLogo, appGlyphForSlug } from "./AppLogo.js";
5
6
  import { SKIPLEAGUE_ACCOUNT_URL, SKIPLEAGUE_APPS } from "./apps.js";
6
7
  /**
7
8
  * The canonical SkipLeague account control: a boxed user-icon button that opens
@@ -66,7 +67,9 @@ export function ProfileMenu({ user, currentSlug, apps = SKIPLEAGUE_APPS, enabled
66
67
  transition: "border-color 0.15s, color 0.15s",
67
68
  }, children: _jsx(CircleUser, { size: 22 }) }), open && (_jsx("div", { role: "menu", style: menuStyle, children: showSignedOut ? (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: "0.5rem 0.75rem", borderBottom: "1px solid var(--skl-color-border)" }, children: _jsx("div", { style: { fontWeight: 600, fontSize: "var(--skl-text-sm)", color: "var(--skl-color-text)" }, children: "Not signed in" }) }), _jsxs("button", { role: "menuitem", onClick: onSignIn, style: { ...itemStyle, width: "100%", textAlign: "left", background: "none", border: "none", cursor: "pointer" }, children: [_jsx(LogIn, { size: 15 }), " ", signInLabel] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { style: { padding: "0.5rem 0.75rem", borderBottom: "1px solid var(--skl-color-border)" }, children: [_jsx("div", { style: { fontWeight: 600, fontSize: "var(--skl-text-sm)", color: "var(--skl-color-text)" }, children: label }), user?.email && _jsx("div", { style: { fontSize: "var(--skl-text-xs)", color: "var(--skl-color-text-muted)" }, children: user.email })] }), visibleApps.length > 0 && (_jsxs(_Fragment, { children: [_jsx("div", { style: switchHeading, children: "Switch app" }), visibleApps.map((a) => {
68
69
  const isCurrent = a.slug === currentSlug;
69
- const inner = (_jsxs("span", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [_jsx(AppBadge, { name: a.name }), a.name] }));
70
+ // Per-app glyph when one exists; letter badge otherwise.
71
+ const glyph = appGlyphForSlug(a.slug);
72
+ const inner = (_jsxs("span", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [glyph ? _jsx(AppLogo, { app: glyph, size: 22 }) : _jsx(AppBadge, { name: a.name }), a.name] }));
70
73
  // The app you're in: light-green highlight, not clickable.
71
74
  return isCurrent ? (_jsx("div", { "aria-current": "page", style: { ...itemStyle, fontWeight: 600, background: "var(--skl-color-current-bg)", color: "var(--skl-color-current-text)", cursor: "default" }, children: inner }, a.slug)) : (_jsx("a", { href: a.url, role: "menuitem", style: itemStyle, children: inner }, a.slug));
72
75
  })] })), _jsx("div", { style: divider }), accountItem, _jsx("div", { style: divider }), _jsxs("button", { role: "menuitem", onClick: onSignOut, style: { ...itemStyle, width: "100%", textAlign: "left", background: "none", border: "none", cursor: "pointer" }, children: [_jsx(LogOut, { size: 15 }), " Sign out"] })] })) }))] }));
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { ProfileMenu } from "./ProfileMenu.js";
2
2
  export type { ProfileMenuProps, ProfileMenuUser, ProfileMenuLinkArgs } from "./ProfileMenu.js";
3
3
  export { AppBadge } from "./AppBadge.js";
4
- export { AppLogo } from "./AppLogo.js";
4
+ export { AppLogo, APP_GLYPHS, appGlyphForSlug } from "./AppLogo.js";
5
5
  export type { AppGlyph } from "./AppLogo.js";
6
6
  export { TopBar, TopBarIconButton, DesktopActionBar } from "./TopBar.js";
7
7
  export type { TopBarTone } from "./TopBar.js";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ProfileMenu } from "./ProfileMenu.js";
2
2
  export { AppBadge } from "./AppBadge.js";
3
- export { AppLogo } from "./AppLogo.js";
3
+ export { AppLogo, APP_GLYPHS, appGlyphForSlug } from "./AppLogo.js";
4
4
  export { TopBar, TopBarIconButton, DesktopActionBar } from "./TopBar.js";
5
5
  export { SidebarNav } from "./SidebarNav.js";
6
6
  export { BottomNav } from "./BottomNav.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skipleague/design",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "SkipUI — the SkipLeague design system: shared tokens and React components for every SkipLeague app.",
5
5
  "type": "module",
6
6
  "repository": {
package/src/AppLogo.tsx CHANGED
@@ -11,6 +11,28 @@ export type AppGlyph =
11
11
  | "guide"
12
12
  | "flow";
13
13
 
14
+ /** Every app that has a glyph, for slug→glyph resolution and fallbacks. */
15
+ export const APP_GLYPHS: readonly AppGlyph[] = [
16
+ "lists",
17
+ "racquetball",
18
+ "trips",
19
+ "gifts",
20
+ "reading",
21
+ "today",
22
+ "guide",
23
+ "flow",
24
+ ];
25
+
26
+ /**
27
+ * Resolve a platform app slug (e.g. `"skipracquetball"`) to its {@link AppGlyph}
28
+ * by dropping the `skip` prefix, or `null` when no glyph exists for it (so a
29
+ * caller can fall back to a letter {@link AppBadge}). Case-insensitive.
30
+ */
31
+ export function appGlyphForSlug(slug: string): AppGlyph | null {
32
+ const key = slug.replace(/^skip/i, "").toLowerCase();
33
+ return (APP_GLYPHS as readonly string[]).includes(key) ? (key as AppGlyph) : null;
34
+ }
35
+
14
36
  // Exact 24×24 glyph paths (design_handoff_app_glyphs — the finalized round that
15
37
  // fixed racquetball, trips, and flow and added the guide mark). `color` only
16
38
  // matters for filled sub-shapes (e.g. the racquetball ball), which are filled
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from "react";
2
2
  import { CircleUser, LogIn, LogOut, Settings } from "lucide-react";
3
3
  import { AppBadge } from "./AppBadge.js";
4
+ import { AppLogo, appGlyphForSlug } from "./AppLogo.js";
4
5
  import { SKIPLEAGUE_ACCOUNT_URL, SKIPLEAGUE_APPS, type AppLink } from "./apps.js";
5
6
 
6
7
  export interface ProfileMenuUser {
@@ -202,9 +203,11 @@ export function ProfileMenu({
202
203
  <div style={switchHeading}>Switch app</div>
203
204
  {visibleApps.map((a) => {
204
205
  const isCurrent = a.slug === currentSlug;
206
+ // Per-app glyph when one exists; letter badge otherwise.
207
+ const glyph = appGlyphForSlug(a.slug);
205
208
  const inner = (
206
209
  <span style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
207
- <AppBadge name={a.name} />
210
+ {glyph ? <AppLogo app={glyph} size={22} /> : <AppBadge name={a.name} />}
208
211
  {a.name}
209
212
  </span>
210
213
  );
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { ProfileMenu } from "./ProfileMenu.js";
2
2
  export type { ProfileMenuProps, ProfileMenuUser, ProfileMenuLinkArgs } from "./ProfileMenu.js";
3
3
  export { AppBadge } from "./AppBadge.js";
4
- export { AppLogo } from "./AppLogo.js";
4
+ export { AppLogo, APP_GLYPHS, appGlyphForSlug } from "./AppLogo.js";
5
5
  export type { AppGlyph } from "./AppLogo.js";
6
6
  export { TopBar, TopBarIconButton, DesktopActionBar } from "./TopBar.js";
7
7
  export type { TopBarTone } from "./TopBar.js";