@destyler/tabs 0.0.3 → 0.0.4
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/README.md +7 -0
- package/dist/component.cjs +381 -16
- package/dist/component.d.cts +511 -4
- package/dist/component.d.mts +511 -4
- package/dist/component.d.ts +511 -4
- package/dist/component.mjs +371 -8
- package/dist/index.cjs +21 -351
- package/dist/index.d.cts +4 -374
- package/dist/index.d.mts +4 -374
- package/dist/index.d.ts +4 -374
- package/dist/index.mjs +8 -347
- package/dist/resolver.cjs +10 -6
- package/dist/resolver.d.cts +3 -3
- package/dist/resolver.d.mts +3 -3
- package/dist/resolver.d.ts +3 -3
- package/dist/resolver.mjs +9 -5
- package/package.json +17 -19
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @destyler/tabs
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@destyler/tabs)
|
|
4
|
+
|
|
5
|
+
> a set of layered sections of content—known as tab panels—that are displayed one at a time.
|
|
6
|
+
|
|
7
|
+
[GitHub](https://github.com/destyler/destyler) | [Documentation](https://destyler-dev.zeabur.app/)
|
package/dist/component.cjs
CHANGED
|
@@ -1,18 +1,383 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
require('
|
|
5
|
-
require('@destyler/
|
|
6
|
-
require('@destyler/
|
|
7
|
-
require('@destyler/
|
|
8
|
-
require('@destyler/
|
|
9
|
-
require('@destyler/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
const vue = require('vue');
|
|
4
|
+
const composition = require('@destyler/composition');
|
|
5
|
+
const presence = require('@destyler/presence');
|
|
6
|
+
const primitive = require('@destyler/primitive');
|
|
7
|
+
const directives = require('@destyler/directives');
|
|
8
|
+
const shared = require('@destyler/shared');
|
|
9
|
+
const rovingFocus = require('@destyler/roving-focus');
|
|
10
|
+
|
|
11
|
+
function makeTriggerId(baseId, value) {
|
|
12
|
+
return `${baseId}-trigger-${value}`;
|
|
13
|
+
}
|
|
14
|
+
function makeContentId(baseId, value) {
|
|
15
|
+
return `${baseId}-content-${value}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const tabsRootProps = {
|
|
19
|
+
...primitive.primitiveProps,
|
|
20
|
+
/**
|
|
21
|
+
* The controlled value of the tab to activate. Can be bind as `v-model`.
|
|
22
|
+
*/
|
|
23
|
+
modelValue: {
|
|
24
|
+
type: String,
|
|
25
|
+
required: false
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* The value of the tab that should be active when initially rendered. Use when you do not need to control the state of the tabs
|
|
29
|
+
*/
|
|
30
|
+
defaultValue: {
|
|
31
|
+
type: String,
|
|
32
|
+
required: false
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Whether a tab is activated automatically or manually.
|
|
36
|
+
*
|
|
37
|
+
* @default automatic
|
|
38
|
+
*/
|
|
39
|
+
activationMode: {
|
|
40
|
+
type: String,
|
|
41
|
+
required: false,
|
|
42
|
+
default: "automatic"
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* The reading direction of the combobox when applicable.
|
|
46
|
+
*/
|
|
47
|
+
dir: {
|
|
48
|
+
type: String,
|
|
49
|
+
required: false
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* The orientation the tabs are layed out.
|
|
53
|
+
* Mainly so arrow navigation is done accordingly (left & right vs. up & down)
|
|
54
|
+
*
|
|
55
|
+
* @default horizontal
|
|
56
|
+
*/
|
|
57
|
+
orientation: {
|
|
58
|
+
type: String,
|
|
59
|
+
required: false,
|
|
60
|
+
default: "horizontal"
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const tabsRootEmits = {
|
|
64
|
+
/**
|
|
65
|
+
* Event handler called when the value changes
|
|
66
|
+
*/
|
|
67
|
+
"update:modelValue": (_value) => true
|
|
68
|
+
};
|
|
69
|
+
const [injectTabsRootContext, provideTabsRootContext] = shared.createContext("DestylerTabsRoot");
|
|
70
|
+
const TabsRoot = vue.defineComponent({
|
|
71
|
+
name: "DestylerTabsRoot",
|
|
72
|
+
props: tabsRootProps,
|
|
73
|
+
emits: tabsRootEmits,
|
|
74
|
+
setup(props, { emit }) {
|
|
75
|
+
const { orientation, dir: propDir } = vue.toRefs(props);
|
|
76
|
+
const dir = composition.useDirection(propDir);
|
|
77
|
+
composition.useForwardExpose();
|
|
78
|
+
const modelValue = composition.useVModel(props, "modelValue", emit, {
|
|
79
|
+
defaultValue: props.defaultValue,
|
|
80
|
+
passive: props.modelValue === void 0
|
|
81
|
+
});
|
|
82
|
+
const tabsList = vue.ref();
|
|
83
|
+
provideTabsRootContext({
|
|
84
|
+
modelValue,
|
|
85
|
+
changeModelValue: (value) => {
|
|
86
|
+
modelValue.value = value;
|
|
87
|
+
},
|
|
88
|
+
orientation,
|
|
89
|
+
dir,
|
|
90
|
+
activationMode: props.activationMode,
|
|
91
|
+
baseId: composition.useId(),
|
|
92
|
+
tabsList
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
dir,
|
|
96
|
+
orientation
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
render() {
|
|
100
|
+
return vue.h(primitive.Primitive, {
|
|
101
|
+
"as": this.$props.as,
|
|
102
|
+
"asChild": this.$props.asChild,
|
|
103
|
+
"dir": this.dir,
|
|
104
|
+
"data-orientation": this.orientation
|
|
105
|
+
}, () => this.$slots.default?.());
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const tabsContentProps = {
|
|
110
|
+
...primitive.primitiveProps,
|
|
111
|
+
/**
|
|
112
|
+
* A unique value that associates the content with a trigger.
|
|
113
|
+
*/
|
|
114
|
+
value: {
|
|
115
|
+
type: String,
|
|
116
|
+
required: true
|
|
117
|
+
},
|
|
118
|
+
/**
|
|
119
|
+
* Used to force mounting when more control is needed. Useful when
|
|
120
|
+
* controlling animation with Vue animation libraries.
|
|
121
|
+
*/
|
|
122
|
+
forceMount: {
|
|
123
|
+
type: Boolean,
|
|
124
|
+
required: false
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const TabsContent = vue.defineComponent({
|
|
128
|
+
name: "DestylerTabsContent",
|
|
129
|
+
props: tabsContentProps,
|
|
130
|
+
setup(props) {
|
|
131
|
+
const { forwardRef } = composition.useForwardExpose();
|
|
132
|
+
const rootContext = injectTabsRootContext();
|
|
133
|
+
const triggerId = vue.computed(
|
|
134
|
+
() => makeTriggerId(rootContext.baseId, props.value)
|
|
135
|
+
);
|
|
136
|
+
const contentId = vue.computed(
|
|
137
|
+
() => makeContentId(rootContext.baseId, props.value)
|
|
138
|
+
);
|
|
139
|
+
const isSelected = vue.computed(
|
|
140
|
+
() => props.value === rootContext.modelValue.value
|
|
141
|
+
);
|
|
142
|
+
const isMountAnimationPreventedRef = vue.ref(isSelected.value);
|
|
143
|
+
vue.onMounted(() => {
|
|
144
|
+
requestAnimationFrame(() => {
|
|
145
|
+
isMountAnimationPreventedRef.value = false;
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
forwardRef,
|
|
150
|
+
rootContext,
|
|
151
|
+
triggerId,
|
|
152
|
+
contentId,
|
|
153
|
+
isSelected,
|
|
154
|
+
isMountAnimationPreventedRef
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
render() {
|
|
158
|
+
return vue.h(
|
|
159
|
+
presence.Presence,
|
|
160
|
+
{
|
|
161
|
+
present: this.isSelected,
|
|
162
|
+
forceMount: false
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
default: ({ present }) => [
|
|
166
|
+
vue.withDirectives(
|
|
167
|
+
vue.h(
|
|
168
|
+
primitive.Primitive,
|
|
169
|
+
{
|
|
170
|
+
"ref": (el) => this.forwardRef(el),
|
|
171
|
+
"as": this.$props.as,
|
|
172
|
+
"asChild": this.$props.asChild,
|
|
173
|
+
"role": "tabpanel",
|
|
174
|
+
"data-state": this.isSelected ? "active" : "inactive",
|
|
175
|
+
"data-orientation": this.rootContext.orientation.value,
|
|
176
|
+
"aria-labelledby": this.triggerId,
|
|
177
|
+
"hidden": !present.value,
|
|
178
|
+
"tabindex": "0",
|
|
179
|
+
"style": {
|
|
180
|
+
animationDuration: this.isMountAnimationPreventedRef ? "0s" : void 0
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
() => [
|
|
184
|
+
this.$props.forceMount || this.isSelected ? this.$slots.default?.() : null
|
|
185
|
+
]
|
|
186
|
+
),
|
|
187
|
+
[[directives.BindOnceDirective, { id: this.contentId }]]
|
|
188
|
+
)
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const tabsIndicatorProps = {
|
|
196
|
+
...primitive.primitiveProps,
|
|
197
|
+
/**
|
|
198
|
+
* @default button
|
|
199
|
+
*/
|
|
200
|
+
as: {
|
|
201
|
+
...primitive.primitiveProps.as,
|
|
202
|
+
default: "button"
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const TabsIndicator = vue.defineComponent({
|
|
206
|
+
name: "DestylerTabsIndicator",
|
|
207
|
+
props: tabsIndicatorProps,
|
|
208
|
+
setup() {
|
|
209
|
+
const context = injectTabsRootContext();
|
|
210
|
+
composition.useForwardExpose();
|
|
211
|
+
const indicatorStyle = vue.ref({
|
|
212
|
+
size: null,
|
|
213
|
+
position: null
|
|
214
|
+
});
|
|
215
|
+
vue.watch(() => context.modelValue.value, async () => {
|
|
216
|
+
await vue.nextTick();
|
|
217
|
+
updateIndicatorStyle();
|
|
218
|
+
}, { immediate: true });
|
|
219
|
+
composition.useResizeObserver(context.tabsList, updateIndicatorStyle);
|
|
220
|
+
function updateIndicatorStyle() {
|
|
221
|
+
const activeTab = context.tabsList.value?.querySelector('[role="tab"][data-state="active"]');
|
|
222
|
+
if (!activeTab)
|
|
223
|
+
return;
|
|
224
|
+
if (context.orientation.value === "horizontal") {
|
|
225
|
+
indicatorStyle.value = {
|
|
226
|
+
size: activeTab.offsetWidth,
|
|
227
|
+
position: activeTab.offsetLeft
|
|
228
|
+
};
|
|
229
|
+
} else {
|
|
230
|
+
indicatorStyle.value = {
|
|
231
|
+
size: activeTab.offsetHeight,
|
|
232
|
+
position: activeTab.offsetTop
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
indicatorStyle
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
render() {
|
|
241
|
+
if (typeof this.indicatorStyle.size === "number") {
|
|
242
|
+
return vue.h(primitive.Primitive, vue.mergeProps(this.$props, this.$attrs, {
|
|
243
|
+
style: {
|
|
244
|
+
"--destyler-tabs-indicator-size": `${this.indicatorStyle.size}px`,
|
|
245
|
+
"--destyler-tabs-indicator-position": `${this.indicatorStyle.position}px`
|
|
246
|
+
}
|
|
247
|
+
}), () => this.$slots.default?.());
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const tabsListProps = {
|
|
253
|
+
...primitive.primitiveProps,
|
|
254
|
+
/**
|
|
255
|
+
* When `true`, keyboard navigation will loop from last tab to first, and vice versa.
|
|
256
|
+
*/
|
|
257
|
+
loop: {
|
|
258
|
+
type: Boolean,
|
|
259
|
+
required: false,
|
|
260
|
+
default: true
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const TabsList = vue.defineComponent({
|
|
264
|
+
name: "DestylerTabsList",
|
|
265
|
+
props: tabsListProps,
|
|
266
|
+
setup(props) {
|
|
267
|
+
const { loop } = vue.toRefs(props);
|
|
268
|
+
const { forwardRef, currentElement } = composition.useForwardExpose();
|
|
269
|
+
const context = injectTabsRootContext();
|
|
270
|
+
context.tabsList = currentElement;
|
|
271
|
+
return {
|
|
272
|
+
forwardRef,
|
|
273
|
+
loop,
|
|
274
|
+
context
|
|
275
|
+
};
|
|
276
|
+
},
|
|
277
|
+
render() {
|
|
278
|
+
return vue.h(rovingFocus.RovingFocusGroup, {
|
|
279
|
+
asChild: true,
|
|
280
|
+
loop: this.loop,
|
|
281
|
+
dir: this.context.dir.value,
|
|
282
|
+
orientation: this.context.orientation.value
|
|
283
|
+
}, () => vue.h(primitive.Primitive, {
|
|
284
|
+
"ref": (el) => this.forwardRef(el),
|
|
285
|
+
"role": "tablist",
|
|
286
|
+
"asChild": this.$props.asChild,
|
|
287
|
+
"as": this.$props.as,
|
|
288
|
+
"aria-orientation": this.context.orientation.value
|
|
289
|
+
}, {
|
|
290
|
+
default: () => this.$slots.default?.()
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const tabsTriggerProps = {
|
|
296
|
+
...primitive.primitiveProps,
|
|
297
|
+
/**
|
|
298
|
+
* @default button
|
|
299
|
+
*/
|
|
300
|
+
as: {
|
|
301
|
+
...primitive.primitiveProps.as,
|
|
302
|
+
default: "button"
|
|
303
|
+
},
|
|
304
|
+
/**
|
|
305
|
+
* A unique value that associates the trigger with a content.
|
|
306
|
+
*/
|
|
307
|
+
value: {
|
|
308
|
+
type: String,
|
|
309
|
+
required: true
|
|
310
|
+
},
|
|
311
|
+
/**
|
|
312
|
+
* When `true`, prevents the user from interacting with the tab.
|
|
313
|
+
*/
|
|
314
|
+
disabled: {
|
|
315
|
+
type: Boolean,
|
|
316
|
+
required: false
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
const TabsTrigger = vue.defineComponent({
|
|
320
|
+
name: "DestylerTabsTrigger",
|
|
321
|
+
props: tabsTriggerProps,
|
|
322
|
+
setup(props) {
|
|
323
|
+
const { forwardRef } = composition.useForwardExpose();
|
|
324
|
+
const rootContext = injectTabsRootContext();
|
|
325
|
+
const triggerId = vue.computed(() => makeTriggerId(rootContext.baseId, props.value));
|
|
326
|
+
const contentId = vue.computed(() => makeContentId(rootContext.baseId, props.value));
|
|
327
|
+
const isSelected = vue.computed(() => props.value === rootContext.modelValue.value);
|
|
328
|
+
return {
|
|
329
|
+
forwardRef,
|
|
330
|
+
rootContext,
|
|
331
|
+
triggerId,
|
|
332
|
+
contentId,
|
|
333
|
+
isSelected
|
|
334
|
+
};
|
|
335
|
+
},
|
|
336
|
+
render() {
|
|
337
|
+
return vue.h(rovingFocus.RovingFocusItem, {
|
|
338
|
+
asChild: true,
|
|
339
|
+
focusable: !this.$props.disabled,
|
|
340
|
+
active: this.isSelected
|
|
341
|
+
}, () => vue.withDirectives(vue.h(primitive.Primitive, {
|
|
342
|
+
"ref": (el) => this.forwardRef(el),
|
|
343
|
+
"role": "tab",
|
|
344
|
+
"type": this.$props.as === "button" ? "button" : void 0,
|
|
345
|
+
"as": this.$props.as,
|
|
346
|
+
"asChild": this.$props.asChild,
|
|
347
|
+
"aria-selected": this.isSelected ? "true" : "false",
|
|
348
|
+
"aria-controls": this.contentId,
|
|
349
|
+
"data-state": this.isSelected ? "active" : "inactive",
|
|
350
|
+
"disabled": this.$props.disabled,
|
|
351
|
+
"data-disabled": this.$props.disabled ? "" : void 0,
|
|
352
|
+
"data-orientation": this.rootContext.orientation.value,
|
|
353
|
+
"onMousedown": vue.withModifiers((event) => {
|
|
354
|
+
if (!this.$props.disabled && event.ctrlKey === false)
|
|
355
|
+
this.rootContext.changeModelValue(this.$props.value);
|
|
356
|
+
else
|
|
357
|
+
event.preventDefault();
|
|
358
|
+
}, ["left"]),
|
|
359
|
+
"onKeydown": vue.withModifiers(() => {
|
|
360
|
+
this.rootContext.changeModelValue(this.$props.value);
|
|
361
|
+
}, ["enter", "space"]),
|
|
362
|
+
"onFocus": () => {
|
|
363
|
+
const isAutomaticActivation = this.rootContext.activationMode !== "manual";
|
|
364
|
+
if (!this.isSelected && !this.$props.disabled && isAutomaticActivation)
|
|
365
|
+
this.rootContext.changeModelValue(this.$props.value);
|
|
366
|
+
}
|
|
367
|
+
}, () => this.$slots.default?.()), [
|
|
368
|
+
[directives.BindOnceDirective, { id: this.triggerId }]
|
|
369
|
+
]));
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
exports.TabsContent = TabsContent;
|
|
374
|
+
exports.TabsIndicator = TabsIndicator;
|
|
375
|
+
exports.TabsList = TabsList;
|
|
376
|
+
exports.TabsRoot = TabsRoot;
|
|
377
|
+
exports.TabsTrigger = TabsTrigger;
|
|
378
|
+
exports.tabsContentProps = tabsContentProps;
|
|
379
|
+
exports.tabsIndicatorProps = tabsIndicatorProps;
|
|
380
|
+
exports.tabsListProps = tabsListProps;
|
|
381
|
+
exports.tabsRootEmits = tabsRootEmits;
|
|
382
|
+
exports.tabsRootProps = tabsRootProps;
|
|
383
|
+
exports.tabsTriggerProps = tabsTriggerProps;
|