@destyler/tabs 0.0.2 → 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 ADDED
@@ -0,0 +1,7 @@
1
+ # @destyler/tabs
2
+
3
+ [![NPM version](https://img.shields.io/npm/v/@destyler/tabs?color=a1b858&tabs=)](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/)
@@ -1,18 +1,383 @@
1
1
  'use strict';
2
2
 
3
- const index = require('./index.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');
11
-
12
-
13
-
14
- exports.DestylerTabsContent = index.DestylerTabsContent;
15
- exports.DestylerTabsIndicator = index.DestylerTabsIndicator;
16
- exports.DestylerTabsList = index.DestylerTabsList;
17
- exports.DestylerTabsRoot = index.DestylerTabsRoot;
18
- exports.DestylerTabsTrigger = index.DestylerTabsTrigger;
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;