@azmr/ui 0.1.0 → 0.2.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 (91) hide show
  1. package/README.md +171 -0
  2. package/dist/avatar.d.ts +58 -0
  3. package/dist/avatar.js +84 -0
  4. package/dist/breadcrumb.d.ts +47 -0
  5. package/dist/breadcrumb.js +120 -0
  6. package/dist/button-CYt2Zw3D.d.ts +64 -0
  7. package/dist/button.d.ts +54 -0
  8. package/dist/button.js +19 -0
  9. package/dist/chunk-2RIWL363.js +27 -0
  10. package/dist/chunk-2TQSHVXA.js +12 -0
  11. package/dist/chunk-7EOSDFS3.js +10 -0
  12. package/dist/chunk-C2HKIPLQ.js +118 -0
  13. package/dist/chunk-CAJLW6K2.js +16 -0
  14. package/dist/chunk-CZJZYN54.js +36 -0
  15. package/dist/chunk-EE4V6XMB.js +35 -0
  16. package/dist/chunk-EFHRRLAW.js +211 -0
  17. package/dist/chunk-GLB75NT3.js +21 -0
  18. package/dist/chunk-GX2YEJ43.js +36 -0
  19. package/dist/chunk-LTOLV67A.js +19 -0
  20. package/dist/chunk-LZMX27IZ.js +10 -0
  21. package/dist/chunk-NIIDYIZA.js +1 -0
  22. package/dist/chunk-Q4PPM2SU.js +97 -0
  23. package/dist/chunk-WVE2E37B.js +20 -0
  24. package/dist/chunk-XRZHP6BD.js +92 -0
  25. package/dist/disclosure.d.ts +72 -0
  26. package/dist/disclosure.js +221 -0
  27. package/dist/display.d.ts +111 -0
  28. package/dist/display.js +237 -0
  29. package/dist/floating.d.ts +81 -0
  30. package/dist/floating.js +231 -0
  31. package/dist/hooks.d.ts +33 -0
  32. package/dist/hooks.js +55 -0
  33. package/dist/icons/lucide.d.ts +77 -0
  34. package/dist/icons/lucide.js +135 -0
  35. package/dist/icons/mui-map.d.ts +1268 -0
  36. package/dist/icons/mui-map.js +352 -0
  37. package/dist/icons.d.ts +48 -0
  38. package/dist/icons.js +19 -0
  39. package/dist/index.d.ts +238 -3
  40. package/dist/index.js +242 -15
  41. package/dist/input.d.ts +14 -0
  42. package/dist/input.js +8 -0
  43. package/dist/layout/mui-map.d.ts +80 -0
  44. package/dist/layout/mui-map.js +104 -0
  45. package/dist/layout.d.ts +89 -0
  46. package/dist/layout.js +455 -0
  47. package/dist/lib.d.ts +11 -0
  48. package/dist/lib.js +6 -0
  49. package/dist/menu.d.ts +105 -0
  50. package/dist/menu.js +56 -0
  51. package/dist/modal.d.ts +105 -0
  52. package/dist/modal.js +273 -0
  53. package/dist/motion.css +295 -0
  54. package/dist/pagination.d.ts +82 -0
  55. package/dist/pagination.js +211 -0
  56. package/dist/presets.d.ts +97 -0
  57. package/dist/presets.js +94 -0
  58. package/dist/progress.d.ts +70 -0
  59. package/dist/progress.js +138 -0
  60. package/dist/react.d.ts +81 -0
  61. package/dist/react.js +281 -0
  62. package/dist/responsive-YJBnvsjt.d.ts +69 -0
  63. package/dist/scroll-area.d.ts +30 -0
  64. package/dist/scroll-area.js +80 -0
  65. package/dist/shared-Fkx-sjPt.d.ts +15 -0
  66. package/dist/spinner.d.ts +18 -0
  67. package/dist/spinner.js +39 -0
  68. package/dist/split-button.d.ts +32 -0
  69. package/dist/split-button.js +54 -0
  70. package/dist/stepper.d.ts +27 -0
  71. package/dist/stepper.js +104 -0
  72. package/dist/styles.css +24 -0
  73. package/dist/table.d.ts +52 -0
  74. package/dist/table.js +124 -0
  75. package/dist/theme.d.ts +111 -0
  76. package/dist/theme.js +414 -0
  77. package/dist/toast.d.ts +77 -0
  78. package/dist/toast.js +203 -0
  79. package/dist/toggle.d.ts +55 -0
  80. package/dist/toggle.js +88 -0
  81. package/dist/tokens.css +337 -0
  82. package/dist/transition.d.ts +51 -0
  83. package/dist/transition.js +79 -0
  84. package/dist/types-BBudf463.d.ts +39 -0
  85. package/dist/typography/mui-map.d.ts +91 -0
  86. package/dist/typography/mui-map.js +62 -0
  87. package/dist/typography.d.ts +90 -0
  88. package/dist/typography.js +193 -0
  89. package/dist/useSignal-C-3ZGURN.d.ts +21 -0
  90. package/dist/variants-De0c0ucO.d.ts +60 -0
  91. package/package.json +167 -12
