@glyphui/ui 0.3.0 → 0.4.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 (63) hide show
  1. package/README.md +65 -12
  2. package/dist/chart.d.ts +1 -1
  3. package/dist/chart.js +28 -19
  4. package/dist/color-mode-script-Bf7LYkOj.js +13 -0
  5. package/dist/color-mode-script-KdwItKN2.d.ts +16 -0
  6. package/dist/data-table.js +3 -3
  7. package/dist/{field-CWZ_FdkD.js → field-CVbywksE.js} +21 -6
  8. package/dist/{field-context-anGPYXbW.js → field-context-7I-cWvvS.js} +2 -2
  9. package/dist/form.d.ts +11 -4
  10. package/dist/form.js +34 -9
  11. package/dist/glyph-context-DxGJrdvr.d.ts +50 -0
  12. package/dist/{index-BWK4czIG.d.ts → index-Bi7rZ6ba.d.ts} +11 -0
  13. package/dist/index.d.ts +87 -80
  14. package/dist/index.js +155 -82
  15. package/dist/react-router.d.ts +18 -0
  16. package/dist/react-router.js +44 -0
  17. package/dist/{states-DR-OoDT-.js → states-IvhjBgna.js} +2 -2
  18. package/dist/{tv-DBAGlgXJ.js → tv-DLbT66dC.js} +13 -1
  19. package/dist/vite.d.ts +27 -0
  20. package/dist/vite.js +29 -0
  21. package/fonts.css +10 -0
  22. package/package.json +32 -5
  23. package/src/components/badge/badge.styles.ts +11 -16
  24. package/src/components/banner/banner.styles.ts +4 -4
  25. package/src/components/button/button.styles.ts +3 -1
  26. package/src/components/calendar/calendar.styles.ts +3 -2
  27. package/src/components/dropdown-menu/dropdown-menu.styles.ts +2 -2
  28. package/src/components/eyebrow/eyebrow.styles.ts +1 -1
  29. package/src/components/field/field-context.ts +2 -0
  30. package/src/components/field/field.styles.ts +4 -2
  31. package/src/components/field/field.tsx +25 -4
  32. package/src/components/file-upload/file-upload.styles.ts +3 -3
  33. package/src/components/link/link.styles.ts +1 -1
  34. package/src/components/modal/modal.styles.ts +2 -0
  35. package/src/components/modal/modal.tsx +30 -5
  36. package/src/components/navigation-menu/navigation-menu.tsx +14 -4
  37. package/src/components/password-input/password-input.styles.ts +3 -3
  38. package/src/components/prose/prose.tsx +1 -1
  39. package/src/components/scroll-area/scroll-area.tsx +23 -1
  40. package/src/components/segmented-control/segmented-control.tsx +17 -2
  41. package/src/components/slider/slider.tsx +9 -2
  42. package/src/components/stepper/stepper.styles.ts +2 -2
  43. package/src/components/tag-input/tag-input.styles.ts +1 -1
  44. package/src/components/text/text.styles.ts +4 -4
  45. package/src/integrations/react-router.tsx +39 -0
  46. package/src/integrations/vite.test-d.ts +6 -0
  47. package/src/integrations/vite.test.ts +28 -0
  48. package/src/integrations/vite.ts +33 -0
  49. package/src/internal/dev.ts +12 -0
  50. package/src/patterns/app-shell/app-shell.styles.ts +1 -1
  51. package/src/patterns/app-shell/navbar.tsx +34 -16
  52. package/src/patterns/app-shell/sidebar.tsx +7 -2
  53. package/src/patterns/chart/chart-parts.tsx +10 -2
  54. package/src/patterns/chart/chart.tsx +22 -5
  55. package/src/patterns/form/form.tsx +48 -6
  56. package/src/patterns/form/index.ts +1 -0
  57. package/src/provider/color-mode-script.ts +28 -0
  58. package/src/provider/color-mode.tsx +14 -20
  59. package/src/provider/glyph-context.ts +38 -0
  60. package/src/provider/glyph-provider.tsx +16 -4
  61. package/src/provider/index.ts +4 -0
  62. package/src/provider/theme-scope.tsx +7 -3
  63. package/styles.css +72 -0
@@ -8,8 +8,19 @@ import {
8
8
  useMemo,
9
9
  useState,
10
10
  } from 'react'
