@zag-js/radio-group 1.35.3 → 1.37.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.
@@ -180,6 +180,7 @@ function connect(service, normalize) {
180
180
  form: prop("form"),
181
181
  value: props.value,
182
182
  required: prop("required"),
183
+ "aria-labelledby": dom.getItemLabelId(scope, props.value),
183
184
  "aria-invalid": itemState.invalid || void 0,
184
185
  onClick(event) {
185
186
  if (readOnly) {
@@ -216,14 +217,18 @@ function connect(service, normalize) {
216
217
  },
217
218
  getIndicatorProps() {
218
219
  const rect = context.get("indicatorRect");
219
- const rectIsEmpty = rect == null || rect.width === 0 && rect.height === 0 && rect.x === 0 && rect.y === 0;
220
+ const animateIndicator = context.get("animateIndicator");
220
221
  return normalize.element({
221
222
  id: dom.getIndicatorId(scope),
222
223
  ...import_radio_group.parts.indicator.attrs,
223
224
  dir: prop("dir"),
224
- hidden: context.get("value") == null || rectIsEmpty,
225
+ hidden: context.get("value") == null || isRectEmpty(rect),
225
226
  "data-disabled": (0, import_dom_query.dataAttr)(groupDisabled),
226
227
  "data-orientation": prop("orientation"),
228
+ onTransitionEnd(event) {
229
+ if ((0, import_dom_query.getEventTarget)(event) !== event.currentTarget) return;
230
+ send({ type: "INDICATOR_TRANSITION_END" });
231
+ },
227
232
  style: {
228
233
  "--transition-property": "left, top, width, height",
229
234
  "--left": (0, import_utils.toPx)(rect?.x),
@@ -231,9 +236,9 @@ function connect(service, normalize) {
231
236
  "--width": (0, import_utils.toPx)(rect?.width),
232
237
  "--height": (0, import_utils.toPx)(rect?.height),
233
238
  position: "absolute",
234
- willChange: "var(--transition-property)",
235
- transitionProperty: "var(--transition-property)",
236
- transitionDuration: "var(--transition-duration, 150ms)",
239
+ willChange: animateIndicator ? "var(--transition-property)" : "auto",
240
+ transitionProperty: animateIndicator ? "var(--transition-property)" : "none",
241
+ transitionDuration: animateIndicator ? "var(--transition-duration, 150ms)" : "0ms",
237
242
  transitionTimingFunction: "var(--transition-timing-function)",
238
243
  [prop("orientation") === "horizontal" ? "left" : "top"]: prop("orientation") === "horizontal" ? "var(--left)" : "var(--top)"
239
244
  }
@@ -241,6 +246,7 @@ function connect(service, normalize) {
241
246
  }
242
247
  };
243
248
  }
249
+ var isRectEmpty = (rect) => rect == null || rect.width === 0 && rect.height === 0 && rect.x === 0 && rect.y === 0;
244
250
  // Annotate the CommonJS export names for ESM import in node:
245
251
  0 && (module.exports = {
246
252
  connect
@@ -1,5 +1,5 @@
1
1
  // src/radio-group.connect.ts
2
- import { dataAttr, isLeftClick, isSafari, visuallyHiddenStyle } from "@zag-js/dom-query";
2
+ import { dataAttr, getEventTarget, isLeftClick, isSafari, visuallyHiddenStyle } from "@zag-js/dom-query";
3
3
  import { isFocusVisible } from "@zag-js/focus-visible";
4
4
  import { toPx } from "@zag-js/utils";
5
5
  import { parts } from "./radio-group.anatomy.mjs";
@@ -146,6 +146,7 @@ function connect(service, normalize) {
146
146
  form: prop("form"),
147
147
  value: props.value,
148
148
  required: prop("required"),
149
+ "aria-labelledby": dom.getItemLabelId(scope, props.value),
149
150
  "aria-invalid": itemState.invalid || void 0,
150
151
  onClick(event) {
151
152
  if (readOnly) {
@@ -182,14 +183,18 @@ function connect(service, normalize) {
182
183
  },
183
184
  getIndicatorProps() {
184
185
  const rect = context.get("indicatorRect");
185
- const rectIsEmpty = rect == null || rect.width === 0 && rect.height === 0 && rect.x === 0 && rect.y === 0;
186
+ const animateIndicator = context.get("animateIndicator");
186
187
  return normalize.element({
187
188
  id: dom.getIndicatorId(scope),
188
189
  ...parts.indicator.attrs,
189
190
  dir: prop("dir"),
190
- hidden: context.get("value") == null || rectIsEmpty,
191
+ hidden: context.get("value") == null || isRectEmpty(rect),
191
192
  "data-disabled": dataAttr(groupDisabled),
192
193
  "data-orientation": prop("orientation"),
194
+ onTransitionEnd(event) {
195
+ if (getEventTarget(event) !== event.currentTarget) return;
196
+ send({ type: "INDICATOR_TRANSITION_END" });
197
+ },
193
198
  style: {
194
199
  "--transition-property": "left, top, width, height",
195
200
  "--left": toPx(rect?.x),
@@ -197,9 +202,9 @@ function connect(service, normalize) {
197
202
  "--width": toPx(rect?.width),
198
203
  "--height": toPx(rect?.height),
199
204
  position: "absolute",
200
- willChange: "var(--transition-property)",
201
- transitionProperty: "var(--transition-property)",
202
- transitionDuration: "var(--transition-duration, 150ms)",
205
+ willChange: animateIndicator ? "var(--transition-property)" : "auto",
206
+ transitionProperty: animateIndicator ? "var(--transition-property)" : "none",
207
+ transitionDuration: animateIndicator ? "var(--transition-duration, 150ms)" : "0ms",
203
208
  transitionTimingFunction: "var(--transition-timing-function)",
204
209
  [prop("orientation") === "horizontal" ? "left" : "top"]: prop("orientation") === "horizontal" ? "var(--left)" : "var(--top)"
205
210
  }
@@ -207,6 +212,7 @@ function connect(service, normalize) {
207
212
  }
208
213
  };
209
214
  }
215
+ var isRectEmpty = (rect) => rect == null || rect.width === 0 && rect.height === 0 && rect.x === 0 && rect.y === 0;
210
216
  export {
211
217
  connect
212
218
  };
@@ -72,6 +72,9 @@ var machine = (0, import_core.createMachine)({
72
72
  indicatorRect: bindable(() => ({
73
73
  defaultValue: null
74
74
  })),
75
+ animateIndicator: bindable(() => ({
76
+ defaultValue: false
77
+ })),
75
78
  fieldsetDisabled: bindable(() => ({
76
79
  defaultValue: false
77
80
  })),
@@ -83,18 +86,19 @@ var machine = (0, import_core.createMachine)({
83
86
  refs() {
84
87
  return {
85
88
  indicatorCleanup: null,
86
- focusVisibleValue: null
89
+ focusVisibleValue: null,
90
+ prevValue: null
87
91
  };
88
92
  },
89
93
  computed: {
90
94
  isDisabled: ({ prop, context }) => !!prop("disabled") || context.get("fieldsetDisabled")
91
95
  },
92
- entry: ["syncIndicatorRect", "syncSsr"],
96
+ entry: ["syncPrevValue", "syncIndicatorRect", "syncSsr"],
93
97
  exit: ["cleanupObserver"],
94
98
  effects: ["trackFormControlState", "trackFocusVisible"],
95
99
  watch({ track, action, context }) {
96
100
  track([() => context.get("value")], () => {
97
- action(["syncIndicatorRect", "syncInputElements"]);
101
+ action(["syncIndicatorAnimation", "syncIndicatorRect", "syncInputElements"]);
98
102
  });
99
103
  },
100
104
  on: {
@@ -115,6 +119,9 @@ var machine = (0, import_core.createMachine)({
115
119
  },
116
120
  SET_FOCUSED: {
117
121
  actions: ["setFocused"]
122
+ },
123
+ INDICATOR_TRANSITION_END: {
124
+ actions: ["clearIndicatorAnimation"]
118
125
  }
119
126
  },
120
127
  states: {
@@ -154,6 +161,19 @@ var machine = (0, import_core.createMachine)({
154
161
  const focusVisibleValue = event.value != null && event.focusVisible ? event.value : null;
155
162
  context.set("focusVisibleValue", focusVisibleValue);
156
163
  },
164
+ syncPrevValue({ context, refs }) {
165
+ refs.set("prevValue", context.get("value"));
166
+ },
167
+ syncIndicatorAnimation({ context, refs }) {
168
+ const prevValue = refs.get("prevValue");
169
+ const nextValue = context.get("value");
170
+ const animate = prevValue != null && nextValue != null && prevValue !== nextValue;
171
+ context.set("animateIndicator", animate);
172
+ refs.set("prevValue", nextValue);
173
+ },
174
+ clearIndicatorAnimation({ context }) {
175
+ context.set("animateIndicator", false);
176
+ },
157
177
  syncInputElements({ context, scope }) {
158
178
  const inputs = dom.getInputEls(scope);
159
179
  inputs.forEach((input) => {
@@ -38,6 +38,9 @@ var machine = createMachine({
38
38
  indicatorRect: bindable(() => ({
39
39
  defaultValue: null
40
40
  })),
41
+ animateIndicator: bindable(() => ({
42
+ defaultValue: false
43
+ })),
41
44
  fieldsetDisabled: bindable(() => ({
42
45
  defaultValue: false
43
46
  })),
@@ -49,18 +52,19 @@ var machine = createMachine({
49
52
  refs() {
50
53
  return {
51
54
  indicatorCleanup: null,
52
- focusVisibleValue: null
55
+ focusVisibleValue: null,
56
+ prevValue: null
53
57
  };
54
58
  },
55
59
  computed: {
56
60
  isDisabled: ({ prop, context }) => !!prop("disabled") || context.get("fieldsetDisabled")
57
61
  },
58
- entry: ["syncIndicatorRect", "syncSsr"],
62
+ entry: ["syncPrevValue", "syncIndicatorRect", "syncSsr"],
59
63
  exit: ["cleanupObserver"],
60
64
  effects: ["trackFormControlState", "trackFocusVisible"],
61
65
  watch({ track, action, context }) {
62
66
  track([() => context.get("value")], () => {
63
- action(["syncIndicatorRect", "syncInputElements"]);
67
+ action(["syncIndicatorAnimation", "syncIndicatorRect", "syncInputElements"]);
64
68
  });
65
69
  },
66
70
  on: {
@@ -81,6 +85,9 @@ var machine = createMachine({
81
85
  },
82
86
  SET_FOCUSED: {
83
87
  actions: ["setFocused"]
88
+ },
89
+ INDICATOR_TRANSITION_END: {
90
+ actions: ["clearIndicatorAnimation"]
84
91
  }
85
92
  },
86
93
  states: {
@@ -120,6 +127,19 @@ var machine = createMachine({
120
127
  const focusVisibleValue = event.value != null && event.focusVisible ? event.value : null;
121
128
  context.set("focusVisibleValue", focusVisibleValue);
122
129
  },
130
+ syncPrevValue({ context, refs }) {
131
+ refs.set("prevValue", context.get("value"));
132
+ },
133
+ syncIndicatorAnimation({ context, refs }) {
134
+ const prevValue = refs.get("prevValue");
135
+ const nextValue = context.get("value");
136
+ const animate = prevValue != null && nextValue != null && prevValue !== nextValue;
137
+ context.set("animateIndicator", animate);
138
+ refs.set("prevValue", nextValue);
139
+ },
140
+ clearIndicatorAnimation({ context }) {
141
+ context.set("animateIndicator", false);
142
+ },
123
143
  syncInputElements({ context, scope }) {
124
144
  const inputs = dom.getInputEls(scope);
125
145
  inputs.forEach((input) => {
@@ -86,6 +86,10 @@ interface PrivateContext {
86
86
  * The active tab indicator's dom rect
87
87
  */
88
88
  indicatorRect: Rect | null;
89
+ /**
90
+ * Whether indicator transitions should be animated
91
+ */
92
+ animateIndicator: boolean;
89
93
  /**
90
94
  * Whether the radio group's fieldset is disabled
91
95
  */
@@ -107,6 +111,10 @@ interface Refs {
107
111
  * Function to clean up the observer for the active tab's rect
108
112
  */
109
113
  indicatorCleanup: VoidFunction | null;
114
+ /**
115
+ * Previous selected value, used to detect real value transitions
116
+ */
117
+ prevValue: string | null;
110
118
  }
111
119
  interface RadioGroupSchema {
112
120
  state: "idle";
@@ -86,6 +86,10 @@ interface PrivateContext {
86
86
  * The active tab indicator's dom rect
87
87
  */
88
88
  indicatorRect: Rect | null;
89
+ /**
90
+ * Whether indicator transitions should be animated
91
+ */
92
+ animateIndicator: boolean;
89
93
  /**
90
94
  * Whether the radio group's fieldset is disabled
91
95
  */
@@ -107,6 +111,10 @@ interface Refs {
107
111
  * Function to clean up the observer for the active tab's rect
108
112
  */
109
113
  indicatorCleanup: VoidFunction | null;
114
+ /**
115
+ * Previous selected value, used to detect real value transitions
116
+ */
117
+ prevValue: string | null;
110
118
  }
111
119
  interface RadioGroupSchema {
112
120
  state: "idle";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/radio-group",
3
- "version": "1.35.3",
3
+ "version": "1.37.0",
4
4
  "description": "Core logic for the radio group widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -27,12 +27,12 @@
27
27
  "url": "https://github.com/chakra-ui/zag/issues"
28
28
  },
29
29
  "dependencies": {
30
- "@zag-js/anatomy": "1.35.3",
31
- "@zag-js/dom-query": "1.35.3",
32
- "@zag-js/focus-visible": "1.35.3",
33
- "@zag-js/utils": "1.35.3",
34
- "@zag-js/core": "1.35.3",
35
- "@zag-js/types": "1.35.3"
30
+ "@zag-js/focus-visible": "1.37.0",
31
+ "@zag-js/utils": "1.37.0",
32
+ "@zag-js/dom-query": "1.37.0",
33
+ "@zag-js/anatomy": "1.37.0",
34
+ "@zag-js/core": "1.37.0",
35
+ "@zag-js/types": "1.37.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "clean-package": "2.2.0"