@zag-js/tabs 0.82.2 → 1.0.1

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.js CHANGED
@@ -4,77 +4,66 @@ var anatomy$1 = require('@zag-js/anatomy');
4
4
  var domQuery = require('@zag-js/dom-query');
5
5
  var utils = require('@zag-js/utils');
6
6
  var core = require('@zag-js/core');
7
- var elementRect = require('@zag-js/element-rect');
8
7
  var types = require('@zag-js/types');
9
8
 
10
9
  // src/tabs.anatomy.ts
11
10
  var anatomy = anatomy$1.createAnatomy("tabs").parts("root", "list", "trigger", "content", "indicator");
12
11
  var parts = anatomy.build();
13
- var dom = domQuery.createScope({
14
- getRootId: (ctx) => ctx.ids?.root ?? `tabs:${ctx.id}`,
15
- getListId: (ctx) => ctx.ids?.list ?? `tabs:${ctx.id}:list`,
16
- getContentId: (ctx, id) => ctx.ids?.content ?? `tabs:${ctx.id}:content-${id}`,
17
- getTriggerId: (ctx, id) => ctx.ids?.trigger ?? `tabs:${ctx.id}:trigger-${id}`,
18
- getIndicatorId: (ctx) => ctx.ids?.indicator ?? `tabs:${ctx.id}:indicator`,
19
- getListEl: (ctx) => dom.getById(ctx, dom.getListId(ctx)),
20
- getContentEl: (ctx, id) => dom.getById(ctx, dom.getContentId(ctx, id)),
21
- getTriggerEl: (ctx, id) => dom.getById(ctx, dom.getTriggerId(ctx, id)),
22
- getIndicatorEl: (ctx) => dom.getById(ctx, dom.getIndicatorId(ctx)),
23
- getElements: (ctx) => {
24
- const ownerId = CSS.escape(dom.getListId(ctx));
25
- const selector = `[role=tab][data-ownedby='${ownerId}']:not([disabled])`;
26
- return domQuery.queryAll(dom.getListEl(ctx), selector);
27
- },
28
- getFirstTriggerEl: (ctx) => utils.first(dom.getElements(ctx)),
29
- getLastTriggerEl: (ctx) => utils.last(dom.getElements(ctx)),
30
- getNextTriggerEl: (ctx, id) => domQuery.nextById(dom.getElements(ctx), dom.getTriggerId(ctx, id), ctx.loopFocus),
31
- getPrevTriggerEl: (ctx, id) => domQuery.prevById(dom.getElements(ctx), dom.getTriggerId(ctx, id), ctx.loopFocus),
32
- getSelectedContentEl: (ctx) => {
33
- if (!ctx.value) return;
34
- return dom.getContentEl(ctx, ctx.value);
35
- },
36
- getSelectedTriggerEl: (ctx) => {
37
- if (!ctx.value) return;
38
- return dom.getTriggerEl(ctx, ctx.value);
39
- },
40
- getOffsetRect: (el) => {
41
- return {
42
- left: el?.offsetLeft ?? 0,
43
- top: el?.offsetTop ?? 0,
44
- width: el?.offsetWidth ?? 0,
45
- height: el?.offsetHeight ?? 0
46
- };
47
- },
48
- getRectById: (ctx, id) => {
49
- const tab = domQuery.itemById(dom.getElements(ctx), dom.getTriggerId(ctx, id));
50
- return dom.resolveRect(dom.getOffsetRect(tab));
51
- },
52
- resolveRect: (rect) => ({
53
- width: `${rect.width}px`,
54
- height: `${rect.height}px`,
55
- left: `${rect.left}px`,
56
- top: `${rect.top}px`
57
- })
12
+ var getRootId = (ctx) => ctx.ids?.root ?? `tabs:${ctx.id}`;
13
+ var getListId = (ctx) => ctx.ids?.list ?? `tabs:${ctx.id}:list`;
14
+ var getContentId = (ctx, id) => ctx.ids?.content ?? `tabs:${ctx.id}:content-${id}`;
15
+ var getTriggerId = (ctx, id) => ctx.ids?.trigger ?? `tabs:${ctx.id}:trigger-${id}`;
16
+ var getIndicatorId = (ctx) => ctx.ids?.indicator ?? `tabs:${ctx.id}:indicator`;
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));
20
+ var getIndicatorEl = (ctx) => ctx.getById(getIndicatorId(ctx));
21
+ var getElements = (ctx) => {
22
+ const ownerId = CSS.escape(getListId(ctx));
23
+ const selector = `[role=tab][data-ownedby='${ownerId}']:not([disabled])`;
24
+ return domQuery.queryAll(getListEl(ctx), selector);
25
+ };
26
+ var getFirstTriggerEl = (ctx) => utils.first(getElements(ctx));
27
+ var getLastTriggerEl = (ctx) => utils.last(getElements(ctx));
28
+ var getNextTriggerEl = (ctx, opts) => domQuery.nextById(getElements(ctx), getTriggerId(ctx, opts.value), opts.loopFocus);
29
+ var getPrevTriggerEl = (ctx, opts) => domQuery.prevById(getElements(ctx), getTriggerId(ctx, opts.value), opts.loopFocus);
30
+ var getOffsetRect = (el) => {
31
+ return {
32
+ left: el?.offsetLeft ?? 0,
33
+ top: el?.offsetTop ?? 0,
34
+ width: el?.offsetWidth ?? 0,
35
+ height: el?.offsetHeight ?? 0
36
+ };
37
+ };
38
+ var getRectById = (ctx, id) => {
39
+ const tab = domQuery.itemById(getElements(ctx), getTriggerId(ctx, id));
40
+ return resolveRect(getOffsetRect(tab));
41
+ };
42
+ var resolveRect = (rect) => ({
43
+ width: `${rect.width}px`,
44
+ height: `${rect.height}px`,
45
+ left: `${rect.left}px`,
46
+ top: `${rect.top}px`
58
47
  });
