@zag-js/tabs 1.27.1 → 1.29.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.
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _zag_js_anatomy from '@zag-js/anatomy';
2
2
  import * as _zag_js_core from '@zag-js/core';
3
3
  import { EventObject, Machine, Service } from '@zag-js/core';
4
- import { RequiredBy, DirectionProperty, CommonProperties, PropTypes, NormalizeProps } from '@zag-js/types';
4
+ import { RequiredBy, DirectionProperty, CommonProperties, Rect, PropTypes, NormalizeProps } from '@zag-js/types';
5
5
 
6
6
  declare const anatomy: _zag_js_anatomy.AnatomyInstance<"root" | "list" | "trigger" | "content" | "indicator">;
7
7
 
@@ -95,13 +95,7 @@ type TabsSchema = {
95
95
  ssr: boolean;
96
96
  value: string | null;
97
97
  focusedValue: string | null;
98
- indicatorTransition: boolean;
99
- indicatorRect: {
100
- left: string;
101
- top: string;
102
- width: string;
103
- height: string;
104
- };
98
+ indicatorRect: Rect | null;
105
99
  };
106
100
  refs: {
107
101
  indicatorCleanup: VoidFunction | null | undefined;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _zag_js_anatomy from '@zag-js/anatomy';
2
2
  import * as _zag_js_core from '@zag-js/core';
3
3
  import { EventObject, Machine, Service } from '@zag-js/core';
4
- import { RequiredBy, DirectionProperty, CommonProperties, PropTypes, NormalizeProps } from '@zag-js/types';
4
+ import { RequiredBy, DirectionProperty, CommonProperties, Rect, PropTypes, NormalizeProps } from '@zag-js/types';
5
5
 
6
6
  declare const anatomy: _zag_js_anatomy.AnatomyInstance<"root" | "list" | "trigger" | "content" | "indicator">;
7
7
 
@@ -95,13 +95,7 @@ type TabsSchema = {
95
95
  ssr: boolean;
96
96
  value: string | null;
97
97
  focusedValue: string | null;
98
- indicatorTransition: boolean;
99
- indicatorRect: {
100
- left: string;
101
- top: string;
102
- width: string;
103
- height: string;
104
- };
98
+ indicatorRect: Rect | null;
105
99
  };
106
100
  refs: {
107
101
  indicatorCleanup: VoidFunction | null | undefined;
package/dist/index.js CHANGED
@@ -11,12 +11,12 @@ var anatomy = anatomy$1.createAnatomy("tabs").parts("root", "list", "trigger", "
11
11
  var parts = anatomy.build();
12
12
  var getRootId = (ctx) => ctx.ids?.root ?? `tabs:${ctx.id}`;
13
13
  var getListId = (ctx) => ctx.ids?.list ?? `tabs:${ctx.id}:list`;
14
- var getContentId = (ctx, id) => ctx.ids?.content?.(id) ?? `tabs:${ctx.id}:content-${id}`;
15
- var getTriggerId = (ctx, id) => ctx.ids?.trigger?.(id) ?? `tabs:${ctx.id}:trigger-${id}`;
14
+ var getContentId = (ctx, value) => ctx.ids?.content?.(value) ?? `tabs:${ctx.id}:content-${value}`;
15
+ var getTriggerId = (ctx, value) => ctx.ids?.trigger?.(value) ?? `tabs:${ctx.id}:trigger-${value}`;
16
16
  var getIndicatorId = (ctx) => ctx.ids?.indicator ?? `tabs:${ctx.id}:indicator`;
17
17
  var getListEl = (ctx) => ctx.getById(getListId(ctx));
18
- var getContentEl = (ctx, id) => ctx.getById(getContentId(ctx, id));
19
- var getTriggerEl = (ctx, id) => ctx.getById(getTriggerId(ctx, id));
18
+ var getContentEl = (ctx, value) => ctx.getById(getContentId(ctx, value));
19
+ var getTriggerEl = (ctx, value) => value != null ? ctx.getById(getTriggerId(ctx, value)) : null;
20
20
  var getIndicatorEl = (ctx) => ctx.getById(getIndicatorId(ctx));
21
21
  var getElements = (ctx) => {
22
22
  const ownerId = CSS.escape(getListId(ctx));
@@ -28,21 +28,15 @@ var getLastTriggerEl = (ctx) => utils.last(getElements(ctx));
28
28
  var getNextTriggerEl = (ctx, opts) => domQuery.nextById(getElements(ctx), getTriggerId(ctx, opts.value), opts.loopFocus);
29
29
  var getPrevTriggerEl = (ctx, opts) => domQuery.prevById(getElements(ctx), getTriggerId(ctx, opts.value), opts.loopFocus);
30
30
  var getOffsetRect = (el) => ({
31
- left: el?.offsetLeft ?? 0,
32
- top: el?.offsetTop ?? 0,
31
+ x: el?.offsetLeft ?? 0,
32
+ y: el?.offsetTop ?? 0,
33
33
  width: el?.offsetWidth ?? 0,
34
34
  height: el?.offsetHeight ?? 0
35
35
  });
36
- var getRectById = (ctx, id) => {
37
- const tab = domQuery.itemById(getElements(ctx), getTriggerId(ctx, id));
38
- return resolveRect(getOffsetRect(tab));
36
+ var getRectByValue = (ctx, value) => {
37
+ const tab = domQuery.itemById(getElements(ctx), getTriggerId(ctx, value));
38
+ return getOffsetRect(tab);
39
39
  };
40
- var resolveRect = (rect) => ({
41
- width: `${rect.width}px`,
42
- height: `${rect.height}px`,
43
- left: `${rect.left}px`,
44
- top: `${rect.top}px`
45
- });
46
40
 
47
41
  // src/tabs.connect.ts
48
42
  function connect(service, normalize) {
@@ -207,23 +201,23 @@ function connect(service, normalize) {
207
201
  });
208
202
  },
209
203
  getIndicatorProps() {
210
- const indicatorRect = context.get("indicatorRect");
211
- const indicatorTransition = context.get("indicatorTransition");
204
+ const rect = context.get("indicatorRect");
212
205
  return normalize.element({
213
206
  id: getIndicatorId(scope),
214
207
  ...parts.indicator.attrs,
215
208
  dir: prop("dir"),
216
209
  "data-orientation": prop("orientation"),
210
+ hidden: rect == null,
217
211
  style: {
218
212
  "--transition-property": "left, right, top, bottom, width, height",
219
- "--left": indicatorRect.left,
220
- "--top": indicatorRect.top,
221
- "--width": indicatorRect.width,
222
- "--height": indicatorRect.height,
213
+ "--left": utils.toPx(rect?.x),
214
+ "--top": utils.toPx(rect?.y),
215
+ "--width": utils.toPx(rect?.width),
216
+ "--height": utils.toPx(rect?.height),
223
217
  position: "absolute",
224
218
  willChange: "var(--transition-property)",
225
219
  transitionProperty: "var(--transition-property)",
226
- transitionDuration: indicatorTransition ? "var(--transition-duration, 150ms)" : "0ms",
220
+ transitionDuration: "var(--transition-duration, 150ms)",
227
221
  transitionTimingFunction: "var(--transition-timing-function)",
228
222
  [isHorizontal ? "left" : "top"]: isHorizontal ? "var(--left)" : "var(--top)"
229
223
  }
@@ -267,15 +261,14 @@ var machine = createMachine({
267
261
  }
268
262
  })),
269
263
  ssr: bindable(() => ({ defaultValue: true })),
270
- indicatorTransition: bindable(() => ({ defaultValue: false })),
271
264
  indicatorRect: bindable(() => ({
272
- defaultValue: { left: "0px", top: "0px", width: "0px", height: "0px" }
265
+ defaultValue: null
273
266
  }))
274
267
  };
275
268
  },
276
269
  watch({ context, prop, track, action }) {
277
270
  track([() => context.get("value")], () => {
278
- action(["allowIndicatorTransition", "syncIndicatorRect", "syncTabIndex", "navigateIfNeeded"]);
271
+ action(["syncIndicatorRect", "syncTabIndex", "navigateIfNeeded"]);
279
272
  });
280
273
  track([() => prop("dir"), () => prop("orientation")], () => {
281
274
  action(["syncIndicatorRect"]);
@@ -449,23 +442,14 @@ var machine = createMachine({
449
442
  const cleanup = refs.get("indicatorCleanup");
450
443
  if (cleanup) cleanup();
451
444
  },
452
- allowIndicatorTransition({ context }) {
453
- context.set("indicatorTransition", true);
454
- },
455
445
  setIndicatorRect({ context, event, scope }) {
456
446
  const value = event.id ?? context.get("value");
457
447
  const indicatorEl = getIndicatorEl(scope);
458
448
  if (!indicatorEl) return;
459
- if (!value) {
460
- context.set("indicatorTransition", false);
461
- return;
462
- }
449
+ if (!value) return;
463
450
  const triggerEl = getTriggerEl(scope, value);
464
451
  if (!triggerEl) return;
465
- context.set("indicatorRect", getRectById(scope, value));
466
- domQuery.nextTick(() => {
467
- context.set("indicatorTransition", false);
468
- });
452
+ context.set("indicatorRect", getRectByValue(scope, value));
469
453
  },
470
454
  syncSsr({ context }) {
471
455
  context.set("ssr", false);
@@ -473,23 +457,17 @@ var machine = createMachine({
473
457
  syncIndicatorRect({ context, refs, scope }) {
474
458
  const cleanup = refs.get("indicatorCleanup");
475
459
  if (cleanup) cleanup();
476
- const value = context.get("value");
477
- if (!value) {
478
- context.set("indicatorTransition", false);
479
- return;
480
- }
481
- const triggerEl = getTriggerEl(scope, value);
482
460
  const indicatorEl = getIndicatorEl(scope);
483
- if (!triggerEl || !indicatorEl) return;
484
- const indicatorCleanup = domQuery.trackElementRect([triggerEl], {
485
- measure(el) {
486
- return getOffsetRect(el);
487
- },
488
- onEntry({ rects }) {
489
- const [rect] = rects;
490
- context.set("indicatorRect", resolveRect(rect));
491
- }
492
- });
461
+ if (!indicatorEl) return;
462
+ const exec = () => {
463
+ const triggerEl = getTriggerEl(scope, context.get("value"));
464
+ if (!triggerEl) return;
465
+ const rect = getOffsetRect(triggerEl);
466
+ context.set("indicatorRect", (prev) => utils.isEqual(prev, rect) ? prev : rect);
467
+ };
468
+ exec();
469
+ const triggerEls = getElements(scope);
470
+ const indicatorCleanup = utils.callAll(...triggerEls.map((el) => domQuery.resizeObserverBorderBox.observe(el, exec)));
493
471
  refs.set("indicatorCleanup", indicatorCleanup);
494
472
  },
495
473
  navigateIfNeeded({ context, prop, scope }) {
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createAnatomy } from '@zag-js/anatomy';
2
- import { isAnchorElement, trackElementRect, nextTick, raf, getFocusables, prevById, nextById, clickIfLink, itemById, queryAll, dataAttr, isOpeningInNewTab, isSafari, isComposingEvent, contains, getEventTarget, getEventKey } from '@zag-js/dom-query';
3
- import { last, first, createSplitProps } from '@zag-js/utils';
2
+ import { isAnchorElement, resizeObserverBorderBox, raf, getFocusables, prevById, nextById, clickIfLink, queryAll, itemById, dataAttr, isOpeningInNewTab, isSafari, isComposingEvent, contains, getEventTarget, getEventKey } from '@zag-js/dom-query';
3
+ import { callAll, last, first, createSplitProps, isEqual, toPx } from '@zag-js/utils';
4
4
  import { setup } from '@zag-js/core';
5
5
  import { createProps } from '@zag-js/types';
6
6
 
@@ -9,12 +9,12 @@ var anatomy = createAnatomy("tabs").parts("root", "list", "trigger", "content",
9
9
  var parts = anatomy.build();
10
10
  var getRootId = (ctx) => ctx.ids?.root ?? `tabs:${ctx.id}`;
11
11
  var getListId = (ctx) => ctx.ids?.list ?? `tabs:${ctx.id}:list`;
12
- var getContentId = (ctx, id) => ctx.ids?.content?.(id) ?? `tabs:${ctx.id}:content-${id}`;
13
- var getTriggerId = (ctx, id) => ctx.ids?.trigger?.(id) ?? `tabs:${ctx.id}:trigger-${id}`;
12
+ var getContentId = (ctx, value) => ctx.ids?.content?.(value) ?? `tabs:${ctx.id}:content-${value}`;
13
+ var getTriggerId = (ctx, value) => ctx.ids?.trigger?.(value) ?? `tabs:${ctx.id}:trigger-${value}`;
14
14
  var getIndicatorId = (ctx) => ctx.ids?.indicator ?? `tabs:${ctx.id}:indicator`;
15
15
  var getListEl = (ctx) => ctx.getById(getListId(ctx));
16
- var getContentEl = (ctx, id) => ctx.getById(getContentId(ctx, id));
17
- var getTriggerEl = (ctx, id) => ctx.getById(getTriggerId(ctx, id));
16
+ var getContentEl = (ctx, value) => ctx.getById(getContentId(ctx, value));
17
+ var getTriggerEl = (ctx, value) => value != null ? ctx.getById(getTriggerId(ctx, value)) : null;
18
18
  var getIndicatorEl = (ctx) => ctx.getById(getIndicatorId(ctx));
19
19
  var getElements = (ctx) => {
20
20
  const ownerId = CSS.escape(getListId(ctx));
@@ -26,21 +26,15 @@ var getLastTriggerEl = (ctx) => last(getElements(ctx));
26
26
  var getNextTriggerEl = (ctx, opts) => nextById(getElements(ctx), getTriggerId(ctx, opts.value), opts.loopFocus);
27
27
  var getPrevTriggerEl = (ctx, opts) => prevById(getElements(ctx), getTriggerId(ctx, opts.value), opts.loopFocus);
28
28
  var getOffsetRect = (el) => ({
29
- left: el?.offsetLeft ?? 0,
30
- top: el?.offsetTop ?? 0,
29
+ x: el?.offsetLeft ?? 0,
30
+ y: el?.offsetTop ?? 0,
31
31
  width: el?.offsetWidth ?? 0,
32
32
  height: el?.offsetHeight ?? 0
33
33
  });
34
- var getRectById = (ctx, id) => {
35
- const tab = itemById(getElements(ctx), getTriggerId(ctx, id));
36
- return resolveRect(getOffsetRect(tab));
34
+ var getRectByValue = (ctx, value) => {
35
+ const tab = itemById(getElements(ctx), getTriggerId(ctx, value));
36
+ return getOffsetRect(tab);
37
37
  };
38
- var resolveRect = (rect) => ({
39
- width: `${rect.width}px`,
40
- height: `${rect.height}px`,
41
- left: `${rect.left}px`,
42
- top: `${rect.top}px`
43
- });
44
38
 
45
39
  // src/tabs.connect.ts
46
40
  function connect(service, normalize) {
@@ -205,23 +199,23 @@ function connect(service, normalize) {
205
199
  });
206
200
  },
207
201
  getIndicatorProps() {
208
- const indicatorRect = context.get("indicatorRect");
209
- const indicatorTransition = context.get("indicatorTransition");
202
+ const rect = context.get("indicatorRect");
210
203
  return normalize.element({
211
204
  id: getIndicatorId(scope),
212
205
  ...parts.indicator.attrs,
213
206
  dir: prop("dir"),
214
207
  "data-orientation": prop("orientation"),
208
+ hidden: rect == null,
215
209
  style: {
216
210
  "--transition-property": "left, right, top, bottom, width, height",
217
- "--left": indicatorRect.left,
218
- "--top": indicatorRect.top,
219
- "--width": indicatorRect.width,
220
- "--height": indicatorRect.height,
211
+ "--left": toPx(rect?.x),
212
+ "--top": toPx(rect?.y),
213
+ "--width": toPx(rect?.width),
214
+ "--height": toPx(rect?.height),
221
215
  position: "absolute",
222
216
  willChange: "var(--transition-property)",
223
217
  transitionProperty: "var(--transition-property)",
224
- transitionDuration: indicatorTransition ? "var(--transition-duration, 150ms)" : "0ms",
218
+ transitionDuration: "var(--transition-duration, 150ms)",
225
219
  transitionTimingFunction: "var(--transition-timing-function)",
226
220
  [isHorizontal ? "left" : "top"]: isHorizontal ? "var(--left)" : "var(--top)"
227
221
  }
@@ -265,15 +259,14 @@ var machine = createMachine({
265
259
  }
266
260
  })),
267
261
  ssr: bindable(() => ({ defaultValue: true })),
268
- indicatorTransition: bindable(() => ({ defaultValue: false })),
269
262
  indicatorRect: bindable(() => ({
270
- defaultValue: { left: "0px", top: "0px", width: "0px", height: "0px" }
263
+ defaultValue: null
271
264
  }))
272
265
  };
273
266
  },
274
267
  watch({ context, prop, track, action }) {
275
268
  track([() => context.get("value")], () => {
276
- action(["allowIndicatorTransition", "syncIndicatorRect", "syncTabIndex", "navigateIfNeeded"]);
269
+ action(["syncIndicatorRect", "syncTabIndex", "navigateIfNeeded"]);
277
270
  });
278
271
  track([() => prop("dir"), () => prop("orientation")], () => {
279
272
  action(["syncIndicatorRect"]);
@@ -447,23 +440,14 @@ var machine = createMachine({
447
440
  const cleanup = refs.get("indicatorCleanup");
448
441
  if (cleanup) cleanup();
449
442
  },
450
- allowIndicatorTransition({ context }) {
451
- context.set("indicatorTransition", true);
452
- },
453
443
  setIndicatorRect({ context, event, scope }) {
454
444
  const value = event.id ?? context.get("value");
455
445
  const indicatorEl = getIndicatorEl(scope);
456
446
  if (!indicatorEl) return;
457
- if (!value) {
458
- context.set("indicatorTransition", false);
459
- return;
460
- }
447
+ if (!value) return;
461
448
  const triggerEl = getTriggerEl(scope, value);
462
449
  if (!triggerEl) return;
463
- context.set("indicatorRect", getRectById(scope, value));
464
- nextTick(() => {
465
- context.set("indicatorTransition", false);
466
- });
450
+ context.set("indicatorRect", getRectByValue(scope, value));
467
451
  },
468
452
  syncSsr({ context }) {
469
453
  context.set("ssr", false);
@@ -471,23 +455,17 @@ var machine = createMachine({
471
455
  syncIndicatorRect({ context, refs, scope }) {
472
456
  const cleanup = refs.get("indicatorCleanup");
473
457
  if (cleanup) cleanup();
474
- const value = context.get("value");
475
- if (!value) {
476
- context.set("indicatorTransition", false);
477
- return;
478
- }
479
- const triggerEl = getTriggerEl(scope, value);
480
458
  const indicatorEl = getIndicatorEl(scope);
481
- if (!triggerEl || !indicatorEl) return;
482
- const indicatorCleanup = trackElementRect([triggerEl], {
483
- measure(el) {
484
- return getOffsetRect(el);
485
- },
486
- onEntry({ rects }) {
487
- const [rect] = rects;
488
- context.set("indicatorRect", resolveRect(rect));
489
- }
490
- });
459
+ if (!indicatorEl) return;
460
+ const exec = () => {
461
+ const triggerEl = getTriggerEl(scope, context.get("value"));
462
+ if (!triggerEl) return;
463
+ const rect = getOffsetRect(triggerEl);
464
+ context.set("indicatorRect", (prev) => isEqual(prev, rect) ? prev : rect);
465
+ };
466
+ exec();
467
+ const triggerEls = getElements(scope);
468
+ const indicatorCleanup = callAll(...triggerEls.map((el) => resizeObserverBorderBox.observe(el, exec)));
491
469
  refs.set("indicatorCleanup", indicatorCleanup);
492
470
  },
493
471
  navigateIfNeeded({ context, prop, scope }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/tabs",
3
- "version": "1.27.1",
3
+ "version": "1.29.0",
4
4
  "description": "Core logic for the tabs widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -26,11 +26,11 @@
26
26
  "url": "https://github.com/chakra-ui/zag/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@zag-js/anatomy": "1.27.1",
30
- "@zag-js/dom-query": "1.27.1",
31
- "@zag-js/utils": "1.27.1",
32
- "@zag-js/core": "1.27.1",
33
- "@zag-js/types": "1.27.1"
29
+ "@zag-js/anatomy": "1.29.0",
30
+ "@zag-js/dom-query": "1.29.0",
31
+ "@zag-js/utils": "1.29.0",
32
+ "@zag-js/core": "1.29.0",
33
+ "@zag-js/types": "1.29.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "clean-package": "2.2.0"