11
-
12
- export type ColorModePreference = ColorMode | 'system'
11
+ import {
12
+ type ColorModeOptions,
13
+ type ColorModePreference,
14
+ DARK_QUERY,
15
+ DEFAULT_STORAGE_KEY,
16
+ getColorModeScript,
17
+ } from './color-mode-script'
18
+
19
+ export {
20
+ type ColorModeOptions,
21
+ type ColorModePreference,
22
+ getColorModeScript,
23
+ } from './color-mode-script'
13
24
 
14
25
  export interface ColorModeContextValue {
15
26
  /** What the user chose. */
@@ -21,16 +32,8 @@ export interface ColorModeContextValue {
21
32
 
22
33
  const ColorModeContext = createContext<ColorModeContextValue | null>(null)
23
34
 
24
- const DARK_QUERY = '(prefers-color-scheme: dark)'
25
35
  const isBrowser = typeof window !== 'undefined'
26
36
 
27
- export interface ColorModeOptions {
28
- /** Used when nothing is stored yet. */
29
- defaultMode?: ColorModePreference
30
- /** localStorage key. Set to `null` to disable persistence. */
31
- storageKey?: string | null
32
- }
33
-
34
37
  function readStoredMode(storageKey: string | null): ColorModePreference | null {
35
38
  if (!isBrowser || !storageKey) return null
36
39
  try {
@@ -59,7 +62,7 @@ function suppressTransitions(): () => void {
59
62
  export function ColorModeProvider({
60
63
  children,
61
64
  defaultMode = 'system',
62
- storageKey = 'glyph-mode',
65
+ storageKey = DEFAULT_STORAGE_KEY,
63
66
  }: ColorModeOptions & { children: ReactNode }) {
64
67
  const [mode, setModeState] = useState<ColorModePreference>(
65
68
  () => readStoredMode(storageKey) ?? defaultMode,
@@ -112,15 +115,6 @@ export function useColorMode(): ColorModeContextValue {
112
115
  * Inline script that sets `data-mode` on `<html>` before first paint, so a dark-mode
113
116
  * visitor never sees a light flash. Put it in `<head>`.
114
117
  */
115
- export function getColorModeScript({
116
- defaultMode = 'system',
117
- storageKey = 'glyph-mode',
118
- }: ColorModeOptions = {}): string {
119
- return `(function(){try{var d=${JSON.stringify(defaultMode)},m=null;${
120
- storageKey ? `m=localStorage.getItem(${JSON.stringify(storageKey)});` : ''
121
- }if(m!=='light'&&m!=='dark'&&m!=='system')m=d;if(m==='system')m=matchMedia('${DARK_QUERY}').matches?'dark':'light';var r=document.documentElement;r.dataset.mode=m;r.style.colorScheme=m}catch(e){}})()`
122
- }
123
-
124
118
  /** `<script>` element version of `getColorModeScript` for SSR frameworks. */
125
119
  export function ColorModeScript(props: ColorModeOptions) {
126
120
  return (
@@ -22,9 +22,29 @@ export interface LinkComponentProps {
22
22
  */
23
23
  export type LinkComponent = ElementType<LinkComponentProps>
24
24
 
25
+ export interface IsActiveOptions {
26
+ /** Match only this exact path, not its children. Default false. */
27
+ end?: boolean
28
+ }
29
+
30
+ /**
31
+ * A hook that reports whether `href` is the current location. Glyph calls it once per
32
+ * navigation item on every render, so it must follow the rules of hooks.
33
+ */
34
+ export type UseIsActive = (href: string, options?: IsActiveOptions) => boolean
35
+
36
+ /** Connects Glyph's navigation components to your router. */
37
+ export interface GlyphRouter {
38
+ /** Renders links client-side. */
39
+ Link: LinkComponent
40
+ /** Marks Sidebar, Navbar and NavigationMenu items active automatically. */
41
+ useIsActive?: UseIsActive
42
+ }
43
+
25
44
  export interface GlyphContextValue {
26
45
  theme: Theme
27
46
  linkComponent: LinkComponent
47
+ useIsActive: UseIsActive
28
48
  }
29
49
 
30
50
  export const GlyphContext = createContext<GlyphContextValue | null>(null)
@@ -40,3 +60,21 @@ export function useGlyph(): GlyphContextValue {
40
60
  export function useLinkComponent(): LinkComponent {
41
61
  return useContext(GlyphContext)?.linkComponent ?? 'a'
42
62
  }
63
+
64
+ export const neverActive: UseIsActive = () => false
65
+
66
+ /**
67
+ * Whether `href` is the current page, using the router passed to `GlyphProvider`.
68
+ * Returns `explicit` when it is set, so an `active` prop always wins.
69
+ */
70
+ export function useIsActive(
71
+ href: string | undefined,
72
+ options?: IsActiveOptions & { explicit?: boolean | undefined },
73
+ ): boolean {
74
+ const isActive = useContext(GlyphContext)?.useIsActive ?? neverActive
75
+ const { explicit, ...matchOptions } = options ?? {}
76
+ // Always call the router hook so the hook order never changes between renders.
77
+ const matched = isActive(href ?? '', matchOptions)
78
+ if (explicit !== undefined) return explicit
79
+ return href ? matched : false
80
+ }
@@ -5,7 +5,7 @@ import { ConfirmProvider } from '../components/alert-dialog/confirm'
5
5
  import { Toaster, type ToasterProps } from '../components/toast/toast'
6
6
  import { glyph } from '../themes'
7
7
  import { type ColorModePreference, ColorModeProvider } from './color-mode'
8
- import { GlyphContext, type LinkComponent } from './glyph-context'
8
+ import { GlyphContext, type GlyphRouter, type LinkComponent, neverActive } from './glyph-context'
9
9
  import { ThemeStyle } from './theme-style'
10
10
 
11
11
  export interface GlyphProviderProps {
@@ -16,7 +16,13 @@ export interface GlyphProviderProps {
16
16
  defaultMode?: ColorModePreference
17
17
  /** localStorage key for the user's color mode. `null` disables persistence. */
18
18
  storageKey?: string | null
19
- /** Your router's link, used by Navbar, Sidebar and other navigation components. */
19
+ /**
20
+ * Your router: its link component, plus an optional `useIsActive` hook that marks the
21
+ * current page in Sidebar, Navbar and NavigationMenu. React Router:
22
+ * `import { reactRouter } from '@glyphui/ui/react-router'`.
23
+ */
24
+ router?: GlyphRouter
25
+ /** Your router's link only. Prefer `router`, which also handles active items. */
20
26
  linkComponent?: LinkComponent
21
27
  /**
22
28
  * Mounts a themed `<Toaster />` so `toast()` works anywhere. `true` for defaults, or pass
@@ -31,10 +37,16 @@ export function GlyphProvider({
31
37
  theme = glyph,
32
38
  defaultMode,
33
39
  storageKey,
34
- linkComponent = 'a',
40
+ router,
41
+ linkComponent,
35
42
  toaster = false,
36
43
  }: GlyphProviderProps) {
37
- const value = useMemo(() => ({ theme, linkComponent }), [theme, linkComponent])
44
+ const Link = router?.Link ?? linkComponent ?? 'a'
45
+ const useIsActive = router?.useIsActive ?? neverActive
46
+ const value = useMemo(
47
+ () => ({ theme, linkComponent: Link, useIsActive }),
48
+ [theme, Link, useIsActive],
49
+ )
38
50
  return (
39
51
  <ColorModeProvider
40
52
  {...(defaultMode !== undefined && { defaultMode })}
@@ -9,9 +9,13 @@ export {
9
9
  } from './color-mode'
10
10
  export {
11
11
  type GlyphContextValue,
12
+ type GlyphRouter,
13
+ type IsActiveOptions,
12
14
  type LinkComponent,
13
15
  type LinkComponentProps,
16
+ type UseIsActive,
14
17
  useGlyph,
18
+ useIsActive,
15
19
  useLinkComponent,
16
20
  } from './glyph-context'
17
21
  export { GlyphProvider, type GlyphProviderProps } from './glyph-provider'
@@ -2,7 +2,7 @@ import { type ColorMode, scopeSelector, type Theme } from '@glyphui/tokens'
2
2
  import { Slot } from 'radix-ui'
3
3
  import { type ComponentProps, useContext, useMemo } from 'react'
4
4
  import { cn } from '../utils/cn'
5
- import { GlyphContext } from './glyph-context'
5
+ import { GlyphContext, neverActive } from './glyph-context'
6
6
  import { ThemeStyle } from './theme-style'
7
7
 
8
8
  export interface ThemeScopeProps extends ComponentProps<'div'> {
@@ -16,8 +16,12 @@ export interface ThemeScopeProps extends ComponentProps<'div'> {
16
16
  export function ThemeScope({ theme, mode, asChild, children, ...props }: ThemeScopeProps) {
17
17
  const parent = useContext(GlyphContext)
18
18
  const value = useMemo(
19
- () => ({ theme, linkComponent: parent?.linkComponent ?? 'a' }),
20
- [theme, parent?.linkComponent],
19
+ () => ({
20
+ theme,
21
+ linkComponent: parent?.linkComponent ?? 'a',
22
+ useIsActive: parent?.useIsActive ?? neverActive,
23
+ }),
24
+ [theme, parent?.linkComponent, parent?.useIsActive],
21
25
  )
22
26
  const Comp = asChild ? Slot.Root : 'div'
23
27
  return (
package/styles.css CHANGED
@@ -39,7 +39,17 @@
39
39
  --color-danger-foreground: var(--glyph-danger-foreground);
40
40
  --color-info: var(--glyph-info);
41
41
  --color-info-foreground: var(--glyph-info-foreground);
42
+ --color-primary-text: var(--glyph-primary-text);
43
+ --color-success-text: var(--glyph-success-text);
44
+ --color-warning-text: var(--glyph-warning-text);
45
+ --color-danger-text: var(--glyph-danger-text);
46
+ --color-info-text: var(--glyph-info-text);
42
47
  --color-overlay: var(--glyph-overlay);
48
+ --color-chart-1: var(--glyph-chart-1);
49
+ --color-chart-2: var(--glyph-chart-2);
50
+ --color-chart-3: var(--glyph-chart-3);
51
+ --color-chart-4: var(--glyph-chart-4);
52
+ --color-chart-5: var(--glyph-chart-5);
43
53
 
44
54
  --radius-xs: var(--glyph-radius-xs);
45
55
  --radius-sm: var(--glyph-radius-sm);
@@ -142,6 +152,68 @@
142
152
  transform: translateY(-8px);
143
153
  }
144
154
  }
155
+ @keyframes glyph-fade-out {
156
+ to {
157
+ opacity: 0;
158
+ }
159
+ }
160
+ @keyframes glyph-sheet-in-right {
161
+ from {
162
+ transform: translateX(100%);
163
+ }
164
+ }
165
+ @keyframes glyph-sheet-out-right {
166
+ to {
167
+ transform: translateX(100%);
168
+ }
169
+ }
170
+ @keyframes glyph-sheet-in-left {
171
+ from {
172
+ transform: translateX(-100%);
173
+ }
174
+ }
175
+ @keyframes glyph-sheet-out-left {
176
+ to {
177
+ transform: translateX(-100%);
178
+ }
179
+ }
180
+ @keyframes glyph-sheet-in-top {
181
+ from {
182
+ transform: translateY(-100%);
183
+ }
184
+ }
185
+ @keyframes glyph-sheet-out-top {
186
+ to {
187
+ transform: translateY(-100%);
188
+ }
189
+ }
190
+ @keyframes glyph-sheet-in-bottom {
191
+ from {
192
+ transform: translateY(100%);
193
+ }
194
+ }
195
+ @keyframes glyph-sheet-out-bottom {
196
+ to {
197
+ transform: translateY(100%);
198
+ }
199
+ }
200
+ /* Radix sets the content height variable on Accordion and Collapsible content. */
201
+ @keyframes glyph-collapse-down {
202
+ from {
203
+ height: 0;
204
+ }
205
+ to {
206
+ height: var(--radix-accordion-content-height, var(--radix-collapsible-content-height));
207
+ }
208
+ }
209
+ @keyframes glyph-collapse-up {
210
+ from {
211
+ height: var(--radix-accordion-content-height, var(--radix-collapsible-content-height));
212
+ }
213
+ to {
214
+ height: 0;
215
+ }
216
+ }
145
217
 
146
218
  @layer base {
147
219
  *,