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