@photon-ai/pho-ui 0.7.2 → 1.0.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.
Files changed (121) hide show
  1. package/dist/chunks/avatar-CiAvJNNs.js +99 -0
  2. package/dist/chunks/avatar-CiAvJNNs.js.map +1 -0
  3. package/dist/chunks/close-button-8f-oRJXt.js +420 -0
  4. package/dist/chunks/close-button-8f-oRJXt.js.map +1 -0
  5. package/dist/chunks/{cn-7uGUzCOM.js → cn-DTiB6ek_.js} +4 -2
  6. package/dist/chunks/{cn-7uGUzCOM.js.map → cn-DTiB6ek_.js.map} +1 -1
  7. package/dist/chunks/dialog-Bgb2Z1_v.js +141 -0
  8. package/dist/chunks/dialog-Bgb2Z1_v.js.map +1 -0
  9. package/dist/chunks/{input-DbJpdu6v.js → input-BSJGBUbf.js} +36 -36
  10. package/dist/chunks/input-BSJGBUbf.js.map +1 -0
  11. package/dist/chunks/menu-COLB_liS.js +205 -0
  12. package/dist/chunks/menu-COLB_liS.js.map +1 -0
  13. package/dist/chunks/{sidebar-CivriTLb.js → sidebar-16Pfn7IC.js} +12 -12
  14. package/dist/chunks/sidebar-16Pfn7IC.js.map +1 -0
  15. package/dist/chunks/spinner-Cy8bkyJf.js +54 -0
  16. package/dist/chunks/spinner-Cy8bkyJf.js.map +1 -0
  17. package/dist/components/accordion.js +21 -21
  18. package/dist/components/accordion.js.map +1 -1
  19. package/dist/components/alert-dialog.js +26 -26
  20. package/dist/components/alert-dialog.js.map +1 -1
  21. package/dist/components/autocomplete.js +46 -46
  22. package/dist/components/autocomplete.js.map +1 -1
  23. package/dist/components/avatar.js +1 -1
  24. package/dist/components/badge.js +1 -1
  25. package/dist/components/button.js +11 -11
  26. package/dist/components/checkbox.js +3 -3
  27. package/dist/components/checkbox.js.map +1 -1
  28. package/dist/components/collapsible.js +8 -8
  29. package/dist/components/collapsible.js.map +1 -1
  30. package/dist/components/combobox.js +69 -69
  31. package/dist/components/combobox.js.map +1 -1
  32. package/dist/components/context-menu.js +57 -44
  33. package/dist/components/context-menu.js.map +1 -1
  34. package/dist/components/dialog.js +3 -2
  35. package/dist/components/drawer.js +15 -15
  36. package/dist/components/drawer.js.map +1 -1
  37. package/dist/components/field.js +18 -18
  38. package/dist/components/field.js.map +1 -1
  39. package/dist/components/fieldset.js +9 -9
  40. package/dist/components/fieldset.js.map +1 -1
  41. package/dist/components/form.js +1 -1
  42. package/dist/components/input.js +1 -1
  43. package/dist/components/kbd.js +6 -6
  44. package/dist/components/kbd.js.map +1 -1
  45. package/dist/components/menu.js +6 -4
  46. package/dist/components/menubar.js +5 -5
  47. package/dist/components/menubar.js.map +1 -1
  48. package/dist/components/meter.js +20 -20
  49. package/dist/components/meter.js.map +1 -1
  50. package/dist/components/navigation-menu.js +8 -8
  51. package/dist/components/navigation-menu.js.map +1 -1
  52. package/dist/components/notification.js +24 -24
  53. package/dist/components/notification.js.map +1 -1
  54. package/dist/components/number-field.js +32 -32
  55. package/dist/components/number-field.js.map +1 -1
  56. package/dist/components/otp-field.js +5 -5
  57. package/dist/components/otp-field.js.map +1 -1
  58. package/dist/components/popover.js +10 -10
  59. package/dist/components/popover.js.map +1 -1
  60. package/dist/components/preview-card.js +8 -8
  61. package/dist/components/preview-card.js.map +1 -1
  62. package/dist/components/progress.js +25 -25
  63. package/dist/components/progress.js.map +1 -1
  64. package/dist/components/radio-group.js +13 -13
  65. package/dist/components/radio-group.js.map +1 -1
  66. package/dist/components/scroll-area.js +1 -1
  67. package/dist/components/select.js +27 -27
  68. package/dist/components/select.js.map +1 -1
  69. package/dist/components/separator.js +1 -1
  70. package/dist/components/slider.js +17 -17
  71. package/dist/components/slider.js.map +1 -1
  72. package/dist/components/spinner.js +6 -0
  73. package/dist/components/switch.js +10 -10
  74. package/dist/components/switch.js.map +1 -1
  75. package/dist/components/tabs.js +16 -16
  76. package/dist/components/tabs.js.map +1 -1
  77. package/dist/components/theme-toggle.js +199 -0
  78. package/dist/components/theme-toggle.js.map +1 -0
  79. package/dist/components/toast.js +24 -24
  80. package/dist/components/toast.js.map +1 -1
  81. package/dist/components/toggle-group.js +1 -1
  82. package/dist/components/toggle.js +5 -5
  83. package/dist/components/toggle.js.map +1 -1
  84. package/dist/components/toolbar.js +8 -8
  85. package/dist/components/toolbar.js.map +1 -1
  86. package/dist/components/tooltip.js +10 -10
  87. package/dist/components/tooltip.js.map +1 -1
  88. package/dist/index.js +29 -28
  89. package/dist/sections/sidebar.js +1 -1
  90. package/dist/src/components/avatar/avatar.d.ts +24 -2
  91. package/dist/src/components/avatar/index.d.ts +1 -1
  92. package/dist/src/components/button/button.d.ts +58 -44
  93. package/dist/src/components/button/index.d.ts +1 -1
  94. package/dist/src/components/dialog/dialog.d.ts +50 -7
  95. package/dist/src/components/dialog/index.d.ts +1 -1
  96. package/dist/src/components/menu/index.d.ts +1 -1
  97. package/dist/src/components/menu/menu-pending.d.ts +43 -0
  98. package/dist/src/components/spinner/index.d.ts +1 -0
  99. package/dist/src/components/spinner/spinner.d.ts +32 -0
  100. package/dist/src/components/theme-toggle/index.d.ts +1 -0
  101. package/dist/src/components/theme-toggle/theme-toggle.d.ts +77 -0
  102. package/dist/src/sections/sidebar/sidebar.d.ts +5 -6
  103. package/dist/utils.js +1 -1
  104. package/package.json +9 -1
  105. package/src/styles/fonts.css +14 -18
  106. package/src/styles/pho.css +5 -1
  107. package/src/styles/theme.css +182 -146
  108. package/dist/chunks/avatar-Bb4RZQIa.js +0 -53
  109. package/dist/chunks/avatar-Bb4RZQIa.js.map +0 -1
  110. package/dist/chunks/button-CQ4cZvkC.js +0 -365
  111. package/dist/chunks/button-CQ4cZvkC.js.map +0 -1
  112. package/dist/chunks/close-button-CNhn8z0w.js +0 -29
  113. package/dist/chunks/close-button-CNhn8z0w.js.map +0 -1
  114. package/dist/chunks/dialog-7oOe-eaO.js +0 -63
  115. package/dist/chunks/dialog-7oOe-eaO.js.map +0 -1
  116. package/dist/chunks/input-DbJpdu6v.js.map +0 -1
  117. package/dist/chunks/menu-BfaNHnOp.js +0 -101
  118. package/dist/chunks/menu-BfaNHnOp.js.map +0 -1
  119. package/dist/chunks/sidebar-CivriTLb.js.map +0 -1
  120. package/dist/src/components/context-menu/context-menu.d.ts +0 -34
  121. package/dist/src/components/menu/menu.d.ts +0 -47