@@ -0,0 +1,51 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode, ComponentType } from 'react';
3
+
4
+ interface CountUpProps extends Omit<HTMLAttributes<HTMLSpanElement>, "children"> {
5
+ /** The number to show. When it changes, the display counts to the new value. */
6
+ value: number;
7
+ /** Where to start when `animateOnMount` is on. Default 0. */
8
+ from?: number;
9
+ /** Count up from `from` when first shown, not only when `value` changes. Default false. */
10
+ animateOnMount?: boolean;
11
+ /** Milliseconds to count over. Default 800. */
12
+ duration?: number;
13
+ /** `Intl.NumberFormat` options: currency, percent, decimals. */
14
+ format?: Intl.NumberFormatOptions;
15
+ locale?: string | string[];
16
+ }
17
+ /**
18
+ * A number that counts to its new value. Screen readers are given the **final value at once** (the
19
+ * counting is hidden from them, so nobody hears a stream of numbers), the digits are tabular so the
20
+ * width does not jitter, and under reduced motion it jumps straight to the value.
21
+ */
22
+ declare function CountUp({ value, from, animateOnMount, duration, format, locale, className, ...props }: CountUpProps): react.JSX.Element;
23
+
24
+ type TransitionClass = string | Record<string, string>;
25
+ interface TransitionProps {
26
+ children?: ReactNode;
27
+ /** Gives the element a name, so the same name on another page or list item morphs between them. */
28
+ name?: string;
29
+ /** The animation when none of the more specific ones below apply. `"none"` turns it off. */
30
+ default?: TransitionClass;
31
+ enter?: TransitionClass;
32
+ exit?: TransitionClass;
33
+ update?: TransitionClass;
34
+ /** Used when this and another element share a `name` (the shared-element morph). */
35
+ share?: TransitionClass;
36
+ }
37
+ type ViewTransitionComponent = ComponentType<TransitionProps>;
38
+ /**
39
+ * React's `ViewTransition`, if this React has it. It is not in a stable React yet (the spec expects
40
+ * 19.3), so it is looked up by name at render time, under both the stable and the experimental name.
41
+ */
42
+ declare function findViewTransition(react?: Record<string, unknown>): ViewTransitionComponent | undefined;
43
+ /**
44
+ * Animates a route change, a list change or a shared element with React's `ViewTransition`. Where
45
+ * React does not have it yet, or the person prefers reduced motion, it renders its children and
46
+ * nothing else, so it is safe to add today and switches on when you upgrade React. The cross-fade
47
+ * itself is in `@azmr/ui/styles.css` (opacity only).
48
+ */
49
+ declare const Transition: (props: TransitionProps) => react.JSX.Element;
50
+
51
+ export { CountUp, type CountUpProps, Transition, type TransitionProps, findViewTransition };
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import {
3
+ useReducedMotion
4
+ } from "./chunk-WVE2E37B.js";
5
+ import {
6
+ cn
7
+ } from "./chunk-2TQSHVXA.js";
8
+
9
+ // src/motion/transition/count-up.tsx
10
+ import { useEffect, useRef, useState } from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ var easeOutCubic = (t) => 1 - (1 - t) ** 3;
13
+ function CountUp({
14
+ value,
15
+ from = 0,
16
+ animateOnMount = false,
17
+ duration = 800,
18
+ format,
19
+ locale,
20
+ className,
21
+ ...props
22
+ }) {
23
+ const reduced = useReducedMotion();
24
+ const [display, setDisplay] = useState(animateOnMount ? from : value);
25
+ const current = useRef(display);
26
+ useEffect(() => {
27
+ if (reduced || !Number.isFinite(value)) {
28
+ current.current = value;
29
+ setDisplay(value);
30
+ return;
31
+ }
32
+ const start = current.current;
33
+ if (start === value) return;
34
+ const t0 = performance.now();
35
+ let frame = 0;
36
+ const tick = (now) => {
37
+ const progress = Math.min((now - t0) / duration, 1);
38
+ const next = progress >= 1 ? value : start + (value - start) * easeOutCubic(progress);
39
+ current.current = next;
40
+ setDisplay(next);
41
+ if (progress < 1) frame = requestAnimationFrame(tick);
42
+ };
43
+ frame = requestAnimationFrame(tick);
44
+ return () => cancelAnimationFrame(frame);
45
+ }, [value, reduced, duration]);
46
+ const options = {
47
+ ...Number.isInteger(value) && { maximumFractionDigits: 0 },
48
+ ...format?.minimumFractionDigits !== void 0 && {
49
+ maximumFractionDigits: format.minimumFractionDigits
50
+ },
51
+ ...format
52
+ };
53
+ const text = (n) => new Intl.NumberFormat(locale, options).format(n);
54
+ return /* @__PURE__ */ jsxs("span", { className: cn("tabular-nums", className), ...props, children: [
55
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: text(display) }),
56
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: text(value) })
57
+ ] });
58
+ }
59
+
60
+ // src/motion/transition/transition.tsx
61
+ import * as React from "react";
62
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
63
+ function findViewTransition(react = React) {
64
+ return react.ViewTransition ?? react.unstable_ViewTransition;
65
+ }
66
+ function createTransition(find) {
67
+ return function Transition2(props) {
68
+ const reduced = useReducedMotion();
69
+ const ViewTransition = find();
70
+ if (!ViewTransition || reduced) return /* @__PURE__ */ jsx2(Fragment, { children: props.children });
71
+ return /* @__PURE__ */ jsx2(ViewTransition, { ...props });
72
+ };
73
+ }
74
+ var Transition = createTransition(findViewTransition);
75
+ export {
76
+ CountUp,
77
+ Transition,
78
+ findViewTransition
79
+ };
@@ -0,0 +1,39 @@
1
+ import { SVGProps, ComponentType } from 'react';
2
+
3
+ /** The size scale. A number is a literal pixel size (width and height). */
4
+ type IconSize = "xs" | "sm" | "md" | "lg" | "xl" | number;
5
+ /**
6
+ * The props the Icon passes to the icon component it renders. Lucide, Tabler and plain
7
+ * `(props: SVGProps<SVGSVGElement>) => <svg />` components all accept these, so any of them
8
+ * can be used as an icon. Every prop is optional so the library components assign to it.
9
+ */
10
+ interface IconRenderProps {
11
+ className?: string;
12
+ width?: number | string;
13
+ height?: number | string;
14
+ strokeWidth?: number | string;
15
+ role?: string;
16
+ "aria-hidden"?: boolean | "true" | "false";
17
+ "aria-label"?: string;
18
+ "aria-labelledby"?: string;
19
+ "data-motion"?: string;
20
+ }
21
+ type IconComponent = ComponentType<IconRenderProps>;
22
+ interface IconProps extends Omit<SVGProps<SVGSVGElement>, "ref" | "size" | "children"> {
23
+ /** The icon to render: a Lucide or Tabler icon, or your own SVG component. */
24
+ icon?: IconComponent;
25
+ /** Looks the icon up in the registry (`registerIcons`). `icon` wins when both are given. */
26
+ name?: string;
27
+ /** `xs` .. `xl` follow the size tokens; a number is a pixel size. Default `md`. */
28
+ size?: IconSize;
29
+ /** Turns the icon into a loading indicator. It keeps spinning under reduced motion. */
30
+ spin?: boolean;
31
+ /**
32
+ * Decorative by default (`aria-hidden`). Give `aria-label` or `aria-labelledby` to make the
33
+ * icon meaningful: it then has `role="img"` and an accessible name.
34
+ */
35
+ "aria-label"?: string;
36
+ "aria-labelledby"?: string;
37
+ }
38
+
39
+ export type { IconProps as I, IconComponent as a, IconRenderProps as b, IconSize as c };
@@ -0,0 +1,91 @@
1
+ import { T as TypographyVariant } from '../variants-De0c0ucO.js';
2
+
3
+ /**
4
+ * How MUI `Typography` maps to `@azmr/ui` `Typography`. Pure data for the migration codemod, which
5
+ * rewrites what is exact and flags what is `approximate` for a human to look at. Imports nothing
6
+ * but a type.
7
+ */
8
+ interface MuiVariantTarget {
9
+ /** The `@azmr/ui` variant to use. */
10
+ variant: TypographyVariant;
11
+ /** True when the look differs a little (weight, case), so the codemod should flag it. */
12
+ approximate?: true;
13
+ note?: string;
14
+ }
15
+ declare const muiTypographyMap: {
16
+ readonly variants: {
17
+ readonly h1: {
18
+ readonly variant: "h1";
19
+ };
20
+ readonly h2: {
21
+ readonly variant: "h2";
22
+ };
23
+ readonly h3: {
24
+ readonly variant: "h3";
25
+ };
26
+ readonly h4: {
27
+ readonly variant: "h4";
28
+ };
29
+ readonly h5: {
30
+ readonly variant: "h5";
31
+ };
32
+ readonly h6: {
33
+ readonly variant: "h6";
34
+ };
35
+ readonly subtitle1: {
36
+ readonly variant: "lead";
37
+ readonly approximate: true;
38
+ readonly note: "`lead` is muted text; MUI subtitle1 uses the normal text colour. Add className=\"text-foreground\" to match.";
39
+ };
40
+ readonly subtitle2: {
41
+ readonly variant: "small";
42
+ readonly approximate: true;
43
+ readonly note: "MUI subtitle2 is medium weight; use <Text size=\"sm\" weight=\"medium\"> to match exactly.";
44
+ };
45
+ readonly body1: {
46
+ readonly variant: "body";
47
+ };
48
+ readonly body2: {
49
+ readonly variant: "small";
50
+ };
51
+ readonly caption: {
52
+ readonly variant: "caption";
53
+ readonly approximate: true;
54
+ readonly note: "`caption` is muted text; MUI caption uses the normal text colour. Add className=\"text-foreground\" to match.";
55
+ };
56
+ readonly overline: {
57
+ readonly variant: "overline";
58
+ readonly approximate: true;
59
+ readonly note: "`overline` is muted text; MUI overline uses the normal text colour. Add className=\"text-foreground\" to match.";
60
+ };
61
+ readonly button: {
62
+ readonly variant: "small";
63
+ readonly approximate: true;
64
+ readonly note: "MUI `button` is uppercase medium text; use <Text size=\"sm\" weight=\"medium\" className=\"uppercase\">.";
65
+ };
66
+ readonly inherit: {
67
+ readonly variant: "body";
68
+ readonly approximate: true;
69
+ readonly note: "MUI `inherit` takes the parent's font; use <Text as=\"span\" size=\"base\" className=\"text-inherit\"> if that is the intent.";
70
+ };
71
+ };
72
+ /** MUI prop name -> what to do. A prop name means "same prop"; other strings are instructions. */
73
+ readonly props: {
74
+ readonly variant: "variant";
75
+ readonly component: "as";
76
+ readonly gutterBottom: "gutterBottom";
77
+ readonly noWrap: "noWrap";
78
+ readonly align: "align";
79
+ readonly paragraph: "Use <Text> (a paragraph) instead of paragraph.";
80
+ readonly color: "Use tone (muted, destructive, success, warning, info) or a text-* class.";
81
+ readonly sx: "Use className with Tailwind utilities.";
82
+ };
83
+ };
84
+ type MuiTypographyVariant = keyof typeof muiTypographyMap.variants;
85
+ /**
86
+ * Resolves a MUI Typography `variant` prop. `undefined` is MUI's own default, `body1`. Returns
87
+ * `undefined` for a name MUI does not have.
88
+ */
89
+ declare function resolveMuiTypography(variant: string | undefined): MuiVariantTarget | undefined;
90
+
91
+ export { type MuiTypographyVariant, type MuiVariantTarget, muiTypographyMap, resolveMuiTypography };
@@ -0,0 +1,62 @@
1
+ // src/components/typography/mui-map.ts
2
+ var muiTypographyMap = {
3
+ variants: {
4
+ h1: { variant: "h1" },
5
+ h2: { variant: "h2" },
6
+ h3: { variant: "h3" },
7
+ h4: { variant: "h4" },
8
+ h5: { variant: "h5" },
9
+ h6: { variant: "h6" },
10
+ subtitle1: {
11
+ variant: "lead",
12
+ approximate: true,
13
+ note: '`lead` is muted text; MUI subtitle1 uses the normal text colour. Add className="text-foreground" to match.'
14
+ },
15
+ subtitle2: {
16
+ variant: "small",
17
+ approximate: true,
18
+ note: 'MUI subtitle2 is medium weight; use <Text size="sm" weight="medium"> to match exactly.'
19
+ },
20
+ body1: { variant: "body" },
21
+ body2: { variant: "small" },
22
+ caption: {
23
+ variant: "caption",
24
+ approximate: true,
25
+ note: '`caption` is muted text; MUI caption uses the normal text colour. Add className="text-foreground" to match.'
26
+ },
27
+ overline: {
28
+ variant: "overline",
29
+ approximate: true,
30
+ note: '`overline` is muted text; MUI overline uses the normal text colour. Add className="text-foreground" to match.'
31
+ },
32
+ button: {
33
+ variant: "small",
34
+ approximate: true,
35
+ note: 'MUI `button` is uppercase medium text; use <Text size="sm" weight="medium" className="uppercase">.'
36
+ },
37
+ inherit: {
38
+ variant: "body",
39
+ approximate: true,
40
+ note: 'MUI `inherit` takes the parent\'s font; use <Text as="span" size="base" className="text-inherit"> if that is the intent.'
41
+ }
42
+ },
43
+ /** MUI prop name -> what to do. A prop name means "same prop"; other strings are instructions. */
44
+ props: {
45
+ variant: "variant",
46
+ component: "as",
47
+ gutterBottom: "gutterBottom",
48
+ noWrap: "noWrap",
49
+ align: "align",
50
+ paragraph: "Use <Text> (a paragraph) instead of paragraph.",
51
+ color: "Use tone (muted, destructive, success, warning, info) or a text-* class.",
52
+ sx: "Use className with Tailwind utilities."
53
+ }
54
+ };
55
+ function resolveMuiTypography(variant) {
56
+ const name = variant ?? "body1";
57
+ return Object.hasOwn(muiTypographyMap.variants, name) ? muiTypographyMap.variants[name] : void 0;
58
+ }
59
+ export {
60
+ muiTypographyMap,
61
+ resolveMuiTypography
62
+ };
@@ -0,0 +1,90 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode, ElementType, ComponentPropsWithoutRef, Ref, ReactElement } from 'react';
3
+ import { T as TypographyVariant, a as TypographyAlign } from './variants-De0c0ucO.js';
4
+ export { t as typographyVariants } from './variants-De0c0ucO.js';
5
+
6
+ interface TypographyOwnProps {
7
+ /** The type-scale step. It picks the semantic element and the look. Default `body`. */
8
+ variant?: TypographyVariant;
9
+ align?: TypographyAlign;
10
+ /** Truncate to one line with an ellipsis. */
11
+ noWrap?: boolean;
12
+ /** Add a small bottom margin (MUI's `gutterBottom`). */
13
+ gutterBottom?: boolean;
14
+ }
15
+ type TypographyProps<T extends ElementType = "p"> = TypographyOwnProps & {
16
+ /** Render a different element or component, keeping the look: `variant="h1" as="h2"`. */
17
+ as?: T;
18
+ } & Omit<ComponentPropsWithoutRef<T>, keyof TypographyOwnProps | "as">;
19
+ /**
20
+ * Text with a type-scale variant. The variant decides the semantic element (`h1` to `h6`, `p`,
21
+ * `span`), and `as` overrides it without changing the look. Server-safe.
22
+ */
23
+ declare const Typography: <T extends ElementType = "p">(props: TypographyProps<T> & {
24
+ ref?: Ref<HTMLElement>;
25
+ }) => ReactElement | null;
26
+ type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
27
+ type HeadingSize = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
28
+ interface HeadingProps extends Omit<HTMLAttributes<HTMLHeadingElement>, "children">, Omit<TypographyOwnProps, "variant"> {
29
+ /** The semantic level (`<h2>`). Default 2. */
30
+ level?: HeadingLevel;
31
+ /** The visual size when it should differ from the level, e.g. a quiet `<h2>`. */
32
+ size?: HeadingSize;
33
+ children?: ReactNode;
34
+ }
35
+ /** A heading whose semantic level and visual size are separate decisions. */
36
+ declare const Heading: react.ForwardRefExoticComponent<HeadingProps & react.RefAttributes<HTMLHeadingElement>>;
37
+ declare const TONE_CLASSES: {
38
+ readonly muted: "text-muted-foreground";
39
+ readonly destructive: "text-destructive";
40
+ readonly success: "text-success";
41
+ readonly warning: "text-warning-text";
42
+ readonly info: "text-info";
43
+ };
44
+ declare const SIZE_CLASSES: {
45
+ readonly sm: "text-sm";
46
+ readonly base: "text-base";
47
+ readonly lg: "text-lg";
48
+ };
49
+ declare const WEIGHT_CLASSES: {
50
+ readonly normal: "font-normal";
51
+ readonly medium: "font-medium";
52
+ readonly semibold: "font-semibold";
53
+ readonly bold: "font-bold";
54
+ };
55
+ interface TextProps extends HTMLAttributes<HTMLElement> {
56
+ as?: "p" | "span" | "div" | "label" | "strong" | "em" | "small";
57
+ size?: keyof typeof SIZE_CLASSES;
58
+ tone?: keyof typeof TONE_CLASSES;
59
+ weight?: keyof typeof WEIGHT_CLASSES;
60
+ }
61
+ /** Running text. A paragraph by default; `tone` uses semantic colour tokens. */
62
+ declare const Text: react.ForwardRefExoticComponent<TextProps & react.RefAttributes<HTMLElement>>;
63
+ declare const UNDERLINE_CLASSES: {
64
+ readonly always: "underline";
65
+ readonly hover: "hover:underline focus-visible:underline";
66
+ readonly none: "no-underline";
67
+ };
68
+ interface LinkOwnProps {
69
+ /**
70
+ * `always` (default) keeps links distinguishable without relying on colour alone;
71
+ * `hover` underlines on hover and focus; `none` removes it.
72
+ */
73
+ underline?: keyof typeof UNDERLINE_CLASSES;
74
+ /** Opens in a new tab with `rel="noopener noreferrer"` and tells screen readers. */
75
+ external?: boolean;
76
+ }
77
+ type LinkProps<T extends ElementType = "a"> = LinkOwnProps & {
78
+ /** Render a router link: `<Link as={NextLink} href="/x">`. */
79
+ as?: T;
80
+ } & Omit<ComponentPropsWithoutRef<T>, keyof LinkOwnProps | "as">;
81
+ /** A link. Underlined by default, with a full-strength focus ring. Server-safe. */
82
+ declare const Link: <T extends ElementType = "a">(props: LinkProps<T> & {
83
+ ref?: Ref<HTMLAnchorElement>;
84
+ }) => ReactElement | null;
85
+ /** Inline code. */
86
+ declare const Code: react.ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & react.RefAttributes<HTMLElement>>;
87
+ /** A keyboard key, for documenting shortcuts. */
88
+ declare const Kbd: react.ForwardRefExoticComponent<HTMLAttributes<HTMLElement> & react.RefAttributes<HTMLElement>>;
89
+
90
+ export { Code, Heading, type HeadingProps, Kbd, Link, type LinkOwnProps, type LinkProps, Text, type TextProps, Typography, TypographyAlign, type TypographyOwnProps, type TypographyProps, TypographyVariant };
@@ -0,0 +1,193 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-7EOSDFS3.js";
4
+
5
+ // src/components/typography/typography.tsx
6
+ import {
7
+ forwardRef
8
+ } from "react";
9
+
10
+ // src/components/typography/variants.ts
11
+ var typographyVariants = {
12
+ h1: { element: "h1", classes: "scroll-m-20 text-4xl font-bold tracking-tight text-foreground" },
13
+ h2: {
14
+ element: "h2",
15
+ classes: "scroll-m-20 text-3xl font-semibold tracking-tight text-foreground"
16
+ },
17
+ h3: {
18
+ element: "h3",
19
+ classes: "scroll-m-20 text-2xl font-semibold tracking-tight text-foreground"
20
+ },
21
+ h4: {
22
+ element: "h4",
23
+ classes: "scroll-m-20 text-xl font-semibold tracking-tight text-foreground"
24
+ },
25
+ h5: { element: "h5", classes: "scroll-m-20 text-lg font-semibold text-foreground" },
26
+ h6: { element: "h6", classes: "scroll-m-20 text-base font-semibold text-foreground" },
27
+ lead: { element: "p", classes: "text-lg text-muted-foreground" },
28
+ body: { element: "p", classes: "text-base" },
29
+ small: { element: "p", classes: "text-sm" },
30
+ caption: { element: "span", classes: "text-xs text-muted-foreground" },
31
+ overline: {
32
+ element: "span",
33
+ classes: "text-xs font-medium uppercase tracking-wider text-muted-foreground"
34
+ }
35
+ };
36
+ var ALIGN_CLASSES = {
37
+ left: "text-left",
38
+ center: "text-center",
39
+ right: "text-right",
40
+ justify: "text-justify"
41
+ };
42
+
43
+ // src/components/typography/typography.tsx
44
+ import { jsx, jsxs } from "react/jsx-runtime";
45
+ function TypographyImpl({
46
+ variant = "body",
47
+ as,
48
+ align,
49
+ noWrap,
50
+ gutterBottom,
51
+ className,
52
+ ...props
53
+ }, ref) {
54
+ const definition = typographyVariants[variant];
55
+ const Component = as ?? definition.element;
56
+ return /* @__PURE__ */ jsx(
57
+ Component,
58
+ {
59
+ ref,
60
+ className: cn(
61
+ definition.classes,
62
+ align && ALIGN_CLASSES[align],
63
+ // `truncate` needs a block-level box: on an inline span (caption, overline, or any
64
+ // `as="span"`) the ellipsis would never show and the text would overflow.
65
+ noWrap && cn("truncate", Component === "span" && "block"),
66
+ gutterBottom && "mb-[0.35em]",
67
+ className
68
+ ),
69
+ ...props
70
+ }
71
+ );
72
+ }
73
+ var Typography = forwardRef(TypographyImpl);
74
+ var Heading = forwardRef(function Heading2({ level = 2, size, ...props }, ref) {
75
+ return /* @__PURE__ */ jsx(
76
+ Typography,
77
+ {
78
+ ref,
79
+ as: `h${level}`,
80
+ variant: size ?? `h${level}`,
81
+ ...props
82
+ }
83
+ );
84
+ });
85
+ var TONE_CLASSES = {
86
+ muted: "text-muted-foreground",
87
+ destructive: "text-destructive",
88
+ success: "text-success",
89
+ warning: "text-warning-text",
90
+ info: "text-info"
91
+ };
92
+ var SIZE_CLASSES = { sm: "text-sm", base: "text-base", lg: "text-lg" };
93
+ var WEIGHT_CLASSES = {
94
+ normal: "font-normal",
95
+ medium: "font-medium",
96
+ semibold: "font-semibold",
97
+ bold: "font-bold"
98
+ };
99
+ var Text = forwardRef(function Text2({ as: Component = "p", size = "base", tone, weight, className, ...props }, ref) {
100
+ return /* @__PURE__ */ jsx(
101
+ Component,
102
+ {
103
+ ref,
104
+ className: cn(
105
+ SIZE_CLASSES[size],
106
+ tone && TONE_CLASSES[tone],
107
+ weight && WEIGHT_CLASSES[weight],
108
+ className
109
+ ),
110
+ ...props
111
+ }
112
+ );
113
+ });
114
+ var UNDERLINE_CLASSES = {
115
+ always: "underline",
116
+ hover: "hover:underline focus-visible:underline",
117
+ none: "no-underline"
118
+ };
119
+ function LinkImpl({
120
+ as: Component = "a",
121
+ underline = "always",
122
+ external,
123
+ target: targetProp,
124
+ rel: relProp,
125
+ className,
126
+ children,
127
+ ...props
128
+ }, ref) {
129
+ const target = targetProp ?? (external ? "_blank" : void 0);
130
+ const opensNewTab = target === "_blank";
131
+ const rel = opensNewTab ? safeRel(relProp) : relProp;
132
+ return /* @__PURE__ */ jsxs(
133
+ Component,
134
+ {
135
+ ref,
136
+ className: cn(
137
+ "rounded-sm font-medium text-primary underline-offset-4 outline-none",
138
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
139
+ UNDERLINE_CLASSES[underline],
140
+ className
141
+ ),
142
+ target,
143
+ rel,
144
+ ...props,
145
+ children: [
146
+ children,
147
+ opensNewTab && /* @__PURE__ */ jsx("span", { className: "sr-only", children: "(opens in a new tab)" })
148
+ ]
149
+ }
150
+ );
151
+ }
152
+ function safeRel(rel) {
153
+ const tokens = new Set((rel ?? "").split(/\s+/).filter(Boolean));
154
+ tokens.add("noopener");
155
+ tokens.add("noreferrer");
156
+ return [...tokens].join(" ");
157
+ }
158
+ var Link = forwardRef(LinkImpl);
159
+ var Code = forwardRef(function Code2({ className, ...props }, ref) {
160
+ return /* @__PURE__ */ jsx(
161
+ "code",
162
+ {
163
+ ref,
164
+ className: cn(
165
+ "rounded bg-muted px-[0.3em] py-[0.15em] font-mono text-[0.875em] text-foreground",
166
+ className
167
+ ),
168
+ ...props
169
+ }
170
+ );
171
+ });
172
+ var Kbd = forwardRef(function Kbd2({ className, ...props }, ref) {
173
+ return /* @__PURE__ */ jsx(
174
+ "kbd",
175
+ {
176
+ ref,
177
+ className: cn(
178
+ "rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-xs font-medium text-foreground",
179
+ className
180
+ ),
181
+ ...props
182
+ }
183
+ );
184
+ });
185
+ export {
186
+ Code,
187
+ Heading,
188
+ Kbd,
189
+ Link,
190
+ Text,
191
+ Typography,
192
+ typographyVariants
193
+ };
@@ -0,0 +1,21 @@
1
+ import { Signal } from '@azmr/core';
2
+
3
+ /**
4
+ * React hook that subscribes to a Signal and triggers re-renders on change.
5
+ *
6
+ * Built on `useSyncExternalStore` rather than `useState` + `useEffect`, so
7
+ * concurrent rendering can't tear: React reads the current value via
8
+ * `Signal.peek()` (which never registers a dependency) for both the initial
9
+ * render and any snapshot check mid-render, and only uses the subscription
10
+ * to know when to ask again — it does not rely on `Signal.subscribe()`
11
+ * firing immediately with the current value on subscribe (it deliberately
12
+ * doesn't; read the value separately, as this hook does, rather than wait
13
+ * for the first callback).
14
+ *
15
+ * `subscribe` and `getSnapshot` are memoised on `signal`, so an unrelated
16
+ * re-render does not tear down and re-create the subscription; passing a
17
+ * different signal resubscribes exactly once.
18
+ */
19
+ declare function useSignal<T>(signal: Signal<T>): T;
20
+
21
+ export { useSignal as u };