@spark-ui/components 14.0.0 → 14.1.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 (43) hide show
  1. package/dist/circular-meter/CircularMeter.d.ts +34 -0
  2. package/dist/circular-meter/CircularMeter.styles.d.ts +5 -0
  3. package/dist/circular-meter/CircularMeterContent.d.ts +6 -0
  4. package/dist/circular-meter/CircularMeterContext.d.ts +36 -0
  5. package/dist/circular-meter/CircularMeterLabel.d.ts +7 -0
  6. package/dist/circular-meter/CircularMeterTrack.d.ts +8 -0
  7. package/dist/circular-meter/CircularMeterValue.d.ts +7 -0
  8. package/dist/circular-meter/index.d.mts +16 -0
  9. package/dist/circular-meter/index.d.ts +16 -0
  10. package/dist/circular-meter/index.js +2 -0
  11. package/dist/circular-meter/index.js.map +1 -0
  12. package/dist/circular-meter/index.mjs +316 -0
  13. package/dist/circular-meter/index.mjs.map +1 -0
  14. package/dist/circular-meter/useIntersectionAnimation.d.ts +26 -0
  15. package/dist/meter/Meter.d.ts +18 -0
  16. package/dist/meter/MeterContext.d.ts +12 -0
  17. package/dist/meter/MeterLabel.d.ts +7 -0
  18. package/dist/meter/MeterTrack.d.ts +7 -0
  19. package/dist/meter/MeterTrack.styles.d.ts +8 -0
  20. package/dist/meter/MeterValue.d.ts +7 -0
  21. package/dist/meter/index.d.mts +13 -0
  22. package/dist/meter/index.d.ts +13 -0
  23. package/dist/meter/index.js +2 -0
  24. package/dist/meter/index.js.map +1 -0
  25. package/dist/meter/index.mjs +177 -0
  26. package/dist/meter/index.mjs.map +1 -0
  27. package/dist/meter/useIntersectionAnimation.d.ts +26 -0
  28. package/dist/progress/Progress.d.ts +14 -6
  29. package/dist/progress/ProgressContext.d.ts +3 -3
  30. package/dist/progress/ProgressIndicator.d.ts +9 -2
  31. package/dist/progress/ProgressLabel.d.ts +3 -2
  32. package/dist/progress/ProgressTrack.d.ts +7 -0
  33. package/dist/progress/ProgressValue.d.ts +7 -0
  34. package/dist/progress/index.d.mts +6 -6
  35. package/dist/progress/index.d.ts +6 -6
  36. package/dist/progress/index.js +1 -1
  37. package/dist/progress/index.js.map +1 -1
  38. package/dist/progress/index.mjs +122 -111
  39. package/dist/progress/index.mjs.map +1 -1
  40. package/package.json +4 -4
  41. package/dist/progress/ProgressBar.d.ts +0 -6
  42. package/dist/progress/ProgressBar.styles.d.ts +0 -5
  43. package/dist/progress/ProgressIndicator.styles.d.ts +0 -7