@@ -0,0 +1,43 @@
1
+ import { ComponentType, MouseEvent as ReactMouseEvent, ReactNode, RefObject } from 'react';
2
+ /** Imperative actions exposed by Base UI menu `actionsRef`. */
3
+ interface RootActions {
4
+ unmount: () => void;
5
+ close: () => void;
6
+ }
7
+ /**
8
+ * Whether the surrounding menu has an async item promise in flight —
9
+ * use it to disable sibling items while work is pending.
10
+ * Returns `false` outside a `Menu.Root` / `ContextMenu.Root`.
11
+ */
12
+ export declare function useMenuPending(): boolean;
13
+ export declare function isThenable(value: unknown): value is Promise<unknown>;
14
+ type RootWithPendingProps = {
15
+ onOpenChange?: (open: boolean, eventDetails: {
16
+ cancel: () => void;
17
+ }) => void;
18
+ actionsRef?: RefObject<RootActions | null>;
19
+ children?: ReactNode;
20
+ };
21
+ /**
22
+ * Wraps a Base UI menu root so async `Menu.Item` clicks can lock dismissal
23
+ * (outside press, Escape) until the promise settles.
24
+ */
25
+ export declare function withMenuPendingRoot<P extends RootWithPendingProps>(BaseRoot: ComponentType<P>): ComponentType<P>;
26
+ export type AsyncMenuItemClick = (event: ReactMouseEvent<HTMLElement>) => void | Promise<unknown>;
27
+ /**
28
+ * Shared click handler for `Menu.Item` / `ContextMenu.Item` promise support.
29
+ * Returns whether the item should show its loading state.
30
+ */
31
+ export declare function useAsyncMenuItem({ onClick, closeOnClick, loading, disabled, }: {
32
+ onClick?: AsyncMenuItemClick;
33
+ closeOnClick?: boolean;
34
+ loading?: boolean;
35
+ disabled?: boolean;
36
+ }): {
37
+ inert: boolean;
38
+ blocked: boolean;
39
+ handleClick: (event: ReactMouseEvent<HTMLElement>) => void;
40
+ };
41
+ /** Menu-row spinner — keeps `data-menu-spinner` for the hide-siblings CSS. */
42
+ export declare function MenuItemSpinner(): import("react").JSX.Element;
43
+ export {};
@@ -0,0 +1 @@
1
+ export { Spinner, SPINNER_SIZES, type SpinnerProps, type SpinnerSize, } from './spinner';
@@ -0,0 +1,32 @@
1
+ import { ComponentProps } from 'react';
2
+ /** Size axis — the ring scales with the surrounding control. */
3
+ export declare const SPINNER_SIZES: {
4
+ sm: {
5
+ classes: string;
6
+ description: string;
7
+ };
8
+ md: {
9
+ classes: string;
10
+ description: string;
11
+ };
12
+ lg: {
13
+ classes: string;
14
+ description: string;
15
+ };
16
+ };
17
+ export type SpinnerSize = keyof typeof SPINNER_SIZES;
18
+ export interface SpinnerProps extends Omit<ComponentProps<"svg">, "children"> {
19
+ /** Size variant. @default "md" */
20
+ size?: SpinnerSize;
21
+ }
22
+ /**
23
+ * Indeterminate loading ring — a hairline track with a quarter-circle arc
24
+ * rotating over it. The continuous 2px round-cap stroke matches Pho's
25
+ * hairline rings and Tabler icon language. Uses `currentColor`, so ink
26
+ * follows the parent (`text-pho-*`, white on overlays). Decorative by default
27
+ * (`aria-hidden`); pass `aria-label` when the spinner is the only loading cue.
28
+ */
29
+ export declare function Spinner({ size, className, "aria-label": ariaLabel, "aria-hidden": ariaHidden, ...props }: SpinnerProps): import("react").JSX.Element;
30
+ export declare namespace Spinner {
31
+ var displayName: string;
32
+ }
@@ -0,0 +1 @@
1
+ export { ThemeToggle, getThemeMode, getThemePreference, setThemeMode, parseThemeMode, resolveThemeMode, THEME_STORAGE_KEY, type ThemeMode, type ResolvedTheme, type ThemeToggleProps, type ThemeToggleSize, } from './theme-toggle';
@@ -0,0 +1,77 @@
1
+ import { HTMLAttributes } from 'react';
2
+ /**
3
+ * Appearance preference — `device` follows `prefers-color-scheme`; `light` /
4
+ * `dark` force the scheme via `data-mode`.
5
+ */
6
+ export type ThemeMode = "device" | "light" | "dark";
7
+ /** Resolved color-scheme after applying a preference. */
8
+ export type ResolvedTheme = "light" | "dark";
9
+ /** Default `localStorage` key used by the docs site and this component. */
10
+ export declare const THEME_STORAGE_KEY = "pho-mode";
11
+ declare const THEME_SIZES: {
12
+ readonly sm: {
13
+ readonly track: "gap-0.5 rounded-base p-0.5";
14
+ readonly item: "size-7 rounded-[calc(var(--radius-base)-2px)] [&>svg]:size-3.5";
15
+ };
16
+ readonly md: {
17
+ readonly track: "gap-0.5 rounded-base p-0.5";
18
+ readonly item: "size-8 rounded-[calc(var(--radius-base)-2px)] [&>svg]:size-4";
19
+ };
20
+ readonly lg: {
21
+ readonly track: "gap-1 rounded-base p-1";
22
+ readonly item: "size-9 rounded-[calc(var(--radius-base)-4px)] [&>svg]:size-5";
23
+ };
24
+ };
25
+ export type ThemeToggleSize = keyof typeof THEME_SIZES;
26
+ export interface ThemeToggleProps extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "color" | "onChange" | "defaultValue"> {
27
+ /** Controlled preference. */
28
+ mode?: ThemeMode;
29
+ /** Uncontrolled initial preference. @default "device" */
30
+ defaultMode?: ThemeMode;
31
+ /** Fired when the user picks a segment. */
32
+ onModeChange?: (mode: ThemeMode) => void;
33
+ /**
34
+ * Persist the preference under this `localStorage` key. Pass `null` to skip
35
+ * persistence. @default "pho-mode"
36
+ */
37
+ storageKey?: string | null;
38
+ /**
39
+ * Element that receives `data-mode="dark"`. Defaults to
40
+ * `document.documentElement`.
41
+ */
42
+ target?: HTMLElement | null | (() => HTMLElement | null);
43
+ /** Size variant. @default "md" */
44
+ size?: ThemeToggleSize;
45
+ }
46
+ /** Parse a stored preference; unknown / missing values fall back to `device`. */
47
+ export declare function parseThemeMode(value: string | null | undefined): ThemeMode;
48
+ /** Resolve a preference against the OS color-scheme. */
49
+ export declare function resolveThemeMode(mode: ThemeMode, query?: {
50
+ matches: boolean;
51
+ }): ResolvedTheme;
52
+ /** Drop in-memory preference + media listener (tests / full remounts). */
53
+ export declare function clearThemePreference(): void;
54
+ /** Current document preference (memory, then storage, else `device`). */
55
+ export declare function getThemePreference(storageKey?: string | null): ThemeMode;
56
+ /** Read the resolved light/dark mode from an element (or `<html>`). */
57
+ export declare function getThemeMode(target?: HTMLElement | null): ResolvedTheme;
58
+ /**
59
+ * Apply an appearance preference: persist it, resolve against the OS when
60
+ * `device`, set `data-mode` on the target, and notify document-level toggles.
61
+ */
62
+ export declare function setThemeMode(mode: ThemeMode, options?: {
63
+ target?: HTMLElement | null;
64
+ storageKey?: string | null;
65
+ }): void;
66
+ /**
67
+ * Segmented appearance control — Device / Light / Dark. Forces
68
+ * `data-mode="dark"` (or clears it) so Pho's `light-dark()` tokens follow
69
+ * `color-scheme`. `device` tracks `prefers-color-scheme`. Persists under
70
+ * `pho-mode` by default; pair with a blocking `<head>` script to avoid a
71
+ * flash (see docs).
72
+ */
73
+ export declare function ThemeToggle({ mode: modeProp, defaultMode, onModeChange, storageKey, target, size, className, "aria-label": ariaLabel, ...props }: ThemeToggleProps): import("react").JSX.Element;
74
+ export declare namespace ThemeToggle {
75
+ var displayName: string;
76
+ }
77
+ export {};
@@ -15,16 +15,15 @@ import { AnchorHTMLAttributes, ButtonHTMLAttributes, ComponentProps, FC, ReactNo
15
15
  /** The rail itself — sticky, full-height, hairline right edge. */
16
16
  export declare const SIDEBAR_ROOT = "border-pho-secondary bg-pho-page sticky top-0 flex h-dvh w-64 shrink-0 flex-col border-r px-4 py-5";
17
17
  /** A nav row at rest and on hover. Active/inactive inks split below. */
18
- export declare const SIDEBAR_ITEM = "outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1";
18
+ export declare const SIDEBAR_ITEM = "outline-pho-brand flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-base transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1";
19
19
  /**
20
20
  * Selected row — lifted onto the primary surface with the site hairline.
21
21
  * The icon carries the same ink as the label so the pair reads as one unit.
22
22
  */
23
- export declare const SIDEBAR_ITEM_ACTIVE = "bg-pho-primary text-pho-primary ring-pho-secondary font-medium ring-1 ring-inset *:data-icon:text-fg-pho-primary";
23
+ export declare const SIDEBAR_ITEM_ACTIVE = "bg-pho-primary text-pho-primary ring-pho-secondary font-medium ring-1 ring-inset *:data-icon:text-pho-primary";
24
24
  /**
25
- * Resting row — secondary ink (75% alpha; tertiary at 50% fails contrast for
26
- * nav labels), ghost wash on hover. The icon tracks the label's ink at every
27
- * state instead of sitting on a fainter step.
25
+ * Resting row — secondary ink (75%), ghost wash on hover. Icon tracks the
26
+ * label's ink at every state.
28
27
  */
29
28
  export declare const SIDEBAR_ITEM_INACTIVE: string;
30
29
  interface RootProps extends ComponentProps<"aside"> {
@@ -38,7 +37,7 @@ declare function Header({ className, ...props }: ComponentProps<"div">): import(
38
37
  declare function Nav({ className, ...props }: ComponentProps<"nav">): import("react").JSX.Element;
39
38
  /** One titled cluster of nav rows: a `GroupLabel` followed by a `List`. */
40
39
  declare function Group({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
41
- /** The cluster's caption — secondary ink; quaternary (30%) is illegible. */
40
+ /** The cluster's caption — description ink, regular weight. */
42
41
  declare function GroupLabel({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
43
42
  interface ListProps extends ComponentProps<"ul"> {
44
43
  /** Lay the rows out in two columns — for long, flat registries. */
package/dist/utils.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { t as r } from "./chunks/cn-7uGUzCOM.js";
2
+ import { t as r } from "./chunks/cn-DTiB6ek_.js";
3
3
  import { n as e, r as n, t as i } from "./chunks/link-provider-9TFNyDqO.js";
4
4
  import { t as a } from "./chunks/resolve-variant-CcD8iG5l.js";
5
5
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photon-ai/pho-ui",
3
- "version": "0.7.2",
3
+ "version": "1.0.0",
4
4
  "description": "Pho Design System — Photon's React component library, built on Base UI",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -65,6 +65,10 @@
65
65
  "types": "./dist/src/components/switch/index.d.ts",
66
66
  "import": "./dist/components/switch.js"
67
67
  },
68
+ "./components/theme-toggle": {
69
+ "types": "./dist/src/components/theme-toggle/index.d.ts",
70
+ "import": "./dist/components/theme-toggle.js"
71
+ },
68
72
  "./components/checkbox": {
69
73
  "types": "./dist/src/components/checkbox/index.d.ts",
70
74
  "import": "./dist/components/checkbox.js"
@@ -181,6 +185,10 @@
181
185
  "types": "./dist/src/components/kbd/index.d.ts",
182
186
  "import": "./dist/components/kbd.js"
183
187
  },
188
+ "./components/spinner": {
189
+ "types": "./dist/src/components/spinner/index.d.ts",
190
+ "import": "./dist/components/spinner.js"
191
+ },
184
192
  "./components/notification": {
185
193
  "types": "./dist/src/components/notification/index.d.ts",
186
194
  "import": "./dist/components/notification.js"
@@ -1,19 +1,24 @@
1
1
  /**
2
- * Photon brand fonts, served from the shared asset CDN
3
- * (assets.photon.codes — public R2, versioned path, immutable caching, CORS
4
- * enabled). Import this and the fonts just work; no files to copy.
2
+ * Photon brand fonts. PolySans (commercial) is served from the shared asset
3
+ * CDN (assets.photon.codes — public R2, versioned path, immutable caching,
4
+ * CORS enabled). Azeret Mono is an open Google Font and comes from the Google
5
+ * Fonts CDN, which serves subsetted woff2 per script with `unicode-range`.
6
+ * Import this and the fonts just work; no files to copy.
5
7
  *
6
- * If your app must self-host (air-gapped, strict CSP without
7
- * `font-src https://assets.photon.codes`), skip this import and declare the
8
- * same four @font-face rules against your own /fonts/ paths — the theme only
9
- * references the family names.
8
+ * If your app must self-host (air-gapped, strict CSP without `font-src`
9
+ * covering https://assets.photon.codes and https://fonts.gstatic.com), skip
10
+ * this import and declare the same @font-face rules against your own /fonts/
11
+ * paths — the theme only references the family names.
10
12
  *
11
- * Roles (per photon.codes): PolySans is the voice — body/UI 400–500, headings
12
- * 550–600 (enable `ss02` on display text). Azeret Mono for code and small
13
+ * Roles (per photon.codes): PolySans is the voice — body at 400, UI/headings/
14
+ * hero slogans at 550 (`font-medium`); the `font-display` utility enables
15
+ * `ss02`. Azeret Mono for code and small
13
16
  * labels — always normal case: the system bans all-caps small text for
14
17
  * eyebrows, section titles, and descriptions. Instrument Serif (often
15
18
  * italic) is the editorial accent.
16
19
  */
20
+ @import "https://fonts.googleapis.com/css2?family=Azeret+Mono:wght@100..900&display=swap";
21
+
17
22
  @font-face {
18
23
  font-family: "PolySans";
19
24
  src: url("https://assets.photon.codes/pho-ui/v1/PolySansVF.woff2")
@@ -23,15 +28,6 @@
23
28
  font-display: swap;
24
29
  }
25
30
 
26
- @font-face {
27
- font-family: "Azeret Mono";
28
- src: url("https://assets.photon.codes/pho-ui/v1/AzeretMono-VariableFont_wght.ttf")
29
- format("truetype");
30
- font-weight: 100 900;
31
- font-style: normal;
32
- font-display: swap;
33
- }
34
-
35
31
  @font-face {
36
32
  font-family: "Instrument Serif";
37
33
  src: url("https://assets.photon.codes/pho-ui/v1/InstrumentSerif-Regular.woff2")
@@ -25,10 +25,14 @@
25
25
  }
26
26
 
27
27
  /* Grayscale AA — macOS subpixel smoothing paints PolySans (and other VFs)
28
- noticeably heavier at UI sizes. Matches the docs site's body.antialiased. */
28
+ noticeably heavier at UI sizes. Matches the docs site's body.antialiased.
29
+ font-synthesis: none — Instrument Serif ships only 400 (regular/italic)
30
+ and PolySans has no italic file; never let the browser fabricate a fake
31
+ bold or slant. A missing weight should look like a bug, not an imitation. */
29
32
  body {
30
33
  -webkit-font-smoothing: antialiased;
31
34
  -moz-osx-font-smoothing: grayscale;
35
+ font-synthesis: none;
32
36
  }
33
37
  }
34
38