@zag-js/tabs 0.2.5 → 0.2.7

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.
@@ -0,0 +1,186 @@
1
+ import {
2
+ parts
3
+ } from "./chunk-S2KW2DT4.mjs";
4
+ import {
5
+ dom
6
+ } from "./chunk-OIIQ7RFL.mjs";
7
+
8
+ // ../../utilities/dom/src/attrs.ts
9
+ var dataAttr = (guard) => {
10
+ return guard ? "" : void 0;
11
+ };
12
+
13
+ // ../../utilities/dom/src/platform.ts
14
+ var isDom = () => typeof window !== "undefined";
15
+ function getPlatform() {
16
+ var _a;
17
+ const agent = navigator.userAgentData;
18
+ return (_a = agent == null ? void 0 : agent.platform) != null ? _a : navigator.platform;
19
+ }
20
+ var pt = (v) => isDom() && v.test(getPlatform());
21
+ var vn = (v) => isDom() && v.test(navigator.vendor);
22
+ var isSafari = () => isApple() && vn(/apple/i);
23
+ var isApple = () => pt(/mac|iphone|ipad|ipod/i);
24
+
25
+ // ../../utilities/dom/src/keyboard-event.ts
26
+ var rtlKeyMap = {
27
+ ArrowLeft: "ArrowRight",
28
+ ArrowRight: "ArrowLeft"
29
+ };
30
+ var sameKeyMap = {
31
+ Up: "ArrowUp",
32
+ Down: "ArrowDown",
33
+ Esc: "Escape",
34
+ " ": "Space",
35
+ ",": "Comma",
36
+ Left: "ArrowLeft",
37
+ Right: "ArrowRight"
38
+ };
39
+ function getEventKey(event, options = {}) {
40
+ var _a;
41
+ const { dir = "ltr", orientation = "horizontal" } = options;
42
+ let { key } = event;
43
+ key = (_a = sameKeyMap[key]) != null ? _a : key;
44
+ const isRtl = dir === "rtl" && orientation === "horizontal";
45
+ if (isRtl && key in rtlKeyMap) {
46
+ key = rtlKeyMap[key];
47
+ }
48
+ return key;
49
+ }
50
+
51
+ // src/tabs.connect.ts
52
+ function connect(state, send, normalize) {
53
+ const translations = state.context.translations;
54
+ const isFocused = state.matches("focused");
55
+ return {
56
+ value: state.context.value,
57
+ focusedValue: state.context.focusedValue,
58
+ previousValues: Array.from(state.context.previousValues),
59
+ setValue(value) {
60
+ send({ type: "SET_VALUE", value });
61
+ },
62
+ clearValue() {
63
+ send({ type: "CLEAR_VALUE" });
64
+ },
65
+ rootProps: normalize.element({
66
+ ...parts.root.attrs,
67
+ id: dom.getRootId(state.context),
68
+ "data-orientation": state.context.orientation,
69
+ "data-focus": dataAttr(isFocused),
70
+ dir: state.context.dir
71
+ }),
72
+ tablistProps: normalize.element({
73
+ ...parts.tablist.attrs,
74
+ id: dom.getTablistId(state.context),
75
+ role: "tablist",
76
+ "data-focus": dataAttr(isFocused),
77
+ "aria-orientation": state.context.orientation,
78
+ "data-orientation": state.context.orientation,
79
+ "aria-label": translations.tablistLabel,
80
+ onKeyDown(event) {
81
+ const keyMap = {
82
+ ArrowDown() {
83
+ send("ARROW_DOWN");
84
+ },
85
+ ArrowUp() {
86
+ send("ARROW_UP");
87
+ },
88
+ ArrowLeft() {
89
+ send("ARROW_LEFT");
90
+ },
91
+ ArrowRight() {
92
+ send("ARROW_RIGHT");
93
+ },
94
+ Home() {
95
+ send("HOME");
96
+ },
97
+ End() {
98
+ send("END");
99
+ },
100
+ Enter() {
101
+ send({ type: "ENTER", value: state.context.focusedValue });
102
+ }
103
+ };
104
+ let key = getEventKey(event, state.context);
105
+ const exec = keyMap[key];
106
+ if (exec) {
107
+ event.preventDefault();
108
+ exec(event);
109
+ }
110
+ }
111
+ }),
112
+ getTriggerProps(props) {
113
+ const { value, disabled } = props;
114
+ const selected = state.context.value === value;
115
+ return normalize.button({
116
+ ...parts.trigger.attrs,
117
+ role: "tab",
118
+ type: "button",
119
+ disabled,
120
+ "data-orientation": state.context.orientation,
121
+ "data-disabled": dataAttr(disabled),
122
+ "aria-disabled": disabled,
123
+ "data-value": value,
124
+ "aria-selected": selected,
125
+ "data-selected": dataAttr(selected),
126
+ "aria-controls": dom.getContentId(state.context, value),
127
+ "data-ownedby": dom.getTablistId(state.context),
128
+ id: dom.getTriggerId(state.context, value),
129
+ tabIndex: selected ? 0 : -1,
130
+ onFocus() {
131
+ send({ type: "TAB_FOCUS", value });
132
+ },
133
+ onBlur(event) {
134
+ const target = event.relatedTarget;
135
+ if ((target == null ? void 0 : target.getAttribute("role")) !== "tab") {
136
+ send({ type: "TAB_BLUR" });
137
+ }
138
+ },
139
+ onClick(event) {
140
+ if (disabled)
141
+ return;
142
+ if (isSafari()) {
143
+ event.currentTarget.focus();
144
+ }
145
+ send({ type: "TAB_CLICK", value });
146
+ }
147
+ });
148
+ },
149
+ contentGroupProps: normalize.element({
150
+ ...parts.contentGroup.attrs,
151
+ id: dom.getContentGroupId(state.context),
152
+ "data-orientation": state.context.orientation
153
+ }),
154
+ getContentProps({ value }) {
155
+ const selected = state.context.value === value;
156
+ return normalize.element({
157
+ ...parts.content.attrs,
158
+ id: dom.getContentId(state.context, value),
159
+ tabIndex: 0,
160
+ "aria-labelledby": dom.getTriggerId(state.context, value),
161
+ role: "tabpanel",
162
+ "data-ownedby": dom.getTablistId(state.context),
163
+ hidden: !selected
164
+ });
165
+ },
166
+ indicatorProps: normalize.element({
167
+ id: dom.getIndicatorId(state.context),
168
+ ...parts.indicator.attrs,
169
+ "data-orientation": state.context.orientation,
170
+ style: {
171
+ "--transition-duration": "200ms",
172
+ "--transition-property": "left, right, top, bottom, width, height",
173
+ position: "absolute",
174
+ willChange: "var(--transition-property)",
175
+ transitionProperty: "var(--transition-property)",
176
+ transitionDuration: state.context.hasMeasuredRect ? "var(--transition-duration)" : "0ms",
177
+ transitionTimingFunction: "var(--transition-timing-function)",
178
+ ...state.context.indicatorRect
179
+ }
180
+ })
181
+ };
182
+ }
183
+
184
+ export {
185
+ connect
186
+ };
@@ -0,0 +1,145 @@
1
+ // ../../utilities/core/src/array.ts
2
+ var first = (v) => v[0];
3
+ var last = (v) => v[v.length - 1];
4
+
5
+ // ../../utilities/dom/src/query.ts
6
+ function isDocument(el) {
7
+ return el.nodeType === Node.DOCUMENT_NODE;
8
+ }
9
+ function isWindow(value) {
10
+ return (value == null ? void 0 : value.toString()) === "[object Window]";
11
+ }
12
+ function isFrame(element) {
13
+ return element.localName === "iframe";
14
+ }
15
+ function getDocument(el) {
16
+ var _a;
17
+ if (isWindow(el))
18
+ return el.document;
19
+ if (isDocument(el))
20
+ return el;
21
+ return (_a = el == null ? void 0 : el.ownerDocument) != null ? _a : document;
22
+ }
23
+ function defineDomHelpers(helpers) {
24
+ const dom2 = {
25
+ getRootNode: (ctx) => {
26
+ var _a, _b;
27
+ return (_b = (_a = ctx.getRootNode) == null ? void 0 : _a.call(ctx)) != null ? _b : document;
28
+ },
29
+ getDoc: (ctx) => getDocument(dom2.getRootNode(ctx)),
30
+ getWin: (ctx) => {
31
+ var _a;
32
+ return (_a = dom2.getDoc(ctx).defaultView) != null ? _a : window;
33
+ },
34
+ getActiveElement: (ctx) => dom2.getDoc(ctx).activeElement,
35
+ getById: (ctx, id) => dom2.getRootNode(ctx).getElementById(id)
36
+ };
37
+ return {
38
+ ...dom2,
39
+ ...helpers
40
+ };
41
+ }
42
+ function isHTMLElement(v) {
43
+ return typeof v === "object" && (v == null ? void 0 : v.nodeType) === Node.ELEMENT_NODE && typeof (v == null ? void 0 : v.nodeName) === "string";
44
+ }
45
+ function isVisible(el) {
46
+ if (!isHTMLElement(el))
47
+ return false;
48
+ return el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length > 0;
49
+ }
50
+
51
+ // ../../utilities/dom/src/nodelist.ts
52
+ function queryAll(root, selector) {
53
+ var _a;
54
+ return Array.from((_a = root == null ? void 0 : root.querySelectorAll(selector)) != null ? _a : []);
55
+ }
56
+ function itemById(v, id) {
57
+ return v.find((node) => node.id === id);
58
+ }
59
+ function indexOfId(v, id) {
60
+ const item = itemById(v, id);
61
+ return item ? v.indexOf(item) : -1;
62
+ }
63
+ function nextById(v, id, loop = true) {
64
+ let idx = indexOfId(v, id);
65
+ idx = loop ? (idx + 1) % v.length : Math.min(idx + 1, v.length - 1);
66
+ return v[idx];
67
+ }
68
+ function prevById(v, id, loop = true) {
69
+ let idx = indexOfId(v, id);
70
+ if (idx === -1)
71
+ return loop ? v[v.length - 1] : null;
72
+ idx = loop ? (idx - 1 + v.length) % v.length : Math.max(0, idx - 1);
73
+ return v[idx];
74
+ }
75
+
76
+ // src/tabs.dom.ts
77
+ var dom = defineDomHelpers({
78
+ getRootId: (ctx) => {
79
+ var _a, _b;
80
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.root) != null ? _b : `tabs:${ctx.id}`;
81
+ },
82
+ getTablistId: (ctx) => {
83
+ var _a, _b;
84
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.tablist) != null ? _b : `tabs:${ctx.id}:list`;
85
+ },
86
+ getContentId: (ctx, id) => {
87
+ var _a, _b;
88
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.content) != null ? _b : `tabs:${ctx.id}:content-${id}`;
89
+ },
90
+ getContentGroupId: (ctx) => {
91
+ var _a, _b;
92
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.contentGroup) != null ? _b : `tabs:${ctx.id}:content-group`;
93
+ },
94
+ getTriggerId: (ctx, id) => {
95
+ var _a, _b;
96
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.trigger) != null ? _b : `tabs:${ctx.id}:trigger-${id}`;
97
+ },
98
+ getIndicatorId: (ctx) => `tabs:${ctx.id}:indicator`,
99
+ getTablistEl: (ctx) => dom.getById(ctx, dom.getTablistId(ctx)),
100
+ getContentEl: (ctx, id) => dom.getById(ctx, dom.getContentId(ctx, id)),
101
+ getTriggerEl: (ctx, id) => dom.getById(ctx, dom.getTriggerId(ctx, id)),
102
+ getIndicatorEl: (ctx) => dom.getById(ctx, dom.getIndicatorId(ctx)),
103
+ getElements: (ctx) => {
104
+ const ownerId = CSS.escape(dom.getTablistId(ctx));
105
+ const selector = `[role=tab][data-ownedby='${ownerId}']:not([disabled])`;
106
+ return queryAll(dom.getTablistEl(ctx), selector);
107
+ },
108
+ getFirstEl: (ctx) => first(dom.getElements(ctx)),
109
+ getLastEl: (ctx) => last(dom.getElements(ctx)),
110
+ getNextEl: (ctx, id) => nextById(dom.getElements(ctx), dom.getTriggerId(ctx, id), ctx.loop),
111
+ getPrevEl: (ctx, id) => prevById(dom.getElements(ctx), dom.getTriggerId(ctx, id), ctx.loop),
112
+ getActiveContentEl: (ctx) => {
113
+ if (!ctx.value)
114
+ return;
115
+ const id = dom.getContentId(ctx, ctx.value);
116
+ return dom.getById(ctx, id);
117
+ },
118
+ getRectById: (ctx, id) => {
119
+ var _a;
120
+ const empty = {
121
+ offsetLeft: 0,
122
+ offsetTop: 0,
123
+ offsetWidth: 0,
124
+ offsetHeight: 0
125
+ };
126
+ const tab = (_a = itemById(dom.getElements(ctx), dom.getTriggerId(ctx, id))) != null ? _a : empty;
127
+ if (ctx.isVertical) {
128
+ return {
129
+ top: `${tab.offsetTop}px`,
130
+ height: `${tab.offsetHeight}px`
131
+ };
132
+ }
133
+ return {
134
+ left: `${tab.offsetLeft}px`,
135
+ width: `${tab.offsetWidth}px`
136
+ };
137
+ }
138
+ });
139
+
140
+ export {
141
+ isFrame,
142
+ isHTMLElement,
143
+ isVisible,
144
+ dom
145
+ };
@@ -0,0 +1,9 @@
1
+ // src/tabs.anatomy.ts
2
+ import { createAnatomy } from "@zag-js/anatomy";
3
+ var anatomy = createAnatomy("tabs").parts("root", "tablist", "trigger", "contentGroup", "content", "indicator");
4
+ var parts = anatomy.build();
5
+
6
+ export {
7
+ anatomy,
8
+ parts
9
+ };
@@ -0,0 +1,285 @@
1
+ import {
2
+ dom,
3
+ isFrame,
4
+ isHTMLElement,
5
+ isVisible
6
+ } from "./chunk-OIIQ7RFL.mjs";
7
+
8
+ // src/tabs.machine.ts
9
+ import { createMachine, guards } from "@zag-js/core";
10
+
11
+ // ../../utilities/core/src/guard.ts
12
+ var isArray = (v) => Array.isArray(v);
13
+ var isObject = (v) => !(v == null || typeof v !== "object" || isArray(v));
14
+
15
+ // ../../utilities/core/src/object.ts
16
+ function compact(obj) {
17
+ if (obj === void 0)
18
+ return obj;
19
+ return Object.fromEntries(
20
+ Object.entries(obj).filter(([, value]) => value !== void 0).map(([key, value]) => [key, isObject(value) ? compact(value) : value])
21
+ );
22
+ }
23
+
24
+ // ../../utilities/dom/src/focusable.ts
25
+ var focusableSelector = "input:not([type='hidden']):not([disabled]), select:not([disabled]), textarea:not([disabled]), a[href], button:not([disabled]), [tabindex], iframe, object, embed, area[href], audio[controls], video[controls], [contenteditable]:not([contenteditable='false']), details > summary:first-of-type";
26
+ var getFocusables = (container, includeContainer = false) => {
27
+ if (!container)
28
+ return [];
29
+ const elements = Array.from(container.querySelectorAll(focusableSelector));
30
+ const include = includeContainer == true || includeContainer == "if-empty" && elements.length === 0;
31
+ if (include && isHTMLElement(container) && isFocusable(container)) {
32
+ elements.unshift(container);
33
+ }
34
+ const focusableElements = elements.filter(isFocusable);
35
+ focusableElements.forEach((element, i) => {
36
+ if (isFrame(element) && element.contentDocument) {
37
+ const frameBody = element.contentDocument.body;
38
+ focusableElements.splice(i, 1, ...getFocusables(frameBody));
39
+ }
40
+ });
41
+ return focusableElements;
42
+ };
43
+ function isFocusable(element) {
44
+ if (!element)
45
+ return false;
46
+ return element.matches(focusableSelector) && isVisible(element);
47
+ }
48
+
49
+ // ../../utilities/dom/src/next-tick.ts
50
+ function nextTick(fn) {
51
+ const set = /* @__PURE__ */ new Set();
52
+ function raf2(fn2) {
53
+ const id = globalThis.requestAnimationFrame(fn2);
54
+ set.add(() => globalThis.cancelAnimationFrame(id));
55
+ }
56
+ raf2(() => raf2(fn));
57
+ return function cleanup() {
58
+ set.forEach(function(fn2) {
59
+ fn2();
60
+ });
61
+ };
62
+ }
63
+ function raf(fn) {
64
+ const id = globalThis.requestAnimationFrame(fn);
65
+ return function cleanup() {
66
+ globalThis.cancelAnimationFrame(id);
67
+ };
68
+ }
69
+
70
+ // src/tabs.machine.ts
71
+ var { not } = guards;
72
+ function machine(userContext) {
73
+ const ctx = compact(userContext);
74
+ return createMachine(
75
+ {
76
+ initial: "unknown",
77
+ context: {
78
+ dir: "ltr",
79
+ orientation: "horizontal",
80
+ activationMode: "automatic",
81
+ value: null,
82
+ focusedValue: null,
83
+ previousValues: [],
84
+ indicatorRect: { left: "0px", top: "0px", width: "0px", height: "0px" },
85
+ hasMeasuredRect: false,
86
+ isIndicatorRendered: false,
87
+ loop: true,
88
+ translations: {},
89
+ ...ctx
90
+ },
91
+ computed: {
92
+ isHorizontal: (ctx2) => ctx2.orientation === "horizontal",
93
+ isVertical: (ctx2) => ctx2.orientation === "vertical"
94
+ },
95
+ created: ["setPrevSelectedTabs"],
96
+ watch: {
97
+ focusedValue: "invokeOnFocus",
98
+ value: ["invokeOnChange", "setPrevSelectedTabs", "setIndicatorRect", "setContentTabIndex"],
99
+ dir: ["clearMeasured", "setIndicatorRect"]
100
+ },
101
+ on: {
102
+ SET_VALUE: {
103
+ actions: "setValue"
104
+ },
105
+ CLEAR_VALUE: {
106
+ actions: "clearValue"
107
+ }
108
+ },
109
+ states: {
110
+ unknown: {
111
+ on: {
112
+ SETUP: {
113
+ target: "idle",
114
+ actions: ["checkRenderedElements", "setIndicatorRect", "setContentTabIndex"]
115
+ }
116
+ }
117
+ },
118
+ idle: {
119
+ on: {
120
+ TAB_FOCUS: {
121
+ guard: "selectOnFocus",
122
+ target: "focused",
123
+ actions: ["setFocusedValue", "setValue"]
124
+ },
125
+ TAB_CLICK: {
126
+ target: "focused",
127
+ actions: ["setFocusedValue", "setValue"]
128
+ }
129
+ }
130
+ },
131
+ focused: {
132
+ on: {
133
+ TAB_CLICK: {
134
+ target: "focused",
135
+ actions: ["setFocusedValue", "setValue"]
136
+ },
137
+ ARROW_LEFT: {
138
+ guard: "isHorizontal",
139
+ actions: "focusPrevTab"
140
+ },
141
+ ARROW_RIGHT: {
142
+ guard: "isHorizontal",
143
+ actions: "focusNextTab"
144
+ },
145
+ ARROW_UP: {
146
+ guard: "isVertical",
147
+ actions: "focusPrevTab"
148
+ },
149
+ ARROW_DOWN: {
150
+ guard: "isVertical",
151
+ actions: "focusNextTab"
152
+ },
153
+ HOME: {
154
+ actions: "focusFirstTab"
155
+ },
156
+ END: {
157
+ actions: "focusLastTab"
158
+ },
159
+ ENTER: {
160
+ guard: not("selectOnFocus"),
161
+ actions: "setValue"
162
+ },
163
+ TAB_FOCUS: [
164
+ {
165
+ guard: "selectOnFocus",
166
+ actions: ["setFocusedValue", "setValue"]
167
+ },
168
+ { actions: "setFocusedValue" }
169
+ ],
170
+ TAB_BLUR: {
171
+ target: "idle",
172
+ actions: "clearFocusedValue"
173
+ }
174
+ }
175
+ }
176
+ }
177
+ },
178
+ {
179
+ guards: {
180
+ isVertical: (ctx2) => ctx2.isVertical,
181
+ isHorizontal: (ctx2) => ctx2.isHorizontal,
182
+ selectOnFocus: (ctx2) => ctx2.activationMode === "automatic"
183
+ },
184
+ actions: {
185
+ setFocusedValue(ctx2, evt) {
186
+ ctx2.focusedValue = evt.value;
187
+ },
188
+ clearFocusedValue(ctx2) {
189
+ ctx2.focusedValue = null;
190
+ },
191
+ setValue(ctx2, evt) {
192
+ ctx2.value = evt.value;
193
+ },
194
+ clearValue(ctx2) {
195
+ ctx2.value = null;
196
+ },
197
+ invokeOnDelete(ctx2, evt) {
198
+ var _a;
199
+ (_a = ctx2.onDelete) == null ? void 0 : _a.call(ctx2, { value: evt.value });
200
+ },
201
+ focusFirstTab(ctx2) {
202
+ raf(() => {
203
+ var _a;
204
+ return (_a = dom.getFirstEl(ctx2)) == null ? void 0 : _a.focus();
205
+ });
206
+ },
207
+ focusLastTab(ctx2) {
208
+ raf(() => {
209
+ var _a;
210
+ return (_a = dom.getLastEl(ctx2)) == null ? void 0 : _a.focus();
211
+ });
212
+ },
213
+ focusNextTab(ctx2) {
214
+ if (!ctx2.focusedValue)
215
+ return;
216
+ const next = dom.getNextEl(ctx2, ctx2.focusedValue);
217
+ raf(() => next == null ? void 0 : next.focus());
218
+ },
219
+ focusPrevTab(ctx2) {
220
+ if (!ctx2.focusedValue)
221
+ return;
222
+ const prev = dom.getPrevEl(ctx2, ctx2.focusedValue);
223
+ raf(() => prev == null ? void 0 : prev.focus());
224
+ },
225
+ setIndicatorRect(ctx2) {
226
+ nextTick(() => {
227
+ if (!ctx2.isIndicatorRendered || !ctx2.value)
228
+ return;
229
+ ctx2.indicatorRect = dom.getRectById(ctx2, ctx2.value);
230
+ if (ctx2.hasMeasuredRect)
231
+ return;
232
+ nextTick(() => {
233
+ ctx2.hasMeasuredRect = true;
234
+ });
235
+ });
236
+ },
237
+ checkRenderedElements(ctx2) {
238
+ ctx2.isIndicatorRendered = !!dom.getIndicatorEl(ctx2);
239
+ },
240
+ clearMeasured(ctx2) {
241
+ ctx2.hasMeasuredRect = false;
242
+ },
243
+ invokeOnChange(ctx2) {
244
+ var _a;
245
+ (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: ctx2.value });
246
+ },
247
+ invokeOnFocus(ctx2) {
248
+ var _a;
249
+ (_a = ctx2.onFocus) == null ? void 0 : _a.call(ctx2, { value: ctx2.focusedValue });
250
+ },
251
+ setPrevSelectedTabs(ctx2) {
252
+ if (ctx2.value != null) {
253
+ ctx2.previousValues = pushUnique(Array.from(ctx2.previousValues), ctx2.value);
254
+ }
255
+ },
256
+ setContentTabIndex(ctx2) {
257
+ raf(() => {
258
+ const panel = dom.getActiveContentEl(ctx2);
259
+ if (!panel)
260
+ return;
261
+ const focusables = getFocusables(panel);
262
+ if (focusables.length > 0) {
263
+ panel.removeAttribute("tabindex");
264
+ } else {
265
+ panel.setAttribute("tabindex", "0");
266
+ }
267
+ });
268
+ }
269
+ }
270
+ }
271
+ );
272
+ }
273
+ function pushUnique(arr, value) {
274
+ const newArr = arr.slice();
275
+ const index = newArr.indexOf(value);
276
+ if (index > -1) {
277
+ newArr.splice(index, 1);
278
+ }
279
+ newArr.push(value);
280
+ return newArr;
281
+ }
282
+
283
+ export {
284
+ machine
285
+ };