@@ -0,0 +1,34 @@
1
+ import { Meter as BaseMeter } from '@base-ui/react/meter';
2
+ import { ComponentProps, PropsWithChildren, Ref } from 'react';
3
+ import { MeterIndicatorStylesProps } from '../meter/MeterTrack.styles';
4
+ import { CircularMeterStylesProps } from './CircularMeter.styles';
5
+ export type CircularMeterSize = 'sm' | 'md' | 'lg' | 'xl';
6
+ export interface CircularMeterProps extends Omit<ComponentProps<typeof BaseMeter.Root>, 'render'>, Pick<MeterIndicatorStylesProps, 'intent'>, CircularMeterStylesProps {
7
+ /**
8
+ * Size of the circle.
9
+ *
10
+ * - `sm`: 24px diameter, 4px stroke width
11
+ * - `md`: 64px diameter, 8px stroke width
12
+ * - `lg`: 96px diameter, 8px stroke width
13
+ * - `xl`: 128px diameter, 8px stroke width
14
+ *
15
+ * Defaults to `md`.
16
+ */
17
+ size?: CircularMeterSize;
18
+ /**
19
+ * Change the default rendered element for the one passed as a child, merging their props and behavior.
20
+ */
21
+ asChild?: boolean;
22
+ ref?: Ref<HTMLDivElement>;
23
+ /**
24
+ * Orientation of the circular meter.
25
+ *
26
+ * - `vertical`: Elements are stacked vertically (default)
27
+ * - `horizontal`: Elements are arranged horizontally
28
+ */
29
+ orientation?: 'vertical' | 'horizontal';
30
+ }
31
+ export declare const CircularMeter: {
32
+ ({ className, value, max, min, size: sizeProp, intent, orientation, children, ref, ...others }: PropsWithChildren<CircularMeterProps>): import("react/jsx-runtime").JSX.Element;
33
+ displayName: string;
34
+ };
@@ -0,0 +1,5 @@
1
+ import { VariantProps } from 'class-variance-authority';
2
+ export declare const circularMeterStyles: (props?: ({
3
+ orientation?: "horizontal" | "vertical" | null | undefined;
4
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
5
+ export type CircularMeterStylesProps = VariantProps<typeof circularMeterStyles>;
@@ -0,0 +1,6 @@
1
+ import { ComponentProps, PropsWithChildren } from 'react';
2
+ export type CircularMeterContentProps = ComponentProps<'div'> & PropsWithChildren;
3
+ export declare const CircularMeterContent: {
4
+ ({ className, children, ...others }: CircularMeterContentProps): import("react/jsx-runtime").JSX.Element;
5
+ displayName: string;
6
+ };
@@ -0,0 +1,36 @@
1
+ import { MeterIndicatorStylesProps } from '../meter/MeterTrack.styles';
2
+ import { CircularMeterSize } from './CircularMeter';
3
+ export interface CircularMeterContextValue {
4
+ value: number;
5
+ max: number;
6
+ min: number;
7
+ intent: MeterIndicatorStylesProps['intent'];
8
+ onLabelId: (id?: string) => void;
9
+ /**
10
+ * Size variant of the circular meter.
11
+ */
12
+ sizeProp: CircularMeterSize;
13
+ /**
14
+ * Orientation of the circular meter.
15
+ */
16
+ orientation: 'vertical' | 'horizontal';
17
+ /**
18
+ * Diameter of the SVG circle in pixels.
19
+ */
20
+ size: number;
21
+ /**
22
+ * Radius of the SVG circle in pixels.
23
+ */
24
+ radius: number;
25
+ /**
26
+ * Circumference of the SVG circle in pixels.
27
+ */
28
+ circumference: number;
29
+ /**
30
+ * Stroke width of the SVG circle in pixels.
31
+ */
32
+ strokeWidth: number;
33
+ }
34
+ export declare const CircularMeterContext: import('react').Context<CircularMeterContextValue | null>;
35
+ export declare const ID_PREFIX = ":circular-meter";
36
+ export declare const useCircularMeter: () => CircularMeterContextValue;
@@ -0,0 +1,7 @@
1
+ import { Meter as BaseMeter } from '@base-ui/react/meter';
2
+ import { ComponentProps } from 'react';
3
+ export type CircularMeterLabelProps = Omit<ComponentProps<typeof BaseMeter.Label>, 'render'>;
4
+ export declare const CircularMeterLabel: {
5
+ ({ id: idProp, children, className, ref: forwardedRef, ...others }: CircularMeterLabelProps): import("react/jsx-runtime").JSX.Element;
6
+ displayName: string;
7
+ };
@@ -0,0 +1,8 @@
1
+ import { Meter as BaseMeter } from '@base-ui/react/meter';
2
+ import { ComponentProps, PropsWithChildren } from 'react';
3
+ export declare const useCircularMeterTrack: () => boolean;
4
+ export type CircularMeterTrackProps = Omit<ComponentProps<typeof BaseMeter.Track>, 'render'> & PropsWithChildren;
5
+ export declare const CircularMeterTrack: {
6
+ ({ className, children, ...others }: CircularMeterTrackProps): import("react/jsx-runtime").JSX.Element;
7
+ displayName: string;
8
+ };
@@ -0,0 +1,7 @@
1
+ import { Meter as BaseMeter } from '@base-ui/react/meter';
2
+ import { ComponentProps, PropsWithChildren } from 'react';
3
+ export type CircularMeterValueProps = Omit<ComponentProps<typeof BaseMeter.Value>, 'render'>;
4
+ export declare const CircularMeterValue: {
5
+ ({ className, children, ...others }: PropsWithChildren<CircularMeterValueProps>): import("react/jsx-runtime").JSX.Element;
6
+ displayName: string;
7
+ };
@@ -0,0 +1,16 @@
1
+ import { CircularMeter as Root } from './CircularMeter';
2
+ import { CircularMeterContent } from './CircularMeterContent';
3
+ import { CircularMeterLabel } from './CircularMeterLabel';
4
+ import { CircularMeterTrack } from './CircularMeterTrack';
5
+ import { CircularMeterValue } from './CircularMeterValue';
6
+ export declare const CircularMeter: typeof Root & {
7
+ Content: typeof CircularMeterContent;
8
+ Label: typeof CircularMeterLabel;
9
+ Track: typeof CircularMeterTrack;
10
+ Value: typeof CircularMeterValue;
11
+ };
12
+ export { type CircularMeterProps } from './CircularMeter';
13
+ export { type CircularMeterContentProps } from './CircularMeterContent';
14
+ export { type CircularMeterLabelProps } from './CircularMeterLabel';
15
+ export { type CircularMeterTrackProps } from './CircularMeterTrack';
16
+ export { type CircularMeterValueProps } from './CircularMeterValue';
@@ -0,0 +1,16 @@
1
+ import { CircularMeter as Root } from './CircularMeter';
2
+ import { CircularMeterContent } from './CircularMeterContent';
3
+ import { CircularMeterLabel } from './CircularMeterLabel';
4
+ import { CircularMeterTrack } from './CircularMeterTrack';
5
+ import { CircularMeterValue } from './CircularMeterValue';
6
+ export declare const CircularMeter: typeof Root & {
7
+ Content: typeof CircularMeterContent;
8
+ Label: typeof CircularMeterLabel;
9
+ Track: typeof CircularMeterTrack;
10
+ Value: typeof CircularMeterValue;
11
+ };
12
+ export { type CircularMeterProps } from './CircularMeter';
13
+ export { type CircularMeterContentProps } from './CircularMeterContent';
14
+ export { type CircularMeterLabelProps } from './CircularMeterLabel';
15
+ export { type CircularMeterTrackProps } from './CircularMeterTrack';
16
+ export { type CircularMeterValueProps } from './CircularMeterValue';
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react/jsx-runtime"),M=require("@base-ui/react/meter"),m=require("class-variance-authority"),a=require("react"),w=require("@spark-ui/hooks/use-merge-refs"),L=m.cva(["focus-visible:u-outline gap-md flex items-center"],{variants:{orientation:{vertical:["default:flex-col"],horizontal:["default:flex-row"]}},defaultVariants:{orientation:"vertical"}}),N=a.createContext(null),S=":circular-meter",h=()=>{const r=a.useContext(N);if(!r)throw new Error("useCircularMeter must be used within a CircularMeter provider");return r},E={sm:{diameter:24,strokeWidth:4},md:{diameter:64,strokeWidth:8},lg:{diameter:96,strokeWidth:8},xl:{diameter:128,strokeWidth:8}},I=({className:r,value:t,max:i=100,min:s=0,size:o="lg",intent:l="support",orientation:n="vertical",children:e,ref:d,...u})=>{const[f,p]=a.useState(),{diameter:x,strokeWidth:C}=E[o],y=x/2-C/2,b=2*Math.PI*y,g=a.useMemo(()=>({value:t??0,max:i,min:s,intent:l,onLabelId:p,sizeProp:o,orientation:n,size:x,radius:y,circumference:b,strokeWidth:C}),[i,s,t,l,p,o,n,x,y,b,C]);return c.jsx(N.Provider,{value:g,children:c.jsx(M.Meter.Root,{"data-spark-component":"circular-meter",ref:d,className:m.cx(L({orientation:n}),r),style:u.style,value:t,max:i,min:s,"aria-labelledby":f,...u,children:e})})};I.displayName="CircularMeter";const v=({className:r,children:t,...i})=>{const{orientation:s}=h();return c.jsx("div",{"data-spark-component":"circular-meter-content",className:m.cx("gap-xs flex default:flex-col",s==="vertical"&&"default:text-center",r),...i,children:t})};v.displayName="CircularMeter.Content";function q(r,t,i={}){const{threshold:s=.1,rootMargin:o}=i,l=a.useRef(!1),n=a.useRef(t);return a.useEffect(()=>{n.current=t},[t]),a.useEffect(()=>{const e=r.current;if(!e||l.current)return;const d=new IntersectionObserver(u=>{u.forEach(f=>{f.isIntersecting&&!l.current&&requestAnimationFrame(()=>{l.current||(l.current=!0,n.current(),d.disconnect())})})},{threshold:s,rootMargin:o});return d.observe(e),()=>{d.disconnect()}},[r,s,o]),l.current}const R=a.createContext(!1),V=()=>a.useContext(R),A=m.cva([],{variants:{intent:{main:["text-main/dim-4"],support:["text-support/dim-4"],success:["text-success/dim-4"],alert:["text-alert/dim-4"],danger:["text-error/dim-4"],info:["text-info/dim-4"]}}}),O=m.cva([],{variants:{intent:{main:["text-main"],support:["text-support"],success:["text-success"],alert:["text-alert"],danger:["text-error"],info:["text-info"]}}}),k=({className:r,children:t,...i})=>{const{value:s,max:o,min:l,intent:n,size:e,radius:d,circumference:u,strokeWidth:f}=h(),p=(s-l)/(o-l)*100,x=u-p/100*u,C=O({intent:n}),y=a.useRef(null),[b,g]=a.useState(!1);return q(y,()=>{g(!0)}),c.jsx(M.Meter.Track,{"data-spark-component":"circular-meter-track",className:r,...i,children:c.jsx(R.Provider,{value:!0,children:c.jsxs("svg",{ref:y,width:e,height:e,viewBox:`0 0 ${e} ${e}`,children:[c.jsxs("g",{style:{transform:"rotate(-90deg)",transformOrigin:"center"},children:[c.jsx("circle",{cx:e/2,cy:e/2,r:d,fill:"none",stroke:"currentColor",strokeWidth:f,className:A({intent:n})}),c.jsx("circle",{"data-spark-component":"circular-meter-indicator",cx:e/2,cy:e/2,r:d,fill:"none",stroke:"currentColor",strokeWidth:f,strokeLinecap:"round",className:m.cx(C,"ease-standard transition-[stroke-dashoffset] duration-700","motion-reduce:transition-none"),style:{strokeDasharray:u,strokeDashoffset:b?x:u}})]}),t&&c.jsx("foreignObject",{x:8,y:8,width:e-16,height:e-16,children:c.jsx("div",{className:"p-md flex h-full w-full flex-col items-center justify-center rounded-full text-center",style:{width:e-16,height:e-16},children:t})})]})})})};k.displayName="CircularMeter.Track";const W=m.cva([],{variants:{size:{sm:"",md:"",lg:"",xl:""},inside:{true:["default:text-on-surface/dim-1"],false:["default:text-on-surface"]}},compoundVariants:[{size:"sm",inside:!0,class:"default:text-small"},{size:"md",inside:!0,class:"default:text-small "},{size:"lg",inside:!0,class:"default:text-caption"},{size:"xl",inside:!0,class:"default:text-body-2"},{size:"sm",inside:!1,class:"default:text-body-1"},{size:"md",inside:!1,class:"default:text-body-1"},{size:"lg",inside:!1,class:"default:text-body-1"},{size:"xl",inside:!1,class:"default:text-body-1"}],defaultVariants:{size:"lg",inside:!0}}),z=({id:r,children:t,className:i,ref:s,...o})=>{const l=`${S}-label-${a.useId()}`,n=r||l,{onLabelId:e,sizeProp:d}=h(),u=V(),f=a.useCallback(x=>{e(x?n:void 0)},[n,e]),p=w.useMergeRefs(s,f);return c.jsx(M.Meter.Label,{"data-spark-component":"circular-meter-label",id:n,className:W({size:d,inside:u,className:i}),ref:p,...o,children:t})};z.displayName="CircularMeter.Label";const P=m.cva(["default:text-on-surface default:font-bold"],{variants:{size:{sm:"",md:"",lg:"",xl:""},inside:{true:[],false:[]}},compoundVariants:[{size:"sm",inside:!0,class:"default:text-body-2 default:font-bold"},{size:"md",inside:!0,class:"default:text-body-2 default:font-bold"},{size:"lg",inside:!0,class:"default:text-body-1 default:font-bold"},{size:"xl",inside:!0,class:"default:text-display-3"},{size:"sm",inside:!1,class:"default:text-body-1 default:font-bold"},{size:"md",inside:!1,class:"default:text-headline-2"},{size:"lg",inside:!1,class:"default:text-headline-2"},{size:"xl",inside:!1,class:"default:text-display-3"}],defaultVariants:{size:"lg",inside:!0}}),j=({className:r,children:t,...i})=>{const{sizeProp:s}=h(),o=V();return c.jsx(M.Meter.Value,{"data-spark-component":"circular-meter-value",className:P({size:s,inside:o,className:r}),...i,children:t})};j.displayName="CircularMeter.Value";const T=Object.assign(I,{Content:v,Label:z,Track:k,Value:j});T.displayName="CircularMeter";v.displayName="CircularMeter.Content";z.displayName="CircularMeter.Label";k.displayName="CircularMeter.Track";j.displayName="CircularMeter.Value";exports.CircularMeter=T;
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/circular-meter/CircularMeter.styles.ts","../../src/circular-meter/CircularMeterContext.tsx","../../src/circular-meter/CircularMeter.tsx","../../src/circular-meter/CircularMeterContent.tsx","../../src/circular-meter/useIntersectionAnimation.ts","../../src/circular-meter/CircularMeterTrack.tsx","../../src/circular-meter/CircularMeterLabel.tsx","../../src/circular-meter/CircularMeterValue.tsx","../../src/circular-meter/index.ts"],"sourcesContent":["import { cva, VariantProps } from 'class-variance-authority'\n\nexport const circularMeterStyles = cva(['focus-visible:u-outline gap-md flex items-center'], {\n variants: {\n orientation: {\n vertical: ['default:flex-col'],\n horizontal: ['default:flex-row'],\n },\n },\n defaultVariants: {\n orientation: 'vertical',\n },\n})\n\nexport type CircularMeterStylesProps = VariantProps<typeof circularMeterStyles>\n","import { createContext, useContext } from 'react'\n\nimport { MeterIndicatorStylesProps } from '../meter/MeterTrack.styles'\nimport { CircularMeterSize } from './CircularMeter'\n\nexport interface CircularMeterContextValue {\n value: number\n max: number\n min: number\n intent: MeterIndicatorStylesProps['intent']\n onLabelId: (id?: string) => void\n /**\n * Size variant of the circular meter.\n */\n sizeProp: CircularMeterSize\n /**\n * Orientation of the circular meter.\n */\n orientation: 'vertical' | 'horizontal'\n /**\n * Diameter of the SVG circle in pixels.\n */\n size: number\n /**\n * Radius of the SVG circle in pixels.\n */\n radius: number\n /**\n * Circumference of the SVG circle in pixels.\n */\n circumference: number\n /**\n * Stroke width of the SVG circle in pixels.\n */\n strokeWidth: number\n}\n\nexport const CircularMeterContext = createContext<CircularMeterContextValue | null>(null)\n\nexport const ID_PREFIX = ':circular-meter'\n\nexport const useCircularMeter = () => {\n const context = useContext(CircularMeterContext)\n\n if (!context) {\n throw new Error('useCircularMeter must be used within a CircularMeter provider')\n }\n\n return context\n}\n","import { Meter as BaseMeter } from '@base-ui/react/meter'\nimport { cx } from 'class-variance-authority'\nimport { ComponentProps, PropsWithChildren, Ref, useMemo, useState } from 'react'\n\nimport { MeterIndicatorStylesProps } from '../meter/MeterTrack.styles'\nimport { circularMeterStyles, CircularMeterStylesProps } from './CircularMeter.styles'\nimport { CircularMeterContext } from './CircularMeterContext'\n\nexport type CircularMeterSize = 'sm' | 'md' | 'lg' | 'xl'\n\nconst CIRCULAR_METER_SIZE_CONFIG: Record<\n CircularMeterSize,\n { diameter: number; strokeWidth: number }\n> = {\n sm: { diameter: 24, strokeWidth: 4 },\n md: { diameter: 64, strokeWidth: 8 },\n lg: { diameter: 96, strokeWidth: 8 },\n xl: { diameter: 128, strokeWidth: 8 },\n}\n\nexport interface CircularMeterProps\n extends Omit<ComponentProps<typeof BaseMeter.Root>, 'render'>,\n Pick<MeterIndicatorStylesProps, 'intent'>,\n CircularMeterStylesProps {\n /**\n * Size of the circle.\n *\n * - `sm`: 24px diameter, 4px stroke width\n * - `md`: 64px diameter, 8px stroke width\n * - `lg`: 96px diameter, 8px stroke width\n * - `xl`: 128px diameter, 8px stroke width\n *\n * Defaults to `md`.\n */\n size?: CircularMeterSize\n /**\n * Change the default rendered element for the one passed as a child, merging their props and behavior.\n */\n asChild?: boolean\n ref?: Ref<HTMLDivElement>\n /**\n * Orientation of the circular meter.\n *\n * - `vertical`: Elements are stacked vertically (default)\n * - `horizontal`: Elements are arranged horizontally\n */\n orientation?: 'vertical' | 'horizontal'\n}\n\nexport const CircularMeter = ({\n className,\n value,\n max = 100,\n min = 0,\n size: sizeProp = 'lg',\n intent = 'support',\n orientation = 'vertical',\n children,\n ref,\n ...others\n}: PropsWithChildren<CircularMeterProps>) => {\n const [labelId, setLabelId] = useState<string>()\n\n const { diameter: size, strokeWidth } = CIRCULAR_METER_SIZE_CONFIG[sizeProp]\n\n const radius = size / 2 - strokeWidth / 2\n const circumference = 2 * Math.PI * radius\n\n const contextValue = useMemo(() => {\n return {\n value: value ?? 0,\n max,\n min,\n intent,\n onLabelId: setLabelId,\n sizeProp,\n orientation: orientation,\n size,\n radius,\n circumference,\n strokeWidth,\n }\n }, [\n max,\n min,\n value,\n intent,\n setLabelId,\n sizeProp,\n orientation,\n size,\n radius,\n circumference,\n strokeWidth,\n ])\n\n return (\n <CircularMeterContext.Provider value={contextValue}>\n <BaseMeter.Root\n data-spark-component=\"circular-meter\"\n ref={ref}\n className={cx(circularMeterStyles({ orientation }), className)}\n style={others.style}\n value={value}\n max={max}\n min={min}\n aria-labelledby={labelId}\n {...others}\n >\n {children}\n </BaseMeter.Root>\n </CircularMeterContext.Provider>\n )\n}\n\nCircularMeter.displayName = 'CircularMeter'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, PropsWithChildren } from 'react'\n\nimport { useCircularMeter } from './CircularMeterContext'\n\nexport type CircularMeterContentProps = ComponentProps<'div'> & PropsWithChildren\n\nexport const CircularMeterContent = ({\n className,\n children,\n ...others\n}: CircularMeterContentProps) => {\n const { orientation } = useCircularMeter()\n\n return (\n <div\n data-spark-component=\"circular-meter-content\"\n className={cx(\n 'gap-xs flex default:flex-col',\n orientation === 'vertical' && 'default:text-center',\n className\n )}\n {...others}\n >\n {children}\n </div>\n )\n}\n\nCircularMeterContent.displayName = 'CircularMeter.Content'\n","import { RefObject, useEffect, useRef } from 'react'\n\nexport interface UseIntersectionAnimationOptions {\n /**\n * The threshold at which the callback should be triggered.\n * A value of 0 means as soon as any part of the element is visible.\n * A value of 1 means the entire element must be visible.\n * @default 0.1\n */\n threshold?: number\n /**\n * The root margin for the Intersection Observer.\n * This can be used to trigger the animation before the element enters the viewport.\n * @default undefined\n */\n rootMargin?: string\n}\n\n/**\n * Hook to trigger an animation callback when an element enters the viewport.\n * The callback is only triggered once, when the element first becomes visible.\n *\n * @param elementRef - Reference to the element to observe\n * @param onIntersect - Callback to execute when the element enters the viewport\n * @param options - Configuration options for the Intersection Observer\n * @returns Whether the animation has been triggered\n */\nexport function useIntersectionAnimation(\n elementRef: RefObject<Element | null>,\n onIntersect: () => void,\n options: UseIntersectionAnimationOptions = {}\n): boolean {\n const { threshold = 0.1, rootMargin } = options\n const hasTriggeredRef = useRef(false)\n const callbackRef = useRef(onIntersect)\n\n // Keep callback ref up to date\n useEffect(() => {\n callbackRef.current = onIntersect\n }, [onIntersect])\n\n useEffect(() => {\n const element = elementRef.current\n if (!element || hasTriggeredRef.current) return\n\n const observer = new IntersectionObserver(\n entries => {\n entries.forEach(entry => {\n if (entry.isIntersecting && !hasTriggeredRef.current) {\n // Use requestAnimationFrame to ensure the callback runs at the right time\n requestAnimationFrame(() => {\n if (!hasTriggeredRef.current) {\n hasTriggeredRef.current = true\n callbackRef.current()\n // Disconnect observer after callback is triggered (only trigger once)\n observer.disconnect()\n }\n })\n }\n })\n },\n {\n threshold,\n rootMargin,\n }\n )\n\n observer.observe(element)\n\n return () => {\n observer.disconnect()\n }\n }, [elementRef, threshold, rootMargin])\n\n return hasTriggeredRef.current\n}\n","import { Meter as BaseMeter } from '@base-ui/react/meter'\nimport { cva, cx } from 'class-variance-authority'\nimport {\n ComponentProps,\n createContext,\n PropsWithChildren,\n useContext,\n useRef,\n useState,\n} from 'react'\n\nimport { useCircularMeter } from './CircularMeterContext'\nimport { useIntersectionAnimation } from './useIntersectionAnimation'\n\nconst CircularMeterTrackContext = createContext<boolean>(false)\n\nexport const useCircularMeterTrack = () => {\n return useContext(CircularMeterTrackContext)\n}\n\nexport type CircularMeterTrackProps = Omit<ComponentProps<typeof BaseMeter.Track>, 'render'> &\n PropsWithChildren\n\nconst circularMeterTrackStyles = cva([], {\n variants: {\n intent: {\n main: ['text-main/dim-4'],\n support: ['text-support/dim-4'],\n success: ['text-success/dim-4'],\n alert: ['text-alert/dim-4'],\n danger: ['text-error/dim-4'],\n info: ['text-info/dim-4'],\n },\n },\n})\n\nconst circularMeterIndicatorStyles = cva([], {\n variants: {\n intent: {\n main: ['text-main'],\n support: ['text-support'],\n success: ['text-success'],\n alert: ['text-alert'],\n danger: ['text-error'],\n info: ['text-info'],\n },\n },\n})\n\nexport const CircularMeterTrack = ({ className, children, ...others }: CircularMeterTrackProps) => {\n const { value, max, min, intent, size, radius, circumference, strokeWidth } = useCircularMeter()\n const percentage = ((value - min) / (max - min)) * 100\n const offset = circumference - (percentage / 100) * circumference\n\n const intentClasses = circularMeterIndicatorStyles({ intent })\n const svgRef = useRef<SVGSVGElement>(null)\n const [hasAnimated, setHasAnimated] = useState(false)\n\n // Trigger animation when component enters viewport\n useIntersectionAnimation(svgRef, () => {\n setHasAnimated(true)\n })\n\n return (\n <BaseMeter.Track data-spark-component=\"circular-meter-track\" className={className} {...others}>\n <CircularMeterTrackContext.Provider value={true}>\n <svg ref={svgRef} width={size} height={size} viewBox={`0 0 ${size} ${size}`}>\n <g style={{ transform: 'rotate(-90deg)', transformOrigin: 'center' }}>\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n className={circularMeterTrackStyles({ intent })}\n />\n <circle\n data-spark-component=\"circular-meter-indicator\"\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n className={cx(\n intentClasses,\n 'ease-standard transition-[stroke-dashoffset] duration-700',\n 'motion-reduce:transition-none'\n )}\n style={\n {\n strokeDasharray: circumference,\n // Start at circumference (0% filled) for initial animation, then use offset for subsequent changes\n strokeDashoffset: hasAnimated ? offset : circumference,\n } as React.CSSProperties\n }\n />\n </g>\n {children && (\n <foreignObject x={8} y={8} width={size - 16} height={size - 16}>\n <div\n className=\"p-md flex h-full w-full flex-col items-center justify-center rounded-full text-center\"\n style={{ width: size - 16, height: size - 16 }}\n >\n {children}\n </div>\n </foreignObject>\n )}\n </svg>\n </CircularMeterTrackContext.Provider>\n </BaseMeter.Track>\n )\n}\n\nCircularMeterTrack.displayName = 'CircularMeter.Track'\n","import { Meter as BaseMeter } from '@base-ui/react/meter'\nimport { useMergeRefs } from '@spark-ui/hooks/use-merge-refs'\nimport { cva } from 'class-variance-authority'\nimport { ComponentProps, useCallback, useId } from 'react'\n\nimport { ID_PREFIX, useCircularMeter } from './CircularMeterContext'\nimport { useCircularMeterTrack } from './CircularMeterTrack'\n\nexport type CircularMeterLabelProps = Omit<ComponentProps<typeof BaseMeter.Label>, 'render'>\n\nconst labelStyles = cva([], {\n variants: {\n size: {\n sm: '',\n md: '',\n lg: '',\n xl: '',\n },\n inside: {\n true: ['default:text-on-surface/dim-1'],\n false: ['default:text-on-surface'],\n },\n },\n compoundVariants: [\n // Inside the track\n { size: 'sm', inside: true, class: 'default:text-small' },\n { size: 'md', inside: true, class: 'default:text-small ' },\n { size: 'lg', inside: true, class: 'default:text-caption' },\n { size: 'xl', inside: true, class: 'default:text-body-2' },\n // Outside the track\n { size: 'sm', inside: false, class: 'default:text-body-1' },\n { size: 'md', inside: false, class: 'default:text-body-1' },\n { size: 'lg', inside: false, class: 'default:text-body-1' },\n { size: 'xl', inside: false, class: 'default:text-body-1' },\n ],\n defaultVariants: {\n size: 'lg',\n inside: true,\n },\n})\n\nexport const CircularMeterLabel = ({\n id: idProp,\n children,\n className,\n ref: forwardedRef,\n ...others\n}: CircularMeterLabelProps) => {\n const internalID = `${ID_PREFIX}-label-${useId()}`\n const id = idProp || internalID\n\n const { onLabelId, sizeProp } = useCircularMeter()\n const isInside = useCircularMeterTrack()\n const rootRef = useCallback(\n (el: HTMLSpanElement) => {\n onLabelId(el ? id : undefined)\n },\n [id, onLabelId]\n )\n const ref = useMergeRefs(forwardedRef, rootRef)\n\n return (\n <BaseMeter.Label\n data-spark-component=\"circular-meter-label\"\n id={id}\n className={labelStyles({ size: sizeProp, inside: isInside, className })}\n ref={ref}\n {...others}\n >\n {children}\n </BaseMeter.Label>\n )\n}\n\nCircularMeterLabel.displayName = 'CircularMeter.Label'\n","import { Meter as BaseMeter } from '@base-ui/react/meter'\nimport { cva } from 'class-variance-authority'\nimport { ComponentProps, PropsWithChildren } from 'react'\n\nimport { useCircularMeter } from './CircularMeterContext'\nimport { useCircularMeterTrack } from './CircularMeterTrack'\n\nexport type CircularMeterValueProps = Omit<ComponentProps<typeof BaseMeter.Value>, 'render'>\n\nconst valueStyles = cva(['default:text-on-surface default:font-bold'], {\n variants: {\n size: {\n sm: '',\n md: '',\n lg: '',\n xl: '',\n },\n inside: {\n true: [],\n false: [],\n },\n },\n compoundVariants: [\n // Inside the track\n { size: 'sm', inside: true, class: 'default:text-body-2 default:font-bold' },\n { size: 'md', inside: true, class: 'default:text-body-2 default:font-bold' },\n { size: 'lg', inside: true, class: 'default:text-body-1 default:font-bold' },\n { size: 'xl', inside: true, class: 'default:text-display-3' },\n // Outside the track\n { size: 'sm', inside: false, class: 'default:text-body-1 default:font-bold' },\n { size: 'md', inside: false, class: 'default:text-headline-2' },\n { size: 'lg', inside: false, class: 'default:text-headline-2' },\n { size: 'xl', inside: false, class: 'default:text-display-3' },\n ],\n defaultVariants: {\n size: 'lg',\n inside: true,\n },\n})\n\nexport const CircularMeterValue = ({\n className,\n children,\n ...others\n}: PropsWithChildren<CircularMeterValueProps>) => {\n const { sizeProp } = useCircularMeter()\n const isInside = useCircularMeterTrack()\n\n return (\n <BaseMeter.Value\n data-spark-component=\"circular-meter-value\"\n className={valueStyles({ size: sizeProp, inside: isInside, className })}\n {...others}\n >\n {children}\n </BaseMeter.Value>\n )\n}\n\nCircularMeterValue.displayName = 'CircularMeter.Value'\n","import { CircularMeter as Root } from './CircularMeter'\nimport { CircularMeterContent } from './CircularMeterContent'\nimport { CircularMeterLabel } from './CircularMeterLabel'\nimport { CircularMeterTrack } from './CircularMeterTrack'\nimport { CircularMeterValue } from './CircularMeterValue'\n\nexport const CircularMeter: typeof Root & {\n Content: typeof CircularMeterContent\n Label: typeof CircularMeterLabel\n Track: typeof CircularMeterTrack\n Value: typeof CircularMeterValue\n} = Object.assign(Root, {\n Content: CircularMeterContent,\n Label: CircularMeterLabel,\n Track: CircularMeterTrack,\n Value: CircularMeterValue,\n})\n\nCircularMeter.displayName = 'CircularMeter'\nCircularMeterContent.displayName = 'CircularMeter.Content'\nCircularMeterLabel.displayName = 'CircularMeter.Label'\nCircularMeterTrack.displayName = 'CircularMeter.Track'\nCircularMeterValue.displayName = 'CircularMeter.Value'\n\nexport { type CircularMeterProps } from './CircularMeter'\nexport { type CircularMeterContentProps } from './CircularMeterContent'\nexport { type CircularMeterLabelProps } from './CircularMeterLabel'\nexport { type CircularMeterTrackProps } from './CircularMeterTrack'\nexport { type CircularMeterValueProps } from './CircularMeterValue'\n"],"names":["circularMeterStyles","cva","CircularMeterContext","createContext","ID_PREFIX","useCircularMeter","context","useContext","CIRCULAR_METER_SIZE_CONFIG","CircularMeter","className","value","max","min","sizeProp","intent","orientation","children","ref","others","labelId","setLabelId","useState","size","strokeWidth","radius","circumference","contextValue","useMemo","jsx","BaseMeter","cx","CircularMeterContent","useIntersectionAnimation","elementRef","onIntersect","options","threshold","rootMargin","hasTriggeredRef","useRef","callbackRef","useEffect","element","observer","entries","entry","CircularMeterTrackContext","useCircularMeterTrack","circularMeterTrackStyles","circularMeterIndicatorStyles","CircularMeterTrack","percentage","offset","intentClasses","svgRef","hasAnimated","setHasAnimated","jsxs","labelStyles","CircularMeterLabel","idProp","forwardedRef","internalID","useId","id","onLabelId","isInside","rootRef","useCallback","el","useMergeRefs","valueStyles","CircularMeterValue","Root"],"mappings":"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"}
@@ -0,0 +1,316 @@
1
+ import { jsx as o, jsxs as V } from "react/jsx-runtime";
2
+ import { Meter as C } from "@base-ui/react/meter";
3
+ import { cva as b, cx as k } from "class-variance-authority";
4
+ import { createContext as L, useContext as R, useState as T, useMemo as A, useRef as g, useEffect as w, useId as O, useCallback as $ } from "react";
5
+ import { useMergeRefs as D } from "@spark-ui/hooks/use-merge-refs";
6
+ const P = b(["focus-visible:u-outline gap-md flex items-center"], {
7
+ variants: {
8
+ orientation: {
9
+ vertical: ["default:flex-col"],
10
+ horizontal: ["default:flex-row"]
11
+ }
12
+ },
13
+ defaultVariants: {
14
+ orientation: "vertical"
15
+ }
16
+ }), E = L(null), _ = ":circular-meter", h = () => {
17
+ const r = R(E);
18
+ if (!r)
19
+ throw new Error("useCircularMeter must be used within a CircularMeter provider");
20
+ return r;
21
+ }, F = {
22
+ sm: { diameter: 24, strokeWidth: 4 },
23
+ md: { diameter: 64, strokeWidth: 8 },
24
+ lg: { diameter: 96, strokeWidth: 8 },
25
+ xl: { diameter: 128, strokeWidth: 8 }
26
+ }, S = ({
27
+ className: r,
28
+ value: t,
29
+ max: a = 100,
30
+ min: s = 0,
31
+ size: n = "lg",
32
+ intent: i = "support",
33
+ orientation: l = "vertical",
34
+ children: e,
35
+ ref: d,
36
+ ...c
37
+ }) => {
38
+ const [u, m] = T(), { diameter: f, strokeWidth: p } = F[n], x = f / 2 - p / 2, y = 2 * Math.PI * x, M = A(() => ({
39
+ value: t ?? 0,
40
+ max: a,
41
+ min: s,
42
+ intent: i,
43
+ onLabelId: m,
44
+ sizeProp: n,
45
+ orientation: l,
46
+ size: f,
47
+ radius: x,
48
+ circumference: y,
49
+ strokeWidth: p
50
+ }), [
51
+ a,
52
+ s,
53
+ t,
54
+ i,
55
+ m,
56
+ n,
57
+ l,
58
+ f,
59
+ x,
60
+ y,
61
+ p
62
+ ]);
63
+ return /* @__PURE__ */ o(E.Provider, { value: M, children: /* @__PURE__ */ o(
64
+ C.Root,
65
+ {
66
+ "data-spark-component": "circular-meter",
67
+ ref: d,
68
+ className: k(P({ orientation: l }), r),
69
+ style: c.style,
70
+ value: t,
71
+ max: a,
72
+ min: s,
73
+ "aria-labelledby": u,
74
+ ...c,
75
+ children: e
76
+ }
77
+ ) });
78
+ };
79
+ S.displayName = "CircularMeter";
80
+ const v = ({
81
+ className: r,
82
+ children: t,
83
+ ...a
84
+ }) => {
85
+ const { orientation: s } = h();
86
+ return /* @__PURE__ */ o(
87
+ "div",
88
+ {
89
+ "data-spark-component": "circular-meter-content",
90
+ className: k(
91
+ "gap-xs flex default:flex-col",
92
+ s === "vertical" && "default:text-center",
93
+ r
94
+ ),
95
+ ...a,
96
+ children: t
97
+ }
98
+ );
99
+ };
100
+ v.displayName = "CircularMeter.Content";
101
+ function q(r, t, a = {}) {
102
+ const { threshold: s = 0.1, rootMargin: n } = a, i = g(!1), l = g(t);
103
+ return w(() => {
104
+ l.current = t;
105
+ }, [t]), w(() => {
106
+ const e = r.current;
107
+ if (!e || i.current) return;
108
+ const d = new IntersectionObserver(
109
+ (c) => {
110
+ c.forEach((u) => {
111
+ u.isIntersecting && !i.current && requestAnimationFrame(() => {
112
+ i.current || (i.current = !0, l.current(), d.disconnect());
113
+ });
114
+ });
115
+ },
116
+ {
117
+ threshold: s,
118
+ rootMargin: n
119
+ }
120
+ );
121
+ return d.observe(e), () => {
122
+ d.disconnect();
123
+ };
124
+ }, [r, s, n]), i.current;
125
+ }
126
+ const W = L(!1), j = () => R(W), B = b([], {
127
+ variants: {
128
+ intent: {
129
+ main: ["text-main/dim-4"],
130
+ support: ["text-support/dim-4"],
131
+ success: ["text-success/dim-4"],
132
+ alert: ["text-alert/dim-4"],
133
+ danger: ["text-error/dim-4"],
134
+ info: ["text-info/dim-4"]
135
+ }
136
+ }
137
+ }), G = b([], {
138
+ variants: {
139
+ intent: {
140
+ main: ["text-main"],
141
+ support: ["text-support"],
142
+ success: ["text-success"],
143
+ alert: ["text-alert"],
144
+ danger: ["text-error"],
145
+ info: ["text-info"]
146
+ }
147
+ }
148
+ }), z = ({ className: r, children: t, ...a }) => {
149
+ const { value: s, max: n, min: i, intent: l, size: e, radius: d, circumference: c, strokeWidth: u } = h(), m = (s - i) / (n - i) * 100, f = c - m / 100 * c, p = G({ intent: l }), x = g(null), [y, M] = T(!1);
150
+ return q(x, () => {
151
+ M(!0);
152
+ }), /* @__PURE__ */ o(C.Track, { "data-spark-component": "circular-meter-track", className: r, ...a, children: /* @__PURE__ */ o(W.Provider, { value: !0, children: /* @__PURE__ */ V("svg", { ref: x, width: e, height: e, viewBox: `0 0 ${e} ${e}`, children: [
153
+ /* @__PURE__ */ V("g", { style: { transform: "rotate(-90deg)", transformOrigin: "center" }, children: [
154
+ /* @__PURE__ */ o(
155
+ "circle",
156
+ {
157
+ cx: e / 2,
158
+ cy: e / 2,
159
+ r: d,
160
+ fill: "none",
161
+ stroke: "currentColor",
162
+ strokeWidth: u,
163
+ className: B({ intent: l })
164
+ }
165
+ ),
166
+ /* @__PURE__ */ o(
167
+ "circle",
168
+ {
169
+ "data-spark-component": "circular-meter-indicator",
170
+ cx: e / 2,
171
+ cy: e / 2,
172
+ r: d,
173
+ fill: "none",
174
+ stroke: "currentColor",
175
+ strokeWidth: u,
176
+ strokeLinecap: "round",
177
+ className: k(
178
+ p,
179
+ "ease-standard transition-[stroke-dashoffset] duration-700",
180
+ "motion-reduce:transition-none"
181
+ ),
182
+ style: {
183
+ strokeDasharray: c,
184
+ // Start at circumference (0% filled) for initial animation, then use offset for subsequent changes
185
+ strokeDashoffset: y ? f : c
186
+ }
187
+ }
188
+ )
189
+ ] }),
190
+ t && /* @__PURE__ */ o("foreignObject", { x: 8, y: 8, width: e - 16, height: e - 16, children: /* @__PURE__ */ o(
191
+ "div",
192
+ {
193
+ className: "p-md flex h-full w-full flex-col items-center justify-center rounded-full text-center",
194
+ style: { width: e - 16, height: e - 16 },
195
+ children: t
196
+ }
197
+ ) })
198
+ ] }) }) });
199
+ };
200
+ z.displayName = "CircularMeter.Track";
201
+ const H = b([], {
202
+ variants: {
203
+ size: {
204
+ sm: "",
205
+ md: "",
206
+ lg: "",
207
+ xl: ""
208
+ },
209
+ inside: {
210
+ true: ["default:text-on-surface/dim-1"],
211
+ false: ["default:text-on-surface"]
212
+ }
213
+ },
214
+ compoundVariants: [
215
+ // Inside the track
216
+ { size: "sm", inside: !0, class: "default:text-small" },
217
+ { size: "md", inside: !0, class: "default:text-small " },
218
+ { size: "lg", inside: !0, class: "default:text-caption" },
219
+ { size: "xl", inside: !0, class: "default:text-body-2" },
220
+ // Outside the track
221
+ { size: "sm", inside: !1, class: "default:text-body-1" },
222
+ { size: "md", inside: !1, class: "default:text-body-1" },
223
+ { size: "lg", inside: !1, class: "default:text-body-1" },
224
+ { size: "xl", inside: !1, class: "default:text-body-1" }
225
+ ],
226
+ defaultVariants: {
227
+ size: "lg",
228
+ inside: !0
229
+ }
230
+ }), N = ({
231
+ id: r,
232
+ children: t,
233
+ className: a,
234
+ ref: s,
235
+ ...n
236
+ }) => {
237
+ const i = `${_}-label-${O()}`, l = r || i, { onLabelId: e, sizeProp: d } = h(), c = j(), u = $(
238
+ (f) => {
239
+ e(f ? l : void 0);
240
+ },
241
+ [l, e]
242
+ ), m = D(s, u);
243
+ return /* @__PURE__ */ o(
244
+ C.Label,
245
+ {
246
+ "data-spark-component": "circular-meter-label",
247
+ id: l,
248
+ className: H({ size: d, inside: c, className: a }),
249
+ ref: m,
250
+ ...n,
251
+ children: t
252
+ }
253
+ );
254
+ };
255
+ N.displayName = "CircularMeter.Label";
256
+ const U = b(["default:text-on-surface default:font-bold"], {
257
+ variants: {
258
+ size: {
259
+ sm: "",
260
+ md: "",
261
+ lg: "",
262
+ xl: ""
263
+ },
264
+ inside: {
265
+ true: [],
266
+ false: []
267
+ }
268
+ },
269
+ compoundVariants: [
270
+ // Inside the track
271
+ { size: "sm", inside: !0, class: "default:text-body-2 default:font-bold" },
272
+ { size: "md", inside: !0, class: "default:text-body-2 default:font-bold" },
273
+ { size: "lg", inside: !0, class: "default:text-body-1 default:font-bold" },
274
+ { size: "xl", inside: !0, class: "default:text-display-3" },
275
+ // Outside the track
276
+ { size: "sm", inside: !1, class: "default:text-body-1 default:font-bold" },
277
+ { size: "md", inside: !1, class: "default:text-headline-2" },
278
+ { size: "lg", inside: !1, class: "default:text-headline-2" },
279
+ { size: "xl", inside: !1, class: "default:text-display-3" }
280
+ ],
281
+ defaultVariants: {
282
+ size: "lg",
283
+ inside: !0
284
+ }
285
+ }), I = ({
286
+ className: r,
287
+ children: t,
288
+ ...a
289
+ }) => {
290
+ const { sizeProp: s } = h(), n = j();
291
+ return /* @__PURE__ */ o(
292
+ C.Value,
293
+ {
294
+ "data-spark-component": "circular-meter-value",
295
+ className: U({ size: s, inside: n, className: r }),
296
+ ...a,
297
+ children: t
298
+ }
299
+ );
300
+ };
301
+ I.displayName = "CircularMeter.Value";
302
+ const X = Object.assign(S, {
303
+ Content: v,
304
+ Label: N,
305
+ Track: z,
306
+ Value: I
307
+ });
308
+ X.displayName = "CircularMeter";
309
+ v.displayName = "CircularMeter.Content";
310
+ N.displayName = "CircularMeter.Label";
311
+ z.displayName = "CircularMeter.Track";
312
+ I.displayName = "CircularMeter.Value";
313
+ export {
314
+ X as CircularMeter
315
+ };
316
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../src/circular-meter/CircularMeter.styles.ts","../../src/circular-meter/CircularMeterContext.tsx","../../src/circular-meter/CircularMeter.tsx","../../src/circular-meter/CircularMeterContent.tsx","../../src/circular-meter/useIntersectionAnimation.ts","../../src/circular-meter/CircularMeterTrack.tsx","../../src/circular-meter/CircularMeterLabel.tsx","../../src/circular-meter/CircularMeterValue.tsx","../../src/circular-meter/index.ts"],"sourcesContent":["import { cva, VariantProps } from 'class-variance-authority'\n\nexport const circularMeterStyles = cva(['focus-visible:u-outline gap-md flex items-center'], {\n variants: {\n orientation: {\n vertical: ['default:flex-col'],\n horizontal: ['default:flex-row'],\n },\n },\n defaultVariants: {\n orientation: 'vertical',\n },\n})\n\nexport type CircularMeterStylesProps = VariantProps<typeof circularMeterStyles>\n","import { createContext, useContext } from 'react'\n\nimport { MeterIndicatorStylesProps } from '../meter/MeterTrack.styles'\nimport { CircularMeterSize } from './CircularMeter'\n\nexport interface CircularMeterContextValue {\n value: number\n max: number\n min: number\n intent: MeterIndicatorStylesProps['intent']\n onLabelId: (id?: string) => void\n /**\n * Size variant of the circular meter.\n */\n sizeProp: CircularMeterSize\n /**\n * Orientation of the circular meter.\n */\n orientation: 'vertical' | 'horizontal'\n /**\n * Diameter of the SVG circle in pixels.\n */\n size: number\n /**\n * Radius of the SVG circle in pixels.\n */\n radius: number\n /**\n * Circumference of the SVG circle in pixels.\n */\n circumference: number\n /**\n * Stroke width of the SVG circle in pixels.\n */\n strokeWidth: number\n}\n\nexport const CircularMeterContext = createContext<CircularMeterContextValue | null>(null)\n\nexport const ID_PREFIX = ':circular-meter'\n\nexport const useCircularMeter = () => {\n const context = useContext(CircularMeterContext)\n\n if (!context) {\n throw new Error('useCircularMeter must be used within a CircularMeter provider')\n }\n\n return context\n}\n","import { Meter as BaseMeter } from '@base-ui/react/meter'\nimport { cx } from 'class-variance-authority'\nimport { ComponentProps, PropsWithChildren, Ref, useMemo, useState } from 'react'\n\nimport { MeterIndicatorStylesProps } from '../meter/MeterTrack.styles'\nimport { circularMeterStyles, CircularMeterStylesProps } from './CircularMeter.styles'\nimport { CircularMeterContext } from './CircularMeterContext'\n\nexport type CircularMeterSize = 'sm' | 'md' | 'lg' | 'xl'\n\nconst CIRCULAR_METER_SIZE_CONFIG: Record<\n CircularMeterSize,\n { diameter: number; strokeWidth: number }\n> = {\n sm: { diameter: 24, strokeWidth: 4 },\n md: { diameter: 64, strokeWidth: 8 },\n lg: { diameter: 96, strokeWidth: 8 },\n xl: { diameter: 128, strokeWidth: 8 },\n}\n\nexport interface CircularMeterProps\n extends Omit<ComponentProps<typeof BaseMeter.Root>, 'render'>,\n Pick<MeterIndicatorStylesProps, 'intent'>,\n CircularMeterStylesProps {\n /**\n * Size of the circle.\n *\n * - `sm`: 24px diameter, 4px stroke width\n * - `md`: 64px diameter, 8px stroke width\n * - `lg`: 96px diameter, 8px stroke width\n * - `xl`: 128px diameter, 8px stroke width\n *\n * Defaults to `md`.\n */\n size?: CircularMeterSize\n /**\n * Change the default rendered element for the one passed as a child, merging their props and behavior.\n */\n asChild?: boolean\n ref?: Ref<HTMLDivElement>\n /**\n * Orientation of the circular meter.\n *\n * - `vertical`: Elements are stacked vertically (default)\n * - `horizontal`: Elements are arranged horizontally\n */\n orientation?: 'vertical' | 'horizontal'\n}\n\nexport const CircularMeter = ({\n className,\n value,\n max = 100,\n min = 0,\n size: sizeProp = 'lg',\n intent = 'support',\n orientation = 'vertical',\n children,\n ref,\n ...others\n}: PropsWithChildren<CircularMeterProps>) => {\n const [labelId, setLabelId] = useState<string>()\n\n const { diameter: size, strokeWidth } = CIRCULAR_METER_SIZE_CONFIG[sizeProp]\n\n const radius = size / 2 - strokeWidth / 2\n const circumference = 2 * Math.PI * radius\n\n const contextValue = useMemo(() => {\n return {\n value: value ?? 0,\n max,\n min,\n intent,\n onLabelId: setLabelId,\n sizeProp,\n orientation: orientation,\n size,\n radius,\n circumference,\n strokeWidth,\n }\n }, [\n max,\n min,\n value,\n intent,\n setLabelId,\n sizeProp,\n orientation,\n size,\n radius,\n circumference,\n strokeWidth,\n ])\n\n return (\n <CircularMeterContext.Provider value={contextValue}>\n <BaseMeter.Root\n data-spark-component=\"circular-meter\"\n ref={ref}\n className={cx(circularMeterStyles({ orientation }), className)}\n style={others.style}\n value={value}\n max={max}\n min={min}\n aria-labelledby={labelId}\n {...others}\n >\n {children}\n </BaseMeter.Root>\n </CircularMeterContext.Provider>\n )\n}\n\nCircularMeter.displayName = 'CircularMeter'\n","import { cx } from 'class-variance-authority'\nimport { ComponentProps, PropsWithChildren } from 'react'\n\nimport { useCircularMeter } from './CircularMeterContext'\n\nexport type CircularMeterContentProps = ComponentProps<'div'> & PropsWithChildren\n\nexport const CircularMeterContent = ({\n className,\n children,\n ...others\n}: CircularMeterContentProps) => {\n const { orientation } = useCircularMeter()\n\n return (\n <div\n data-spark-component=\"circular-meter-content\"\n className={cx(\n 'gap-xs flex default:flex-col',\n orientation === 'vertical' && 'default:text-center',\n className\n )}\n {...others}\n >\n {children}\n </div>\n )\n}\n\nCircularMeterContent.displayName = 'CircularMeter.Content'\n","import { RefObject, useEffect, useRef } from 'react'\n\nexport interface UseIntersectionAnimationOptions {\n /**\n * The threshold at which the callback should be triggered.\n * A value of 0 means as soon as any part of the element is visible.\n * A value of 1 means the entire element must be visible.\n * @default 0.1\n */\n threshold?: number\n /**\n * The root margin for the Intersection Observer.\n * This can be used to trigger the animation before the element enters the viewport.\n * @default undefined\n */\n rootMargin?: string\n}\n\n/**\n * Hook to trigger an animation callback when an element enters the viewport.\n * The callback is only triggered once, when the element first becomes visible.\n *\n * @param elementRef - Reference to the element to observe\n * @param onIntersect - Callback to execute when the element enters the viewport\n * @param options - Configuration options for the Intersection Observer\n * @returns Whether the animation has been triggered\n */\nexport function useIntersectionAnimation(\n elementRef: RefObject<Element | null>,\n onIntersect: () => void,\n options: UseIntersectionAnimationOptions = {}\n): boolean {\n const { threshold = 0.1, rootMargin } = options\n const hasTriggeredRef = useRef(false)\n const callbackRef = useRef(onIntersect)\n\n // Keep callback ref up to date\n useEffect(() => {\n callbackRef.current = onIntersect\n }, [onIntersect])\n\n useEffect(() => {\n const element = elementRef.current\n if (!element || hasTriggeredRef.current) return\n\n const observer = new IntersectionObserver(\n entries => {\n entries.forEach(entry => {\n if (entry.isIntersecting && !hasTriggeredRef.current) {\n // Use requestAnimationFrame to ensure the callback runs at the right time\n requestAnimationFrame(() => {\n if (!hasTriggeredRef.current) {\n hasTriggeredRef.current = true\n callbackRef.current()\n // Disconnect observer after callback is triggered (only trigger once)\n observer.disconnect()\n }\n })\n }\n })\n },\n {\n threshold,\n rootMargin,\n }\n )\n\n observer.observe(element)\n\n return () => {\n observer.disconnect()\n }\n }, [elementRef, threshold, rootMargin])\n\n return hasTriggeredRef.current\n}\n","import { Meter as BaseMeter } from '@base-ui/react/meter'\nimport { cva, cx } from 'class-variance-authority'\nimport {\n ComponentProps,\n createContext,\n PropsWithChildren,\n useContext,\n useRef,\n useState,\n} from 'react'\n\nimport { useCircularMeter } from './CircularMeterContext'\nimport { useIntersectionAnimation } from './useIntersectionAnimation'\n\nconst CircularMeterTrackContext = createContext<boolean>(false)\n\nexport const useCircularMeterTrack = () => {\n return useContext(CircularMeterTrackContext)\n}\n\nexport type CircularMeterTrackProps = Omit<ComponentProps<typeof BaseMeter.Track>, 'render'> &\n PropsWithChildren\n\nconst circularMeterTrackStyles = cva([], {\n variants: {\n intent: {\n main: ['text-main/dim-4'],\n support: ['text-support/dim-4'],\n success: ['text-success/dim-4'],\n alert: ['text-alert/dim-4'],\n danger: ['text-error/dim-4'],\n info: ['text-info/dim-4'],\n },\n },\n})\n\nconst circularMeterIndicatorStyles = cva([], {\n variants: {\n intent: {\n main: ['text-main'],\n support: ['text-support'],\n success: ['text-success'],\n alert: ['text-alert'],\n danger: ['text-error'],\n info: ['text-info'],\n },\n },\n})\n\nexport const CircularMeterTrack = ({ className, children, ...others }: CircularMeterTrackProps) => {\n const { value, max, min, intent, size, radius, circumference, strokeWidth } = useCircularMeter()\n const percentage = ((value - min) / (max - min)) * 100\n const offset = circumference - (percentage / 100) * circumference\n\n const intentClasses = circularMeterIndicatorStyles({ intent })\n const svgRef = useRef<SVGSVGElement>(null)\n const [hasAnimated, setHasAnimated] = useState(false)\n\n // Trigger animation when component enters viewport\n useIntersectionAnimation(svgRef, () => {\n setHasAnimated(true)\n })\n\n return (\n <BaseMeter.Track data-spark-component=\"circular-meter-track\" className={className} {...others}>\n <CircularMeterTrackContext.Provider value={true}>\n <svg ref={svgRef} width={size} height={size} viewBox={`0 0 ${size} ${size}`}>\n <g style={{ transform: 'rotate(-90deg)', transformOrigin: 'center' }}>\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n className={circularMeterTrackStyles({ intent })}\n />\n <circle\n data-spark-component=\"circular-meter-indicator\"\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n className={cx(\n intentClasses,\n 'ease-standard transition-[stroke-dashoffset] duration-700',\n 'motion-reduce:transition-none'\n )}\n style={\n {\n strokeDasharray: circumference,\n // Start at circumference (0% filled) for initial animation, then use offset for subsequent changes\n strokeDashoffset: hasAnimated ? offset : circumference,\n } as React.CSSProperties\n }\n />\n </g>\n {children && (\n <foreignObject x={8} y={8} width={size - 16} height={size - 16}>\n <div\n className=\"p-md flex h-full w-full flex-col items-center justify-center rounded-full text-center\"\n style={{ width: size - 16, height: size - 16 }}\n >\n {children}\n </div>\n </foreignObject>\n )}\n </svg>\n </CircularMeterTrackContext.Provider>\n </BaseMeter.Track>\n )\n}\n\nCircularMeterTrack.displayName = 'CircularMeter.Track'\n","import { Meter as BaseMeter } from '@base-ui/react/meter'\nimport { useMergeRefs } from '@spark-ui/hooks/use-merge-refs'\nimport { cva } from 'class-variance-authority'\nimport { ComponentProps, useCallback, useId } from 'react'\n\nimport { ID_PREFIX, useCircularMeter } from './CircularMeterContext'\nimport { useCircularMeterTrack } from './CircularMeterTrack'\n\nexport type CircularMeterLabelProps = Omit<ComponentProps<typeof BaseMeter.Label>, 'render'>\n\nconst labelStyles = cva([], {\n variants: {\n size: {\n sm: '',\n md: '',\n lg: '',\n xl: '',\n },\n inside: {\n true: ['default:text-on-surface/dim-1'],\n false: ['default:text-on-surface'],\n },\n },\n compoundVariants: [\n // Inside the track\n { size: 'sm', inside: true, class: 'default:text-small' },\n { size: 'md', inside: true, class: 'default:text-small ' },\n { size: 'lg', inside: true, class: 'default:text-caption' },\n { size: 'xl', inside: true, class: 'default:text-body-2' },\n // Outside the track\n { size: 'sm', inside: false, class: 'default:text-body-1' },\n { size: 'md', inside: false, class: 'default:text-body-1' },\n { size: 'lg', inside: false, class: 'default:text-body-1' },\n { size: 'xl', inside: false, class: 'default:text-body-1' },\n ],\n defaultVariants: {\n size: 'lg',\n inside: true,\n },\n})\n\nexport const CircularMeterLabel = ({\n id: idProp,\n children,\n className,\n ref: forwardedRef,\n ...others\n}: CircularMeterLabelProps) => {\n const internalID = `${ID_PREFIX}-label-${useId()}`\n const id = idProp || internalID\n\n const { onLabelId, sizeProp } = useCircularMeter()\n const isInside = useCircularMeterTrack()\n const rootRef = useCallback(\n (el: HTMLSpanElement) => {\n onLabelId(el ? id : undefined)\n },\n [id, onLabelId]\n )\n const ref = useMergeRefs(forwardedRef, rootRef)\n\n return (\n <BaseMeter.Label\n data-spark-component=\"circular-meter-label\"\n id={id}\n className={labelStyles({ size: sizeProp, inside: isInside, className })}\n ref={ref}\n {...others}\n >\n {children}\n </BaseMeter.Label>\n )\n}\n\nCircularMeterLabel.displayName = 'CircularMeter.Label'\n","import { Meter as BaseMeter } from '@base-ui/react/meter'\nimport { cva } from 'class-variance-authority'\nimport { ComponentProps, PropsWithChildren } from 'react'\n\nimport { useCircularMeter } from './CircularMeterContext'\nimport { useCircularMeterTrack } from './CircularMeterTrack'\n\nexport type CircularMeterValueProps = Omit<ComponentProps<typeof BaseMeter.Value>, 'render'>\n\nconst valueStyles = cva(['default:text-on-surface default:font-bold'], {\n variants: {\n size: {\n sm: '',\n md: '',\n lg: '',\n xl: '',\n },\n inside: {\n true: [],\n false: [],\n },\n },\n compoundVariants: [\n // Inside the track\n { size: 'sm', inside: true, class: 'default:text-body-2 default:font-bold' },\n { size: 'md', inside: true, class: 'default:text-body-2 default:font-bold' },\n { size: 'lg', inside: true, class: 'default:text-body-1 default:font-bold' },\n { size: 'xl', inside: true, class: 'default:text-display-3' },\n // Outside the track\n { size: 'sm', inside: false, class: 'default:text-body-1 default:font-bold' },\n { size: 'md', inside: false, class: 'default:text-headline-2' },\n { size: 'lg', inside: false, class: 'default:text-headline-2' },\n { size: 'xl', inside: false, class: 'default:text-display-3' },\n ],\n defaultVariants: {\n size: 'lg',\n inside: true,\n },\n})\n\nexport const CircularMeterValue = ({\n className,\n children,\n ...others\n}: PropsWithChildren<CircularMeterValueProps>) => {\n const { sizeProp } = useCircularMeter()\n const isInside = useCircularMeterTrack()\n\n return (\n <BaseMeter.Value\n data-spark-component=\"circular-meter-value\"\n className={valueStyles({ size: sizeProp, inside: isInside, className })}\n {...others}\n >\n {children}\n </BaseMeter.Value>\n )\n}\n\nCircularMeterValue.displayName = 'CircularMeter.Value'\n","import { CircularMeter as Root } from './CircularMeter'\nimport { CircularMeterContent } from './CircularMeterContent'\nimport { CircularMeterLabel } from './CircularMeterLabel'\nimport { CircularMeterTrack } from './CircularMeterTrack'\nimport { CircularMeterValue } from './CircularMeterValue'\n\nexport const CircularMeter: typeof Root & {\n Content: typeof CircularMeterContent\n Label: typeof CircularMeterLabel\n Track: typeof CircularMeterTrack\n Value: typeof CircularMeterValue\n} = Object.assign(Root, {\n Content: CircularMeterContent,\n Label: CircularMeterLabel,\n Track: CircularMeterTrack,\n Value: CircularMeterValue,\n})\n\nCircularMeter.displayName = 'CircularMeter'\nCircularMeterContent.displayName = 'CircularMeter.Content'\nCircularMeterLabel.displayName = 'CircularMeter.Label'\nCircularMeterTrack.displayName = 'CircularMeter.Track'\nCircularMeterValue.displayName = 'CircularMeter.Value'\n\nexport { type CircularMeterProps } from './CircularMeter'\nexport { type CircularMeterContentProps } from './CircularMeterContent'\nexport { type CircularMeterLabelProps } from './CircularMeterLabel'\nexport { type CircularMeterTrackProps } from './CircularMeterTrack'\nexport { type CircularMeterValueProps } from './CircularMeterValue'\n"],"names":["circularMeterStyles","cva","CircularMeterContext","createContext","ID_PREFIX","useCircularMeter","context","useContext","CIRCULAR_METER_SIZE_CONFIG","CircularMeter","className","value","max","min","sizeProp","intent","orientation","children","ref","others","labelId","setLabelId","useState","size","strokeWidth","radius","circumference","contextValue","useMemo","jsx","BaseMeter","cx","CircularMeterContent","useIntersectionAnimation","elementRef","onIntersect","options","threshold","rootMargin","hasTriggeredRef","useRef","callbackRef","useEffect","element","observer","entries","entry","CircularMeterTrackContext","useCircularMeterTrack","circularMeterTrackStyles","circularMeterIndicatorStyles","CircularMeterTrack","percentage","offset","intentClasses","svgRef","hasAnimated","setHasAnimated","jsxs","labelStyles","CircularMeterLabel","idProp","forwardedRef","internalID","useId","id","onLabelId","isInside","rootRef","useCallback","el","useMergeRefs","valueStyles","CircularMeterValue","Root"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { RefObject } from 'react';
2
+ export interface UseIntersectionAnimationOptions {
3
+ /**
4
+ * The threshold at which the callback should be triggered.
5
+ * A value of 0 means as soon as any part of the element is visible.
6
+ * A value of 1 means the entire element must be visible.
7
+ * @default 0.1
8
+ */
9
+ threshold?: number;
10
+ /**
11
+ * The root margin for the Intersection Observer.
12
+ * This can be used to trigger the animation before the element enters the viewport.
13
+ * @default undefined
14
+ */
15
+ rootMargin?: string;
16
+ }
17
+ /**
18
+ * Hook to trigger an animation callback when an element enters the viewport.
19
+ * The callback is only triggered once, when the element first becomes visible.
20
+ *
21
+ * @param elementRef - Reference to the element to observe
22
+ * @param onIntersect - Callback to execute when the element enters the viewport
23
+ * @param options - Configuration options for the Intersection Observer
24
+ * @returns Whether the animation has been triggered
25
+ */
26
+ export declare function useIntersectionAnimation(elementRef: RefObject<Element | null>, onIntersect: () => void, options?: UseIntersectionAnimationOptions): boolean;