59
48
 
60
49
  // src/tabs.connect.ts
61
- function connect(state, send, normalize) {
62
- const translations = state.context.translations;
50
+ function connect(service, normalize) {
51
+ const { state, send, context, prop, scope } = service;
52
+ const translations = prop("translations");
63
53
  const focused = state.matches("focused");
64
- const isVertical = state.context.orientation === "vertical";
65
- const isHorizontal = state.context.orientation === "horizontal";
66
- const composite = state.context.composite;
67
- const indicator = state.context.indicatorState;
54
+ const isVertical = prop("orientation") === "vertical";
55
+ const isHorizontal = prop("orientation") === "horizontal";
56
+ const composite = prop("composite");
68
57
  function getTriggerState(props2) {
69
58
  return {
70
- selected: state.context.value === props2.value,
71
- focused: state.context.focusedValue === props2.value,
59
+ selected: context.get("value") === props2.value,
60
+ focused: context.get("focusedValue") === props2.value,
72
61
  disabled: !!props2.disabled
73
62
  };
74
63
  }
75
64
  return {
76
- value: state.context.value,
77
- focusedValue: state.context.focusedValue,
65
+ value: context.get("value"),
66
+ focusedValue: context.get("focusedValue"),
78
67
  setValue(value) {
79
68
  send({ type: "SET_VALUE", value });
80
69
  },
@@ -82,11 +71,11 @@ function connect(state, send, normalize) {
82
71
  send({ type: "CLEAR_VALUE" });
83
72
  },
84
73
  setIndicatorRect(value) {
85
- const id = dom.getTriggerId(state.context, value);
74
+ const id = getTriggerId(scope, value);
86
75
  send({ type: "SET_INDICATOR_RECT", id });
87
76
  },
88
77
  syncTabIndex() {
89
- send("SYNC_TAB_INDEX");
78
+ send({ type: "SYNC_TAB_INDEX" });
90
79
  },
91
80
  selectNext(fromValue) {
92
81
  send({ type: "TAB_FOCUS", value: fromValue, src: "selectNext" });
@@ -97,26 +86,28 @@ function connect(state, send, normalize) {
97
86
  send({ type: "ARROW_PREV", src: "selectPrev" });
98
87
  },
99
88
  focus() {
100
- dom.getSelectedTriggerEl(state.context)?.focus();
89
+ const value = context.get("value");
90
+ if (!value) return;
91
+ getTriggerEl(scope, value)?.focus();
101
92
  },
102
93
  getRootProps() {
103
94
  return normalize.element({
104
95
  ...parts.root.attrs,
105
- id: dom.getRootId(state.context),
106
- "data-orientation": state.context.orientation,
96
+ id: getRootId(scope),
97
+ "data-orientation": prop("orientation"),
107
98
  "data-focus": domQuery.dataAttr(focused),
108
- dir: state.context.dir
99
+ dir: prop("dir")
109
100
  });
110
101
  },
111
102
  getListProps() {
112
103
  return normalize.element({
113
104
  ...parts.list.attrs,
114
- id: dom.getListId(state.context),
105
+ id: getListId(scope),
115
106
  role: "tablist",
116
- dir: state.context.dir,
107
+ dir: prop("dir"),
117
108
  "data-focus": domQuery.dataAttr(focused),
118
- "aria-orientation": state.context.orientation,
119
- "data-orientation": state.context.orientation,
109
+ "aria-orientation": prop("orientation"),
110
+ "data-orientation": prop("orientation"),
120
111
  "aria-label": translations?.listLabel,
121
112
  onKeyDown(event) {
122
113
  if (event.defaultPrevented) return;
@@ -140,16 +131,19 @@ function connect(state, send, normalize) {
140
131
  send({ type: "ARROW_NEXT", key: "ArrowRight" });
141
132
  },
142
133
  Home() {
143
- send("HOME");
134
+ send({ type: "HOME" });
144
135
  },
145
136
  End() {
146
- send("END");
137
+ send({ type: "END" });
147
138
  },
148
139
  Enter() {
149
140
  send({ type: "ENTER" });
150
141
  }
151
142
  };
152
- let key = domQuery.getEventKey(event, state.context);
143
+ let key = domQuery.getEventKey(event, {
144
+ dir: prop("dir"),
145
+ orientation: prop("orientation")
146
+ });
153
147
  const exec = keyMap[key];
154
148
  if (exec) {
155
149
  event.preventDefault();
@@ -167,18 +161,18 @@ function connect(state, send, normalize) {
167
161
  role: "tab",
168
162
  type: "button",
169
163
  disabled,
170
- dir: state.context.dir,
171
- "data-orientation": state.context.orientation,
164
+ dir: prop("dir"),
165
+ "data-orientation": prop("orientation"),
172
166
  "data-disabled": domQuery.dataAttr(disabled),
173
167
  "aria-disabled": disabled,
174
168
  "data-value": value,
175
169
  "aria-selected": triggerState.selected,
176
170
  "data-selected": domQuery.dataAttr(triggerState.selected),
177
171
  "data-focus": domQuery.dataAttr(triggerState.focused),
178
- "aria-controls": triggerState.selected ? dom.getContentId(state.context, value) : void 0,
179
- "data-ownedby": dom.getListId(state.context),
180
- "data-ssr": domQuery.dataAttr(state.context.ssr),
181
- id: dom.getTriggerId(state.context, value),
172
+ "aria-controls": triggerState.selected ? getContentId(scope, value) : void 0,
173
+ "data-ownedby": getListId(scope),
174
+ "data-ssr": domQuery.dataAttr(context.get("ssr")),
175
+ id: getTriggerId(scope, value),
182
176
  tabIndex: triggerState.selected && composite ? 0 : -1,
183
177
  onFocus() {
184
178
  send({ type: "TAB_FOCUS", value });
@@ -201,36 +195,38 @@ function connect(state, send, normalize) {
201
195
  },
202
196
  getContentProps(props2) {
203
197
  const { value } = props2;
204
- const selected = state.context.value === value;
198
+ const selected = context.get("value") === value;
205
199
  return normalize.element({
206
200
  ...parts.content.attrs,
207
- dir: state.context.dir,
208
- id: dom.getContentId(state.context, value),
201
+ dir: prop("dir"),
202
+ id: getContentId(scope, value),
209
203
  tabIndex: composite ? 0 : -1,
210
- "aria-labelledby": dom.getTriggerId(state.context, value),
204
+ "aria-labelledby": getTriggerId(scope, value),
211
205
  role: "tabpanel",
212
- "data-ownedby": dom.getListId(state.context),
206
+ "data-ownedby": getListId(scope),
213
207
  "data-selected": domQuery.dataAttr(selected),
214
- "data-orientation": state.context.orientation,
208
+ "data-orientation": prop("orientation"),
215
209
  hidden: !selected
216
210
  });
217
211
  },
218
212
  getIndicatorProps() {
213
+ const indicatorRect = context.get("indicatorRect");
214
+ const indicatorTransition = context.get("indicatorTransition");
219
215
  return normalize.element({
220
- id: dom.getIndicatorId(state.context),
216
+ id: getIndicatorId(scope),
221
217
  ...parts.indicator.attrs,
222
- dir: state.context.dir,
223
- "data-orientation": state.context.orientation,
218
+ dir: prop("dir"),
219
+ "data-orientation": prop("orientation"),
224
220
  style: {
225
221
  "--transition-property": "left, right, top, bottom, width, height",
226
- "--left": indicator.rect?.left,
227
- "--top": indicator.rect?.top,
228
- "--width": indicator.rect?.width,
229
- "--height": indicator.rect?.height,
222
+ "--left": indicatorRect.left,
223
+ "--top": indicatorRect.top,
224
+ "--width": indicatorRect.width,
225
+ "--height": indicatorRect.height,
230
226
  position: "absolute",
231
227
  willChange: "var(--transition-property)",
232
228
  transitionProperty: "var(--transition-property)",
233
- transitionDuration: indicator.transition ? "var(--transition-duration, 150ms)" : "0ms",
229
+ transitionDuration: indicatorTransition ? "var(--transition-duration, 150ms)" : "0ms",
234
230
  transitionTimingFunction: "var(--transition-timing-function)",
235
231
  [isHorizontal ? "left" : "top"]: isHorizontal ? "var(--left)" : "var(--top)"
236
232
  }
@@ -238,270 +234,276 @@ function connect(state, send, normalize) {
238
234
  }
239
235
  };
240
236
  }
241
- var { not } = core.guards;
242
- function machine(userContext) {
243
- const ctx = utils.compact(userContext);
244
- return core.createMachine(
245
- {
246
- initial: "idle",
247
- context: {
248
- dir: "ltr",
249
- orientation: "horizontal",
250
- activationMode: "automatic",
251
- value: null,
252
- loopFocus: true,
253
- composite: true,
254
- navigate(details) {
255
- domQuery.clickIfLink(details.node);
237
+ var { not } = core.createGuards();
238
+ var machine = core.createMachine({
239
+ props({ props: props2 }) {
240
+ return {
241
+ dir: "ltr",
242
+ orientation: "horizontal",
243
+ activationMode: "automatic",
244
+ loopFocus: true,
245
+ composite: true,
246
+ navigate(details) {
247
+ domQuery.clickIfLink(details.node);
248
+ },
249
+ defaultValue: null,
250
+ ...props2
251
+ };
252
+ },
253
+ initialState() {
254
+ return "idle";
255
+ },
256
+ context({ prop, bindable }) {
257
+ return {
258
+ value: bindable(() => ({
259
+ defaultValue: prop("defaultValue"),
260
+ value: prop("value"),
261
+ onChange(value) {
262
+ prop("onValueChange")?.({ value });
263
+ }
264
+ })),
265
+ focusedValue: bindable(() => ({
266
+ defaultValue: prop("value") || prop("defaultValue"),
267
+ onChange(value) {
268
+ prop("onFocusChange")?.({ focusedValue: value });
269
+ }
270
+ })),
271
+ ssr: bindable(() => ({ defaultValue: true })),
272
+ indicatorTransition: bindable(() => ({ defaultValue: false })),
273
+ indicatorRect: bindable(() => ({
274
+ defaultValue: { left: "0px", top: "0px", width: "0px", height: "0px" }
275
+ }))
276
+ };
277
+ },
278
+ watch({ context, prop, track, action }) {
279
+ track([() => context.get("value")], () => {
280
+ action(["allowIndicatorTransition", "syncIndicatorRect", "syncTabIndex", "navigateIfNeeded"]);
281
+ });
282
+ track([() => prop("dir"), () => prop("orientation")], () => {
283
+ action(["syncIndicatorRect"]);
284
+ });
285
+ },
286
+ on: {
287
+ SET_VALUE: {
288
+ actions: ["setValue"]
289
+ },
290
+ CLEAR_VALUE: {
291
+ actions: ["clearValue"]
292
+ },
293
+ SET_INDICATOR_RECT: {
294
+ actions: ["setIndicatorRect"]
295
+ },
296
+ SYNC_TAB_INDEX: {
297
+ actions: ["syncTabIndex"]
298
+ }
299
+ },
300
+ entry: ["syncIndicatorRect", "syncTabIndex", "syncSsr"],
301
+ exit: ["cleanupObserver"],
302
+ states: {
303
+ idle: {
304
+ on: {
305
+ TAB_FOCUS: {
306
+ target: "focused",
307
+ actions: ["setFocusedValue"]
256
308
  },
257
- ...ctx,
258
- focusedValue: ctx.value ?? null,
259
- ssr: true,
260
- indicatorState: {
261
- rendered: false,
262
- transition: false,
263
- rect: { left: "0px", top: "0px", width: "0px", height: "0px" }
309
+ TAB_CLICK: {
310
+ target: "focused",
311
+ actions: ["setFocusedValue", "setValue"]
264
312
  }
265
- },
266
- watch: {
267
- value: ["allowIndicatorTransition", "syncIndicatorRect", "syncTabIndex", "navigateIfNeeded"],
268
- dir: ["syncIndicatorRect"],
269
- orientation: ["syncIndicatorRect"]
270
- },
313
+ }
314
+ },
315
+ focused: {
271
316
  on: {
272
- SET_VALUE: {
273
- actions: "setValue"
317
+ TAB_CLICK: {
318
+ target: "focused",
319
+ actions: ["setFocusedValue", "setValue"]
274
320
  },
275
- CLEAR_VALUE: {
276
- actions: "clearValue"
321
+ ARROW_PREV: [
322
+ {
323
+ guard: "selectOnFocus",
324
+ actions: ["focusPrevTab", "selectFocusedTab"]
325
+ },
326
+ {
327
+ actions: ["focusPrevTab"]
328
+ }
329
+ ],
330
+ ARROW_NEXT: [
331
+ {
332
+ guard: "selectOnFocus",
333
+ actions: ["focusNextTab", "selectFocusedTab"]
334
+ },
335
+ {
336
+ actions: ["focusNextTab"]
337
+ }
338
+ ],
339
+ HOME: [
340
+ {
341
+ guard: "selectOnFocus",
342
+ actions: ["focusFirstTab", "selectFocusedTab"]
343
+ },
344
+ {
345
+ actions: ["focusFirstTab"]
346
+ }
347
+ ],
348
+ END: [
349
+ {
350
+ guard: "selectOnFocus",
351
+ actions: ["focusLastTab", "selectFocusedTab"]
352
+ },
353
+ {
354
+ actions: ["focusLastTab"]
355
+ }
356
+ ],
357
+ ENTER: {
358
+ guard: not("selectOnFocus"),
359
+ actions: ["selectFocusedTab"]
277
360
  },
278
- SET_INDICATOR_RECT: {
279
- actions: "setIndicatorRect"
361
+ TAB_FOCUS: {
362
+ actions: ["setFocusedValue"]
280
363
  },
281
- SYNC_TAB_INDEX: {
282
- actions: "syncTabIndex"
364
+ TAB_BLUR: {
365
+ target: "idle",
366
+ actions: ["clearFocusedValue"]
283
367
  }
368
+ }
369
+ }
370
+ },
371
+ implementations: {
372
+ guards: {
373
+ selectOnFocus: ({ prop }) => prop("activationMode") === "automatic"
374
+ },
375
+ actions: {
376
+ selectFocusedTab({ context, prop }) {
377
+ domQuery.raf(() => {
378
+ const focusedValue = context.get("focusedValue");
379
+ if (!focusedValue) return;
380
+ const nullable = prop("deselectable") && context.get("value") === focusedValue;
381
+ const value = nullable ? null : focusedValue;
382
+ context.set("value", value);
383
+ });
284
384
  },
285
- created: ["syncFocusedValue"],
286
- entry: ["checkRenderedElements", "syncIndicatorRect", "syncTabIndex", "syncSsr"],
287
- exit: ["cleanupObserver"],
288
- states: {
289
- idle: {
290
- on: {
291
- TAB_FOCUS: {
292
- target: "focused",
293
- actions: "setFocusedValue"
294
- },
295
- TAB_CLICK: {
296
- target: "focused",
297
- actions: ["setFocusedValue", "setValue"]
298
- }
385
+ setFocusedValue({ context, event, flush }) {
386
+ if (event.value == null) return;
387
+ flush(() => {
388
+ context.set("focusedValue", event.value);
389
+ });
390
+ },
391
+ clearFocusedValue({ context }) {
392
+ context.set("focusedValue", null);
393
+ },
394
+ setValue({ context, event, prop }) {
395
+ const nullable = prop("deselectable") && context.get("value") === context.get("focusedValue");
396
+ context.set("value", nullable ? null : event.value);
397
+ },
398
+ clearValue({ context }) {
399
+ context.set("value", null);
400
+ },
401
+ focusFirstTab({ scope }) {
402
+ domQuery.raf(() => {
403
+ getFirstTriggerEl(scope)?.focus();
404
+ });
405
+ },
406
+ focusLastTab({ scope }) {
407
+ domQuery.raf(() => {
408
+ getLastTriggerEl(scope)?.focus();
409
+ });
410
+ },
411
+ focusNextTab({ context, prop, scope, event }) {
412
+ const focusedValue = event.value ?? context.get("focusedValue");
413
+ if (!focusedValue) return;
414
+ const triggerEl = getNextTriggerEl(scope, {
415
+ value: focusedValue,
416
+ loopFocus: prop("loopFocus")
417
+ });
418
+ domQuery.raf(() => {
419
+ if (prop("composite")) {
420
+ triggerEl?.focus();
421
+ } else if (triggerEl?.dataset.value != null) {
422
+ context.set("focusedValue", triggerEl.dataset.value);
299
423
  }
300
- },
301
- focused: {
302
- on: {
303
- TAB_CLICK: {
304
- target: "focused",
305
- actions: ["setFocusedValue", "setValue"]
306
- },
307
- ARROW_PREV: [
308
- {
309
- guard: "selectOnFocus",
310
- actions: ["focusPrevTab", "selectFocusedTab"]
311
- },
312
- {
313
- actions: "focusPrevTab"
314
- }
315
- ],
316
- ARROW_NEXT: [
317
- {
318
- guard: "selectOnFocus",
319
- actions: ["focusNextTab", "selectFocusedTab"]
320
- },
321
- {
322
- actions: "focusNextTab"
323
- }
324
- ],
325
- HOME: [
326
- {
327
- guard: "selectOnFocus",
328
- actions: ["focusFirstTab", "selectFocusedTab"]
329
- },
330
- {
331
- actions: "focusFirstTab"
332
- }
333
- ],
334
- END: [
335
- {
336
- guard: "selectOnFocus",
337
- actions: ["focusLastTab", "selectFocusedTab"]
338
- },
339
- {
340
- actions: "focusLastTab"
341
- }
342
- ],
343
- ENTER: {
344
- guard: not("selectOnFocus"),
345
- actions: "selectFocusedTab"
346
- },
347
- TAB_FOCUS: {
348
- actions: ["setFocusedValue"]
349
- },
350
- TAB_BLUR: {
351
- target: "idle",
352
- actions: "clearFocusedValue"
353
- }
424
+ });
425
+ },
426
+ focusPrevTab({ context, prop, scope, event }) {
427
+ const focusedValue = event.value ?? context.get("focusedValue");
428
+ if (!focusedValue) return;
429
+ const triggerEl = getPrevTriggerEl(scope, {
430
+ value: focusedValue,
431
+ loopFocus: prop("loopFocus")
432
+ });
433
+ domQuery.raf(() => {
434
+ if (prop("composite")) {
435
+ triggerEl?.focus();
436
+ } else if (triggerEl?.dataset.value != null) {
437
+ context.set("focusedValue", triggerEl.dataset.value);
354
438
  }
355
- }
356
- }
357
- },
358
- {
359
- guards: {
360
- selectOnFocus: (ctx2) => ctx2.activationMode === "automatic"
439
+ });
361
440
  },
362
- actions: {
363
- syncFocusedValue(ctx2) {
364
- if (ctx2.value != null && ctx2.focusedValue == null) {
365
- ctx2.focusedValue = ctx2.value;
441
+ syncTabIndex({ context, scope }) {
442
+ domQuery.raf(() => {
443
+ const value = context.get("value");
444
+ if (!value) return;
445
+ const contentEl = getContentEl(scope, value);
446
+ if (!contentEl) return;
447
+ const focusables = domQuery.getFocusables(contentEl);
448
+ if (focusables.length > 0) {
449
+ contentEl.removeAttribute("tabindex");
450
+ } else {
451
+ contentEl.setAttribute("tabindex", "0");
366
452
  }
367
- },
368
- selectFocusedTab(ctx2) {
369
- domQuery.raf(() => {
370
- const nullable = ctx2.deselectable && ctx2.value === ctx2.focusedValue;
371
- const value = nullable ? null : ctx2.focusedValue;
372
- set.value(ctx2, value);
373
- });
374
- },
375
- setFocusedValue(ctx2, evt) {
376
- if (evt.value == null) return;
377
- set.focusedValue(ctx2, evt.value);
378
- },
379
- clearFocusedValue(ctx2) {
380
- set.focusedValue(ctx2, null);
381
- },
382
- setValue(ctx2, evt) {
383
- const nullable = ctx2.deselectable && ctx2.value === ctx2.focusedValue;
384
- const value = nullable ? null : evt.value;
385
- set.value(ctx2, value);
386
- },
387
- clearValue(ctx2) {
388
- set.value(ctx2, null);
389
- },
390
- focusFirstTab(ctx2) {
391
- domQuery.raf(() => {
392
- dom.getFirstTriggerEl(ctx2)?.focus();
393
- });
394
- },
395
- focusLastTab(ctx2) {
396
- domQuery.raf(() => {
397
- dom.getLastTriggerEl(ctx2)?.focus();
398
- });
399
- },
400
- focusNextTab(ctx2) {
401
- if (!ctx2.focusedValue) return;
402
- const triggerEl = dom.getNextTriggerEl(ctx2, ctx2.focusedValue);
403
- domQuery.raf(() => {
404
- if (ctx2.composite) {
405
- triggerEl?.focus();
406
- } else if (triggerEl?.dataset.value != null) {
407
- set.focusedValue(ctx2, triggerEl.dataset.value);
408
- }
409
- });
410
- },
411
- focusPrevTab(ctx2) {
412
- if (!ctx2.focusedValue) return;
413
- const triggerEl = dom.getPrevTriggerEl(ctx2, ctx2.focusedValue);
414
- domQuery.raf(() => {
415
- if (ctx2.composite) {
416
- triggerEl?.focus();
417
- } else if (triggerEl?.dataset.value != null) {
418
- set.focusedValue(ctx2, triggerEl.dataset.value);
419
- }
420
- });
421
- },
422
- checkRenderedElements(ctx2) {
423
- ctx2.indicatorState.rendered = !!dom.getIndicatorEl(ctx2);
424
- },
425
- syncTabIndex(ctx2) {
426
- domQuery.raf(() => {
427
- const contentEl = dom.getSelectedContentEl(ctx2);
428
- if (!contentEl) return;
429
- const focusables = domQuery.getFocusables(contentEl);
430
- if (focusables.length > 0) {
431
- contentEl.removeAttribute("tabindex");
432
- } else {
433
- contentEl.setAttribute("tabindex", "0");
434
- }
435
- });
436
- },
437
- cleanupObserver(ctx2) {
438
- ctx2.indicatorCleanup?.();
439
- },
440
- allowIndicatorTransition(ctx2) {
441
- ctx2.indicatorState.transition = true;
442
- },
443
- setIndicatorRect(ctx2, evt) {
444
- const value = evt.id ?? ctx2.value;
445
- if (!ctx2.indicatorState.rendered || !value) return;
446
- const triggerEl = dom.getTriggerEl(ctx2, value);
447
- if (!triggerEl) return;
448
- ctx2.indicatorState.rect = dom.getRectById(ctx2, value);
453
+ });
454
+ },
455
+ cleanupObserver({ refs }) {
456
+ const cleanup = refs.get("indicatorCleanup");
457
+ if (cleanup) cleanup();
458
+ },
459
+ allowIndicatorTransition({ context }) {
460
+ context.set("indicatorTransition", true);
461
+ },
462
+ setIndicatorRect({ context, event, scope }) {
463
+ const value = event.id ?? context.get("value");
464
+ const indicatorEl = getIndicatorEl(scope);
465
+ if (!indicatorEl || !value) return;
466
+ const triggerEl = getTriggerEl(scope, value);
467
+ if (!triggerEl) return;
468
+ context.set("indicatorRect", getRectById(scope, value));
469
+ domQuery.nextTick(() => {
470
+ context.set("indicatorTransition", false);
471
+ });
472
+ },
473
+ syncSsr({ context }) {
474
+ context.set("ssr", false);
475
+ },
476
+ syncIndicatorRect({ context, refs, scope }) {
477
+ const cleanup = refs.get("indicatorCleanup");
478
+ if (cleanup) cleanup();
479
+ const value = context.get("value");
480
+ if (!value) return;
481
+ const triggerEl = getTriggerEl(scope, value);
482
+ const indicatorEl = getIndicatorEl(scope);
483
+ if (!triggerEl || !indicatorEl) return;
484
+ const exec = () => {
485
+ const rect = getOffsetRect(triggerEl);
486
+ context.set("indicatorRect", resolveRect(rect));
449
487
  domQuery.nextTick(() => {
450
- ctx2.indicatorState.transition = false;
488
+ context.set("indicatorTransition", false);
451
489
  });
452
- },
453
- syncSsr(ctx2) {
454
- ctx2.ssr = false;
455
- },
456
- syncIndicatorRect(ctx2) {
457
- ctx2.indicatorCleanup?.();
458
- const value = ctx2.value;
459
- if (!ctx2.indicatorState.rendered || !value) return;
460
- const triggerEl = dom.getSelectedTriggerEl(ctx2);
461
- if (!triggerEl) return;
462
- ctx2.indicatorCleanup = elementRect.trackElementRect(triggerEl, {
463
- getRect(el) {
464
- return dom.getOffsetRect(el);
465
- },
466
- onChange(rect) {
467
- ctx2.indicatorState.rect = dom.resolveRect(rect);
468
- domQuery.nextTick(() => {
469
- ctx2.indicatorState.transition = false;
470
- });
471
- }
472
- });
473
- },
474
- navigateIfNeeded(ctx2) {
475
- const triggerEl = dom.getSelectedTriggerEl(ctx2);
476
- if (!domQuery.isAnchorElement(triggerEl)) return;
477
- ctx2.navigate({ value: ctx2.value, node: triggerEl });
478
- }
490
+ };
491
+ exec();
492
+ const win = scope.getWin();
493
+ const obs = new win.ResizeObserver(exec);
494
+ obs.observe(triggerEl);
495
+ refs.set("indicatorCleanup", () => obs.disconnect());
496
+ },
497
+ navigateIfNeeded({ context, prop, scope }) {
498
+ const value = context.get("value");
499
+ if (!value) return;
500
+ const triggerEl = getTriggerEl(scope, value);
501
+ if (!domQuery.isAnchorElement(triggerEl)) return;
502
+ prop("navigate")?.({ value, node: triggerEl });
479
503
  }
480
504
  }
481
- );
482
- }
483
- var invoke = {
484
- change: (ctx) => {
485
- if (ctx.value == null) return;
486
- ctx.onValueChange?.({ value: ctx.value });
487
- },
488
- focusChange: (ctx) => {
489
- if (ctx.focusedValue == null) return;
490
- ctx.onFocusChange?.({ focusedValue: ctx.focusedValue });
491
- }
492
- };
493
- var set = {
494
- value: (ctx, value) => {
495
- if (utils.isEqual(value, ctx.value)) return;
496
- ctx.value = value;
497
- invoke.change(ctx);
498
- },
499
- focusedValue: (ctx, value) => {
500
- if (utils.isEqual(value, ctx.focusedValue)) return;
501
- ctx.focusedValue = value;
502
- invoke.focusChange(ctx);
503
505
  }
504
- };
506
+ });
505
507
  var props = types.createProps()([
506
508
  "activationMode",
507
509
  "composite",
@@ -516,7 +518,8 @@ var props = types.createProps()([
516
518
  "onValueChange",
517
519
  "orientation",
518
520
  "translations",
519
- "value"
521
+ "value",
522
+ "defaultValue"
520
523
  ]);
521
524
  var splitProps = utils.createSplitProps(props);
522
525
  var triggerProps = types.createProps()(["disabled", "value"]);