package.dist.index.mjs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of progress Show documentation
Show all versions of progress Show documentation
Core logic for the progress widget implemented as a state machine
The newest version!
import { createAnatomy } from '@zag-js/anatomy';
import { createScope } from '@zag-js/dom-query';
import { createMachine } from '@zag-js/core';
import { createSplitProps, compact, isNumber } from '@zag-js/utils';
import { createProps } from '@zag-js/types';
// src/progress.anatomy.ts
var anatomy = createAnatomy("progress").parts(
"root",
"label",
"track",
"range",
"valueText",
"view",
"circle",
"circleTrack",
"circleRange"
);
var parts = anatomy.build();
var dom = createScope({
getRootId: (ctx) => ctx.ids?.root ?? `progress-${ctx.id}`,
getTrackId: (ctx) => ctx.ids?.track ?? `progress-${ctx.id}-track`,
getLabelId: (ctx) => ctx.ids?.label ?? `progress-${ctx.id}-label`,
getCircleId: (ctx) => ctx.ids?.circle ?? `progress-${ctx.id}-circle`
});
// src/progress.connect.ts
function connect(state, send, normalize) {
const percent = state.context.percent;
const percentAsString = state.context.isIndeterminate ? "" : `${percent}%`;
const max = state.context.max;
const min = state.context.min;
const orientation = state.context.orientation;
const translations = state.context.translations;
const indeterminate = state.context.isIndeterminate;
const value = state.context.value;
const valueAsString = translations.value({ value, max, percent, min });
const progressState = getProgressState(value, max);
const progressbarProps = {
role: "progressbar",
"aria-label": valueAsString,
"data-max": max,
"aria-valuemin": min,
"aria-valuemax": max,
"aria-valuenow": value ?? void 0,
"data-orientation": orientation,
"data-state": progressState
};
const circleProps = getCircleProps(state.context);
return {
value,
valueAsString,
min,
max,
percent,
percentAsString,
indeterminate,
setValue(value2) {
send({ type: "VALUE.SET", value: value2 });
},
setToMax() {
send({ type: "VALUE.SET", value: max });
},
setToMin() {
send({ type: "VALUE.SET", value: min });
},
getRootProps() {
return normalize.element({
dir: state.context.dir,
...parts.root.attrs,
id: dom.getRootId(state.context),
"data-max": max,
"data-value": value ?? void 0,
"data-state": progressState,
"data-orientation": orientation,
style: {
"--percent": indeterminate ? void 0 : percent
}
});
},
getLabelProps() {
return normalize.element({
dir: state.context.dir,
id: dom.getLabelId(state.context),
...parts.label.attrs,
"data-orientation": orientation
});
},
getValueTextProps() {
return normalize.element({
dir: state.context.dir,
"aria-live": "polite",
...parts.valueText.attrs
});
},
getTrackProps() {
return normalize.element({
dir: state.context.dir,
id: dom.getTrackId(state.context),
...parts.track.attrs,
...progressbarProps
});
},
getRangeProps() {
return normalize.element({
dir: state.context.dir,
...parts.range.attrs,
"data-orientation": orientation,
"data-state": progressState,
style: {
[state.context.isHorizontal ? "width" : "height"]: indeterminate ? void 0 : `${percent}%`
}
});
},
getCircleProps() {
return normalize.element({
dir: state.context.dir,
id: dom.getCircleId(state.context),
...parts.circle.attrs,
...progressbarProps,
...circleProps.root
});
},
getCircleTrackProps() {
return normalize.element({
dir: state.context.dir,
"data-orientation": orientation,
...parts.circleTrack.attrs,
...circleProps.track
});
},
getCircleRangeProps() {
return normalize.element({
dir: state.context.dir,
...parts.circleRange.attrs,
...circleProps.range,
"data-state": progressState
});
},
getViewProps(props2) {
return normalize.element({
dir: state.context.dir,
...parts.view.attrs,
"data-state": props2.state,
hidden: props2.state !== progressState
});
}
};
}
function getProgressState(value, maxValue) {
return value == null ? "indeterminate" : value === maxValue ? "complete" : "loading";
}
function getCircleProps(ctx) {
const circleProps = {
style: {
"--radius": "calc(var(--size) / 2 - var(--thickness) / 2)",
cx: "calc(var(--size) / 2)",
cy: "calc(var(--size) / 2)",
r: "var(--radius)",
fill: "transparent",
strokeWidth: "var(--thickness)"
}
};
return {
root: {
style: {
width: "var(--size)",
height: "var(--size)"
}
},
track: circleProps,
range: {
opacity: ctx.value === 0 ? 0 : void 0,
style: {
...circleProps.style,
"--percent": ctx.percent,
"--circumference": `calc(2 * 3.14159 * var(--radius))`,
"--offset": `calc(var(--circumference) * (100 - var(--percent)) / 100)`,
strokeDashoffset: `calc(var(--circumference) * ((100 - var(--percent)) / 100))`,
strokeDasharray: ctx.isIndeterminate ? void 0 : `var(--circumference)`,
transformOrigin: "center",
transform: "rotate(-90deg)"
}
}
};
}
function midValue(min, max) {
return min + (max - min) / 2;
}
function machine(userContext) {
const ctx = compact(userContext);
return createMachine(
{
id: "progress",
initial: "idle",
context: {
max: ctx.max ?? 100,
min: ctx.min ?? 0,
value: midValue(ctx.min ?? 0, ctx.max ?? 100),
orientation: "horizontal",
translations: {
value: ({ percent }) => percent === -1 ? "loading..." : `${percent} percent`,
...ctx.translations
},
...ctx
},
created: ["validateContext"],
computed: {
isIndeterminate: (ctx2) => ctx2.value === null,
percent(ctx2) {
if (!isNumber(ctx2.value)) return -1;
return Math.round((ctx2.value - ctx2.min) / (ctx2.max - ctx2.min) * 100);
},
isAtMax: (ctx2) => ctx2.value === ctx2.max,
isHorizontal: (ctx2) => ctx2.orientation === "horizontal",
isRtl: (ctx2) => ctx2.dir === "rtl"
},
states: {
idle: {
on: {
"VALUE.SET": {
actions: ["setValue"]
}
}
}
}
},
{
actions: {
setValue: (ctx2, evt) => {
ctx2.value = evt.value === null ? null : Math.max(0, Math.min(evt.value, ctx2.max));
},
validateContext: (ctx2) => {
if (ctx2.value == null) return;
if (!isValidNumber(ctx2.max)) {
throw new Error(`[progress] The max value passed \`${ctx2.max}\` is not a valid number`);
}
if (!isValidMax(ctx2.value, ctx2.max)) {
throw new Error(`[progress] The value passed \`${ctx2.value}\` exceeds the max value \`${ctx2.max}\``);
}
if (!isValidMin(ctx2.value, ctx2.min)) {
throw new Error(`[progress] The value passed \`${ctx2.value}\` exceeds the min value \`${ctx2.min}\``);
}
}
}
}
);
}
function isValidNumber(max) {
return isNumber(max) && !isNaN(max);
}
function isValidMax(value, max) {
return isValidNumber(value) && value <= max;
}
function isValidMin(value, min) {
return isValidNumber(value) && value >= min;
}
var props = createProps()([
"dir",
"getRootNode",
"id",
"ids",
"max",
"min",
"orientation",
"translations",
"value"
]);
var splitProps = createSplitProps(props);
export { anatomy, connect, machine, props, splitProps };
© 2015 - 2025 Weber Informatics LLC | Privacy Policy