@zag-js/tabs 1.41.0 → 2.0.0-next.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.
@@ -43,7 +43,7 @@ function connect(service, normalize) {
43
43
  const focused = state.matches("focused");
44
44
  const isVertical = prop("orientation") === "vertical";
45
45
  const isHorizontal = prop("orientation") === "horizontal";
46
- const composite = prop("composite");
46
+ const virtualFocus = prop("virtualFocus");
47
47
  function getTriggerState(props) {
48
48
  return {
49
49
  selected: context.get("value") === props.value,
@@ -82,8 +82,7 @@ function connect(service, normalize) {
82
82
  },
83
83
  getRootProps() {
84
84
  return normalize.element({
85
- ...import_tabs.parts.root.attrs,
86
- id: dom.getRootId(scope),
85
+ ...import_tabs.parts.root.attrs(scope.id),
87
86
  "data-orientation": prop("orientation"),
88
87
  "data-focus": (0, import_dom_query.dataAttr)(focused),
89
88
  dir: prop("dir")
@@ -91,8 +90,7 @@ function connect(service, normalize) {
91
90
  },
92
91
  getListProps() {
93
92
  return normalize.element({
94
- ...import_tabs.parts.list.attrs,
95
- id: dom.getListId(scope),
93
+ ...import_tabs.parts.list.attrs(scope.id),
96
94
  role: "tablist",
97
95
  dir: prop("dir"),
98
96
  "data-focus": (0, import_dom_query.dataAttr)(focused),
@@ -145,7 +143,7 @@ function connect(service, normalize) {
145
143
  const { value, disabled } = props;
146
144
  const triggerState = getTriggerState(props);
147
145
  return normalize.button({
148
- ...import_tabs.parts.trigger.attrs,
146
+ ...import_tabs.parts.trigger.attrs(scope.id),
149
147
  role: "tab",
150
148
  type: "button",
151
149
  disabled,
@@ -158,10 +156,9 @@ function connect(service, normalize) {
158
156
  "data-selected": (0, import_dom_query.dataAttr)(triggerState.selected),
159
157
  "data-focus": (0, import_dom_query.dataAttr)(triggerState.focused),
160
158
  "aria-controls": triggerState.selected ? dom.getContentId(scope, value) : void 0,
161
- "data-ownedby": dom.getListId(scope),
162
159
  "data-ssr": (0, import_dom_query.dataAttr)(context.get("ssr")),
163
160
  id: dom.getTriggerId(scope, value),
164
- tabIndex: triggerState.selected && composite ? 0 : -1,
161
+ tabIndex: triggerState.selected && !virtualFocus ? 0 : -1,
165
162
  onFocus() {
166
163
  send({ type: "TAB_FOCUS", value });
167
164
  },
@@ -186,13 +183,12 @@ function connect(service, normalize) {
186
183
  const { value } = props;
187
184
  const selected = context.get("value") === value;
188
185
  return normalize.element({
189
- ...import_tabs.parts.content.attrs,
186
+ ...import_tabs.parts.content.attrs(scope.id),
190
187
  dir: prop("dir"),
191
188
  id: dom.getContentId(scope, value),
192
- tabIndex: composite ? 0 : -1,
189
+ tabIndex: !virtualFocus ? 0 : -1,
193
190
  "aria-labelledby": dom.getTriggerId(scope, value),
194
191
  role: "tabpanel",
195
- "data-ownedby": dom.getListId(scope),
196
192
  "data-selected": (0, import_dom_query.dataAttr)(selected),
197
193
  "data-orientation": prop("orientation"),
198
194
  hidden: !selected
@@ -202,8 +198,7 @@ function connect(service, normalize) {
202
198
  const rect = context.get("indicatorRect");
203
199
  const animateIndicator = context.get("animateIndicator");
204
200
  return normalize.element({
205
- id: dom.getIndicatorId(scope),
206
- ...import_tabs.parts.indicator.attrs,
201
+ ...import_tabs.parts.indicator.attrs(scope.id),
207
202
  dir: prop("dir"),
208
203
  "data-orientation": prop("orientation"),
209
204
  hidden: isRectEmpty(rect),
@@ -17,7 +17,7 @@ function connect(service, normalize) {
17
17
  const focused = state.matches("focused");
18
18
  const isVertical = prop("orientation") === "vertical";
19
19
  const isHorizontal = prop("orientation") === "horizontal";
20
- const composite = prop("composite");
20
+ const virtualFocus = prop("virtualFocus");
21
21
  function getTriggerState(props) {
22
22
  return {
23
23
  selected: context.get("value") === props.value,
@@ -56,8 +56,7 @@ function connect(service, normalize) {
56
56
  },
57
57
  getRootProps() {
58
58
  return normalize.element({
59
- ...parts.root.attrs,
60
- id: dom.getRootId(scope),
59
+ ...parts.root.attrs(scope.id),
61
60
  "data-orientation": prop("orientation"),
62
61
  "data-focus": dataAttr(focused),
63
62
  dir: prop("dir")
@@ -65,8 +64,7 @@ function connect(service, normalize) {
65
64
  },
66
65
  getListProps() {
67
66
  return normalize.element({
68
- ...parts.list.attrs,
69
- id: dom.getListId(scope),
67
+ ...parts.list.attrs(scope.id),
70
68
  role: "tablist",
71
69
  dir: prop("dir"),
72
70
  "data-focus": dataAttr(focused),
@@ -119,7 +117,7 @@ function connect(service, normalize) {
119
117
  const { value, disabled } = props;
120
118
  const triggerState = getTriggerState(props);
121
119
  return normalize.button({
122
- ...parts.trigger.attrs,
120
+ ...parts.trigger.attrs(scope.id),
123
121
  role: "tab",
124
122
  type: "button",
125
123
  disabled,
@@ -132,10 +130,9 @@ function connect(service, normalize) {
132
130
  "data-selected": dataAttr(triggerState.selected),
133
131
  "data-focus": dataAttr(triggerState.focused),
134
132
  "aria-controls": triggerState.selected ? dom.getContentId(scope, value) : void 0,
135
- "data-ownedby": dom.getListId(scope),
136
133
  "data-ssr": dataAttr(context.get("ssr")),
137
134
  id: dom.getTriggerId(scope, value),
138
- tabIndex: triggerState.selected && composite ? 0 : -1,
135
+ tabIndex: triggerState.selected && !virtualFocus ? 0 : -1,
139
136
  onFocus() {
140
137
  send({ type: "TAB_FOCUS", value });
141
138
  },
@@ -160,13 +157,12 @@ function connect(service, normalize) {
160
157
  const { value } = props;
161
158
  const selected = context.get("value") === value;
162
159
  return normalize.element({
163
- ...parts.content.attrs,
160
+ ...parts.content.attrs(scope.id),
164
161
  dir: prop("dir"),
165
162
  id: dom.getContentId(scope, value),
166
- tabIndex: composite ? 0 : -1,
163
+ tabIndex: !virtualFocus ? 0 : -1,
167
164
  "aria-labelledby": dom.getTriggerId(scope, value),
168
165
  role: "tabpanel",
169
- "data-ownedby": dom.getListId(scope),
170
166
  "data-selected": dataAttr(selected),
171
167
  "data-orientation": prop("orientation"),
172
168
  hidden: !selected
@@ -176,8 +172,7 @@ function connect(service, normalize) {
176
172
  const rect = context.get("indicatorRect");
177
173
  const animateIndicator = context.get("animateIndicator");
178
174
  return normalize.element({
179
- id: dom.getIndicatorId(scope),
180
- ...parts.indicator.attrs,
175
+ ...parts.indicator.attrs(scope.id),
181
176
  dir: prop("dir"),
182
177
  "data-orientation": prop("orientation"),
183
178
  hidden: isRectEmpty(rect),
package/dist/tabs.dom.js CHANGED
@@ -40,19 +40,18 @@ __export(tabs_dom_exports, {
40
40
  module.exports = __toCommonJS(tabs_dom_exports);
41
41
  var import_dom_query = require("@zag-js/dom-query");
42
42
  var import_utils = require("@zag-js/utils");
43
- var getRootId = (ctx) => ctx.ids?.root ?? `tabs:${ctx.id}`;
44
- var getListId = (ctx) => ctx.ids?.list ?? `tabs:${ctx.id}:list`;
45
- var getContentId = (ctx, value) => ctx.ids?.content?.(value) ?? `tabs:${ctx.id}:content-${value}`;
46
- var getTriggerId = (ctx, value) => ctx.ids?.trigger?.(value) ?? `tabs:${ctx.id}:trigger-${value}`;
47
- var getIndicatorId = (ctx) => ctx.ids?.indicator ?? `tabs:${ctx.id}:indicator`;
48
- var getListEl = (ctx) => ctx.getById(getListId(ctx));
43
+ var import_tabs = require("./tabs.anatomy.js");
44
+ var getRootId = (ctx) => ctx.ids?.root ?? `${ctx.id}`;
45
+ var getListId = (ctx) => ctx.ids?.list ?? `${ctx.id}:list`;
46
+ var getContentId = (ctx, value) => ctx.ids?.content?.(value) ?? `${ctx.id}:content:${value}`;
47
+ var getTriggerId = (ctx, value) => ctx.ids?.trigger?.(value) ?? `${ctx.id}:trigger:${value}`;
48
+ var getIndicatorId = (ctx) => ctx.ids?.indicator ?? `${ctx.id}:indicator`;
49
+ var getListEl = (ctx) => ctx.query(ctx.selector(import_tabs.parts.list));
49
50
  var getContentEl = (ctx, value) => ctx.getById(getContentId(ctx, value));
50
- var getTriggerEl = (ctx, value) => value != null ? ctx.getById(getTriggerId(ctx, value)) : null;
51
- var getIndicatorEl = (ctx) => ctx.getById(getIndicatorId(ctx));
51
+ var getTriggerEl = (ctx, value) => value != null ? ctx.query(`${ctx.selector(import_tabs.parts.trigger)}[data-value="${value}"]`) : null;
52
+ var getIndicatorEl = (ctx) => ctx.query(ctx.selector(import_tabs.parts.indicator));
52
53
  var getElements = (ctx) => {
53
- const ownerId = CSS.escape(getListId(ctx));
54
- const selector = `[role=tab][data-ownedby='${ownerId}']:not([disabled])`;
55
- return (0, import_dom_query.queryAll)(getListEl(ctx), selector);
54
+ return ctx.queryAll(`${ctx.selector(import_tabs.parts.trigger)}:not([disabled])`);
56
55
  };
57
56
  var getFirstTriggerEl = (ctx) => (0, import_utils.first)(getElements(ctx));
58
57
  var getLastTriggerEl = (ctx) => (0, import_utils.last)(getElements(ctx));
package/dist/tabs.dom.mjs CHANGED
@@ -1,19 +1,18 @@
1
1
  // src/tabs.dom.ts
2
- import { itemById, nextById, prevById, queryAll } from "@zag-js/dom-query";
2
+ import { itemById, nextById, prevById } from "@zag-js/dom-query";
3
3
  import { first, last } from "@zag-js/utils";
4
- var getRootId = (ctx) => ctx.ids?.root ?? `tabs:${ctx.id}`;
5
- var getListId = (ctx) => ctx.ids?.list ?? `tabs:${ctx.id}:list`;
6
- var getContentId = (ctx, value) => ctx.ids?.content?.(value) ?? `tabs:${ctx.id}:content-${value}`;
7
- var getTriggerId = (ctx, value) => ctx.ids?.trigger?.(value) ?? `tabs:${ctx.id}:trigger-${value}`;
8
- var getIndicatorId = (ctx) => ctx.ids?.indicator ?? `tabs:${ctx.id}:indicator`;
9
- var getListEl = (ctx) => ctx.getById(getListId(ctx));
4
+ import { parts } from "./tabs.anatomy.mjs";
5
+ var getRootId = (ctx) => ctx.ids?.root ?? `${ctx.id}`;
6
+ var getListId = (ctx) => ctx.ids?.list ?? `${ctx.id}:list`;
7
+ var getContentId = (ctx, value) => ctx.ids?.content?.(value) ?? `${ctx.id}:content:${value}`;
8
+ var getTriggerId = (ctx, value) => ctx.ids?.trigger?.(value) ?? `${ctx.id}:trigger:${value}`;
9
+ var getIndicatorId = (ctx) => ctx.ids?.indicator ?? `${ctx.id}:indicator`;
10
+ var getListEl = (ctx) => ctx.query(ctx.selector(parts.list));
10
11
  var getContentEl = (ctx, value) => ctx.getById(getContentId(ctx, value));
11
- var getTriggerEl = (ctx, value) => value != null ? ctx.getById(getTriggerId(ctx, value)) : null;
12
- var getIndicatorEl = (ctx) => ctx.getById(getIndicatorId(ctx));
12
+ var getTriggerEl = (ctx, value) => value != null ? ctx.query(`${ctx.selector(parts.trigger)}[data-value="${value}"]`) : null;
13
+ var getIndicatorEl = (ctx) => ctx.query(ctx.selector(parts.indicator));
13
14
  var getElements = (ctx) => {
14
- const ownerId = CSS.escape(getListId(ctx));
15
- const selector = `[role=tab][data-ownedby='${ownerId}']:not([disabled])`;
16
- return queryAll(getListEl(ctx), selector);
15
+ return ctx.queryAll(`${ctx.selector(parts.trigger)}:not([disabled])`);
17
16
  };
18
17
  var getFirstTriggerEl = (ctx) => first(getElements(ctx));
19
18
  var getLastTriggerEl = (ctx) => last(getElements(ctx));
@@ -45,7 +45,6 @@ var machine = createMachine({
45
45
  orientation: "horizontal",
46
46
  activationMode: "automatic",
47
47
  loopFocus: true,
48
- composite: true,
49
48
  navigate(details) {
50
49
  (0, import_dom_query.clickIfLink)(details.node);
51
50
  },
@@ -226,10 +225,12 @@ var machine = createMachine({
226
225
  loopFocus: prop("loopFocus")
227
226
  });
228
227
  (0, import_dom_query.raf)(() => {
229
- if (prop("composite")) {
228
+ if (prop("virtualFocus")) {
229
+ if (triggerEl?.dataset.value != null) {
230
+ context.set("focusedValue", triggerEl.dataset.value);
231
+ }
232
+ } else {
230
233
  triggerEl?.focus();
231
- } else if (triggerEl?.dataset.value != null) {
232
- context.set("focusedValue", triggerEl.dataset.value);
233
234
  }
234
235
  });
235
236
  },
@@ -241,10 +242,12 @@ var machine = createMachine({
241
242
  loopFocus: prop("loopFocus")
242
243
  });
243
244
  (0, import_dom_query.raf)(() => {
244
- if (prop("composite")) {
245
+ if (prop("virtualFocus")) {
246
+ if (triggerEl?.dataset.value != null) {
247
+ context.set("focusedValue", triggerEl.dataset.value);
248
+ }
249
+ } else {
245
250
  triggerEl?.focus();
246
- } else if (triggerEl?.dataset.value != null) {
247
- context.set("focusedValue", triggerEl.dataset.value);
248
251
  }
249
252
  });
250
253
  },
@@ -11,7 +11,6 @@ var machine = createMachine({
11
11
  orientation: "horizontal",
12
12
  activationMode: "automatic",
13
13
  loopFocus: true,
14
- composite: true,
15
14
  navigate(details) {
16
15
  clickIfLink(details.node);
17
16
  },
@@ -192,10 +191,12 @@ var machine = createMachine({
192
191
  loopFocus: prop("loopFocus")
193
192
  });
194
193
  raf(() => {
195
- if (prop("composite")) {
194
+ if (prop("virtualFocus")) {
195
+ if (triggerEl?.dataset.value != null) {
196
+ context.set("focusedValue", triggerEl.dataset.value);
197
+ }
198
+ } else {
196
199
  triggerEl?.focus();
197
- } else if (triggerEl?.dataset.value != null) {
198
- context.set("focusedValue", triggerEl.dataset.value);
199
200
  }
200
201
  });
201
202
  },
@@ -207,10 +208,12 @@ var machine = createMachine({
207
208
  loopFocus: prop("loopFocus")
208
209
  });
209
210
  raf(() => {
210
- if (prop("composite")) {
211
+ if (prop("virtualFocus")) {
212
+ if (triggerEl?.dataset.value != null) {
213
+ context.set("focusedValue", triggerEl.dataset.value);
214
+ }
215
+ } else {
211
216
  triggerEl?.focus();
212
- } else if (triggerEl?.dataset.value != null) {
213
- context.set("focusedValue", triggerEl.dataset.value);
214
217
  }
215
218
  });
216
219
  },
@@ -32,7 +32,7 @@ var import_types = require("@zag-js/types");
32
32
  var import_utils = require("@zag-js/utils");
33
33
  var props = (0, import_types.createProps)()([
34
34
  "activationMode",
35
- "composite",
35
+ "virtualFocus",
36
36
  "deselectable",
37
37
  "dir",
38
38
  "getRootNode",
@@ -3,7 +3,7 @@ import { createProps } from "@zag-js/types";
3
3
  import { createSplitProps } from "@zag-js/utils";
4
4
  var props = createProps()([
5
5
  "activationMode",
6
- "composite",
6
+ "virtualFocus",
7
7
  "deselectable",
8
8
  "dir",
9
9
  "getRootNode",
@@ -70,9 +70,15 @@ interface TabsProps extends DirectionProperty, CommonProperties {
70
70
  */
71
71
  onFocusChange?: ((details: FocusChangeDetails) => void) | undefined;
72
72
  /**
73
- * Whether the tab is composite
73
+ * Whether the tabs use virtual focus instead of roving tabindex.
74
+ *
75
+ * When `true`, triggers receive no DOM focus — arrow keys update `focusedValue`
76
+ * and all triggers remain `tabIndex: -1`. Useful when tabs live inside another
77
+ * widget that manages focus (e.g. a combobox).
78
+ *
79
+ * @default false
74
80
  */
75
- composite?: boolean | undefined;
81
+ virtualFocus?: boolean | undefined;
76
82
  /**
77
83
  * Whether the active tab can be deselected when clicking on it.
78
84
  */
@@ -70,9 +70,15 @@ interface TabsProps extends DirectionProperty, CommonProperties {
70
70
  */
71
71
  onFocusChange?: ((details: FocusChangeDetails) => void) | undefined;
72
72
  /**
73
- * Whether the tab is composite
73
+ * Whether the tabs use virtual focus instead of roving tabindex.
74
+ *
75
+ * When `true`, triggers receive no DOM focus — arrow keys update `focusedValue`
76
+ * and all triggers remain `tabIndex: -1`. Useful when tabs live inside another
77
+ * widget that manages focus (e.g. a combobox).
78
+ *
79
+ * @default false
74
80
  */
75
- composite?: boolean | undefined;
81
+ virtualFocus?: boolean | undefined;
76
82
  /**
77
83
  * Whether the active tab can be deselected when clicking on it.
78
84
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/tabs",
3
- "version": "1.41.0",
3
+ "version": "2.0.0-next.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.41.0",
30
- "@zag-js/dom-query": "1.41.0",
31
- "@zag-js/utils": "1.41.0",
32
- "@zag-js/core": "1.41.0",
33
- "@zag-js/types": "1.41.0"
29
+ "@zag-js/anatomy": "2.0.0-next.0",
30
+ "@zag-js/dom-query": "2.0.0-next.0",
31
+ "@zag-js/utils": "2.0.0-next.0",
32
+ "@zag-js/core": "2.0.0-next.0",
33
+ "@zag-js/types": "2.0.0-next.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "clean-package": "2.2.0"