@fluentui/react-tabs 9.8.3 → 9.9.0

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,676 @@
1
+ import { makeStyles, mergeClasses, shorthands } from '@griffel/react';
2
+ import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster';
3
+ import { tokens, typographyStyles } from '@fluentui/react-theme';
4
+ import { useTabAnimatedIndicatorStyles_unstable } from './useTabAnimatedIndicator.styles';
5
+ export const tabClassNames = {
6
+ root: 'fui-Tab',
7
+ icon: 'fui-Tab__icon',
8
+ content: 'fui-Tab__content'
9
+ };
10
+ const reservedSpaceClassNames = {
11
+ content: 'fui-Tab__content--reserved-space'
12
+ };
13
+ // These should match the constants defined in @fluentui/react-icons
14
+ // This package avoids taking a dependency on the icons package for only the constants.
15
+ const iconClassNames = {
16
+ filled: 'fui-Icon-filled',
17
+ regular: 'fui-Icon-regular'
18
+ };
19
+ /**
20
+ * Styles for the root slot
21
+ */ const useRootStyles = makeStyles({
22
+ root: {
23
+ alignItems: 'center',
24
+ display: 'grid',
25
+ flexShrink: 0,
26
+ gridAutoFlow: 'column',
27
+ gridTemplateColumns: 'auto',
28
+ gridTemplateRows: 'auto',
29
+ outlineStyle: 'none',
30
+ position: 'relative'
31
+ },
32
+ button: {
33
+ alignItems: 'center',
34
+ border: 'none',
35
+ borderRadius: tokens.borderRadiusMedium,
36
+ cursor: 'pointer',
37
+ display: 'grid',
38
+ flexShrink: 0,
39
+ gridAutoFlow: 'column',
40
+ gridTemplateColumns: 'auto',
41
+ gridTemplateRows: 'auto',
42
+ fontFamily: tokens.fontFamilyBase,
43
+ lineHeight: tokens.lineHeightBase300,
44
+ outlineStyle: 'none',
45
+ position: 'relative',
46
+ overflow: 'hidden',
47
+ textTransform: 'none'
48
+ },
49
+ horizontal: {
50
+ justifyContent: 'center'
51
+ },
52
+ vertical: {
53
+ justifyContent: 'start'
54
+ },
55
+ smallHorizontal: {
56
+ columnGap: tokens.spacingHorizontalXXS,
57
+ padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalSNudge}`
58
+ },
59
+ smallVertical: {
60
+ // horizontal spacing is deliberate. This is the gap between icon and content.
61
+ columnGap: tokens.spacingHorizontalXXS,
62
+ padding: `${tokens.spacingVerticalXXS} ${tokens.spacingHorizontalSNudge}`
63
+ },
64
+ mediumHorizontal: {
65
+ columnGap: tokens.spacingHorizontalSNudge,
66
+ padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalMNudge}`
67
+ },
68
+ mediumVertical: {
69
+ // horizontal spacing is deliberate. This is the gap between icon and content.
70
+ columnGap: tokens.spacingHorizontalSNudge,
71
+ padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalMNudge}`
72
+ },
73
+ largeHorizontal: {
74
+ columnGap: tokens.spacingHorizontalSNudge,
75
+ padding: `${tokens.spacingVerticalL} ${tokens.spacingHorizontalMNudge}`
76
+ },
77
+ largeVertical: {
78
+ // horizontal spacing is deliberate. This is the gap between icon and content.
79
+ columnGap: tokens.spacingHorizontalSNudge,
80
+ padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalMNudge}`
81
+ },
82
+ transparent: {
83
+ backgroundColor: tokens.colorTransparentBackground,
84
+ ':enabled:hover': {
85
+ backgroundColor: tokens.colorTransparentBackgroundHover
86
+ },
87
+ ':enabled:active': {
88
+ backgroundColor: tokens.colorTransparentBackgroundPressed
89
+ },
90
+ [`& .${tabClassNames.icon}`]: {
91
+ color: tokens.colorNeutralForeground2
92
+ },
93
+ [`:enabled:hover .${tabClassNames.icon}`]: {
94
+ color: tokens.colorNeutralForeground2Hover
95
+ },
96
+ [`:enabled:active .${tabClassNames.icon}`]: {
97
+ color: tokens.colorNeutralForeground2Pressed
98
+ },
99
+ [`& .${tabClassNames.content}`]: {
100
+ color: tokens.colorNeutralForeground2
101
+ },
102
+ [`:enabled:hover .${tabClassNames.content}`]: {
103
+ color: tokens.colorNeutralForeground2Hover
104
+ },
105
+ [`:enabled:active .${tabClassNames.content}`]: {
106
+ color: tokens.colorNeutralForeground2Pressed
107
+ }
108
+ },
109
+ subtle: {
110
+ backgroundColor: tokens.colorSubtleBackground,
111
+ ':enabled:hover': {
112
+ backgroundColor: tokens.colorSubtleBackgroundHover
113
+ },
114
+ ':enabled:active': {
115
+ backgroundColor: tokens.colorSubtleBackgroundPressed
116
+ },
117
+ [`& .${tabClassNames.icon}`]: {
118
+ color: tokens.colorNeutralForeground2
119
+ },
120
+ [`:enabled:hover .${tabClassNames.icon}`]: {
121
+ color: tokens.colorNeutralForeground2Hover
122
+ },
123
+ [`:enabled:active .${tabClassNames.icon}`]: {
124
+ color: tokens.colorNeutralForeground2Pressed
125
+ },
126
+ [`& .${tabClassNames.content}`]: {
127
+ color: tokens.colorNeutralForeground2
128
+ },
129
+ [`:enabled:hover .${tabClassNames.content}`]: {
130
+ color: tokens.colorNeutralForeground2Hover
131
+ },
132
+ [`:enabled:active .${tabClassNames.content}`]: {
133
+ color: tokens.colorNeutralForeground2Pressed
134
+ }
135
+ },
136
+ disabledCursor: {
137
+ cursor: 'not-allowed'
138
+ },
139
+ disabled: {
140
+ backgroundColor: tokens.colorTransparentBackground,
141
+ [`& .${tabClassNames.icon}`]: {
142
+ color: tokens.colorNeutralForegroundDisabled
143
+ },
144
+ [`& .${tabClassNames.content}`]: {
145
+ color: tokens.colorNeutralForegroundDisabled
146
+ }
147
+ },
148
+ selected: {
149
+ [`& .${tabClassNames.icon}`]: {
150
+ color: tokens.colorCompoundBrandForeground1
151
+ },
152
+ [`:enabled:hover .${tabClassNames.icon}`]: {
153
+ color: tokens.colorCompoundBrandForeground1Hover
154
+ },
155
+ [`:enabled:active .${tabClassNames.icon}`]: {
156
+ color: tokens.colorCompoundBrandForeground1Pressed
157
+ },
158
+ [`& .${tabClassNames.content}`]: {
159
+ color: tokens.colorNeutralForeground1
160
+ },
161
+ [`:enabled:hover .${tabClassNames.content}`]: {
162
+ color: tokens.colorNeutralForeground1Hover
163
+ },
164
+ [`:enabled:active .${tabClassNames.content}`]: {
165
+ color: tokens.colorNeutralForeground1Pressed
166
+ }
167
+ }
168
+ });
169
+ const useCircularAppearanceStyles = makeStyles({
170
+ base: {
171
+ borderRadius: tokens.borderRadiusCircular,
172
+ border: `solid ${tokens.strokeWidthThin} ${tokens.colorTransparentStroke}`,
173
+ [`& .${tabClassNames.icon}`]: {
174
+ color: 'inherit'
175
+ },
176
+ [`& .${tabClassNames.content}`]: {
177
+ color: 'inherit'
178
+ }
179
+ },
180
+ medium: {
181
+ paddingBlock: `${tokens.spacingVerticalSNudge}`
182
+ },
183
+ subtle: {
184
+ backgroundColor: tokens.colorSubtleBackground,
185
+ color: tokens.colorNeutralForeground2,
186
+ ':enabled:hover': {
187
+ backgroundColor: tokens.colorSubtleBackgroundHover,
188
+ border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStroke1Hover}`,
189
+ color: tokens.colorNeutralForeground2Hover
190
+ },
191
+ ':enabled:active': {
192
+ backgroundColor: tokens.colorSubtleBackgroundPressed,
193
+ border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStroke1Pressed}`,
194
+ color: tokens.colorNeutralForeground2Pressed
195
+ },
196
+ '@media (forced-colors: active)': {
197
+ border: `solid ${tokens.strokeWidthThin} Canvas`
198
+ }
199
+ },
200
+ subtleSelected: {
201
+ backgroundColor: tokens.colorBrandBackground2,
202
+ border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStroke}`,
203
+ color: tokens.colorBrandForeground2,
204
+ ':enabled:hover': {
205
+ backgroundColor: tokens.colorBrandBackground2Hover,
206
+ border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStrokeHover}`,
207
+ color: tokens.colorBrandForeground2Hover
208
+ },
209
+ ':enabled:active': {
210
+ backgroundColor: tokens.colorBrandBackground2Pressed,
211
+ border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStrokePressed}`,
212
+ color: tokens.colorBrandForeground2Pressed
213
+ },
214
+ '@media (forced-colors: active)': {
215
+ border: `solid ${tokens.strokeWidthThin} Highlight`
216
+ }
217
+ },
218
+ subtleDisabled: {
219
+ backgroundColor: tokens.colorSubtleBackground,
220
+ color: tokens.colorNeutralForegroundDisabled
221
+ },
222
+ subtleDisabledSelected: {
223
+ backgroundColor: tokens.colorNeutralBackgroundDisabled,
224
+ border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStrokeDisabled}`,
225
+ color: tokens.colorNeutralForegroundDisabled
226
+ },
227
+ filled: {
228
+ backgroundColor: tokens.colorNeutralBackground3,
229
+ color: tokens.colorNeutralForeground2,
230
+ ':enabled:hover': {
231
+ backgroundColor: tokens.colorNeutralBackground3Hover,
232
+ color: tokens.colorNeutralForeground2Hover
233
+ },
234
+ ':enabled:active': {
235
+ backgroundColor: tokens.colorNeutralBackground3Pressed,
236
+ color: tokens.colorNeutralForeground2Pressed
237
+ },
238
+ '@media (forced-colors: active)': {
239
+ ':enabled:hover': {
240
+ backgroundColor: 'Highlight',
241
+ forcedColorAdjust: 'none',
242
+ [`& .${tabClassNames.content}`]: {
243
+ color: 'HighlightText'
244
+ },
245
+ [`& .${iconClassNames.filled}`]: {
246
+ color: 'HighlightText'
247
+ },
248
+ [`& .${iconClassNames.regular}`]: {
249
+ color: 'HighlightText'
250
+ }
251
+ }
252
+ }
253
+ },
254
+ filledSelected: {
255
+ backgroundColor: tokens.colorBrandBackground,
256
+ color: tokens.colorNeutralForegroundOnBrand,
257
+ ':enabled:hover': {
258
+ backgroundColor: tokens.colorBrandBackgroundHover,
259
+ color: tokens.colorNeutralForegroundOnBrand
260
+ },
261
+ ':enabled:active': {
262
+ backgroundColor: tokens.colorBrandBackgroundPressed,
263
+ color: tokens.colorNeutralForegroundOnBrand
264
+ },
265
+ '@media (forced-colors: active)': {
266
+ ':enabled': {
267
+ backgroundColor: 'ButtonText',
268
+ [`& .${tabClassNames.content}`]: {
269
+ color: 'ButtonFace',
270
+ forcedColorAdjust: 'none'
271
+ }
272
+ },
273
+ [`:enabled .${tabClassNames.icon}`]: {
274
+ color: 'ButtonFace'
275
+ }
276
+ }
277
+ },
278
+ filledDisabled: {
279
+ backgroundColor: tokens.colorNeutralBackgroundDisabled,
280
+ color: tokens.colorNeutralForegroundDisabled
281
+ },
282
+ filledDisabledSelected: {
283
+ backgroundColor: tokens.colorNeutralBackgroundDisabled,
284
+ border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStrokeDisabled}`,
285
+ color: tokens.colorNeutralForegroundDisabled
286
+ }
287
+ });
288
+ /**
289
+ * Focus styles for the root slot
290
+ */ const useFocusStyles = makeStyles({
291
+ // Tab creates a custom focus indicator because the default focus indicator
292
+ // is applied using an ::after pseudo-element on the root. Since the selection
293
+ // indicator uses an ::after pseudo-element on the root, there is a conflict.
294
+ base: createCustomFocusIndicatorStyle({
295
+ ...shorthands.borderColor('transparent'),
296
+ outlineWidth: tokens.strokeWidthThick,
297
+ outlineColor: 'transparent',
298
+ outlineStyle: 'solid',
299
+ boxShadow: `
300
+ ${tokens.shadow4},
301
+ 0 0 0 ${tokens.strokeWidthThick} ${tokens.colorStrokeFocus2}
302
+ `,
303
+ zIndex: 1
304
+ }, {
305
+ enableOutline: true
306
+ }),
307
+ circular: createCustomFocusIndicatorStyle({
308
+ ...shorthands.borderColor('transparent'),
309
+ outlineWidth: tokens.strokeWidthThick,
310
+ outlineColor: 'transparent',
311
+ outlineStyle: 'solid',
312
+ boxShadow: `
313
+ ${tokens.shadow4},
314
+ 0 0 0 ${tokens.strokeWidthThick} ${tokens.colorStrokeFocus2},
315
+ 0 0 0 ${tokens.strokeWidthThin} ${tokens.colorNeutralStrokeOnBrand} inset
316
+ `,
317
+ zIndex: 1
318
+ }, {
319
+ enableOutline: true
320
+ })
321
+ });
322
+ /** Indicator styles for when pending selection */ const usePendingIndicatorStyles = makeStyles({
323
+ base: {
324
+ ':hover::before': {
325
+ backgroundColor: tokens.colorNeutralStroke1Hover,
326
+ borderRadius: tokens.borderRadiusCircular,
327
+ content: '""',
328
+ position: 'absolute'
329
+ },
330
+ ':active::before': {
331
+ backgroundColor: tokens.colorNeutralStroke1Pressed,
332
+ borderRadius: tokens.borderRadiusCircular,
333
+ content: '""',
334
+ position: 'absolute'
335
+ },
336
+ '@media (forced-colors: active)': {
337
+ ':hover::before': {
338
+ backgroundColor: 'Highlight'
339
+ },
340
+ ':active::before': {
341
+ backgroundColor: 'Highlight'
342
+ }
343
+ }
344
+ },
345
+ disabled: {
346
+ ':hover::before': {
347
+ backgroundColor: tokens.colorTransparentStroke
348
+ },
349
+ ':active::before': {
350
+ backgroundColor: tokens.colorTransparentStroke
351
+ },
352
+ '@media (forced-colors: active)': {
353
+ ':hover::before': {
354
+ backgroundColor: 'transparent'
355
+ },
356
+ ':active::before': {
357
+ backgroundColor: 'transparent'
358
+ }
359
+ }
360
+ },
361
+ smallHorizontal: {
362
+ '::before': {
363
+ bottom: 0,
364
+ height: tokens.strokeWidthThick,
365
+ left: tokens.spacingHorizontalSNudge,
366
+ right: tokens.spacingHorizontalSNudge
367
+ }
368
+ },
369
+ smallVertical: {
370
+ '::before': {
371
+ bottom: tokens.spacingVerticalXS,
372
+ left: 0,
373
+ top: tokens.spacingVerticalXS,
374
+ width: tokens.strokeWidthThicker
375
+ }
376
+ },
377
+ mediumHorizontal: {
378
+ '::before': {
379
+ bottom: 0,
380
+ height: tokens.strokeWidthThicker,
381
+ left: tokens.spacingHorizontalM,
382
+ right: tokens.spacingHorizontalM
383
+ }
384
+ },
385
+ mediumVertical: {
386
+ '::before': {
387
+ bottom: tokens.spacingVerticalS,
388
+ left: 0,
389
+ top: tokens.spacingVerticalS,
390
+ width: tokens.strokeWidthThicker
391
+ }
392
+ },
393
+ largeHorizontal: {
394
+ '::before': {
395
+ bottom: 0,
396
+ height: tokens.strokeWidthThicker,
397
+ left: tokens.spacingHorizontalM,
398
+ right: tokens.spacingHorizontalM
399
+ }
400
+ },
401
+ largeVertical: {
402
+ '::before': {
403
+ bottom: tokens.spacingVerticalMNudge,
404
+ left: 0,
405
+ top: tokens.spacingVerticalMNudge,
406
+ width: tokens.strokeWidthThicker
407
+ }
408
+ }
409
+ });
410
+ const useActiveIndicatorStyles = makeStyles({
411
+ base: {
412
+ '::after': {
413
+ backgroundColor: tokens.colorTransparentStroke,
414
+ borderRadius: tokens.borderRadiusCircular,
415
+ content: '""',
416
+ position: 'absolute'
417
+ }
418
+ },
419
+ selected: {
420
+ '::after': {
421
+ backgroundColor: tokens.colorCompoundBrandStroke
422
+ },
423
+ ':enabled:hover::after': {
424
+ backgroundColor: tokens.colorCompoundBrandStrokeHover
425
+ },
426
+ ':enabled:active::after': {
427
+ backgroundColor: tokens.colorCompoundBrandStrokePressed
428
+ },
429
+ '@media (forced-colors: active)': {
430
+ '::after': {
431
+ backgroundColor: 'ButtonText'
432
+ },
433
+ ':enabled:hover::after': {
434
+ backgroundColor: 'ButtonText'
435
+ },
436
+ ':enabled:active::after': {
437
+ backgroundColor: 'ButtonText'
438
+ }
439
+ }
440
+ },
441
+ disabled: {
442
+ '::after': {
443
+ backgroundColor: tokens.colorNeutralForegroundDisabled
444
+ }
445
+ },
446
+ smallHorizontal: {
447
+ '::after': {
448
+ bottom: 0,
449
+ height: tokens.strokeWidthThick,
450
+ left: tokens.spacingHorizontalSNudge,
451
+ right: tokens.spacingHorizontalSNudge
452
+ }
453
+ },
454
+ smallVertical: {
455
+ '::after': {
456
+ bottom: tokens.spacingVerticalXS,
457
+ left: '0',
458
+ top: tokens.spacingVerticalXS,
459
+ width: tokens.strokeWidthThicker
460
+ }
461
+ },
462
+ mediumHorizontal: {
463
+ '::after': {
464
+ bottom: '0',
465
+ height: tokens.strokeWidthThicker,
466
+ left: tokens.spacingHorizontalM,
467
+ right: tokens.spacingHorizontalM
468
+ }
469
+ },
470
+ mediumVertical: {
471
+ '::after': {
472
+ bottom: tokens.spacingVerticalS,
473
+ left: 0,
474
+ top: tokens.spacingVerticalS,
475
+ width: tokens.strokeWidthThicker
476
+ }
477
+ },
478
+ largeHorizontal: {
479
+ '::after': {
480
+ bottom: 0,
481
+ height: tokens.strokeWidthThicker,
482
+ left: tokens.spacingHorizontalM,
483
+ right: tokens.spacingHorizontalM
484
+ }
485
+ },
486
+ largeVertical: {
487
+ '::after': {
488
+ bottom: tokens.spacingVerticalMNudge,
489
+ left: 0,
490
+ top: tokens.spacingVerticalMNudge,
491
+ width: tokens.strokeWidthThicker
492
+ }
493
+ }
494
+ });
495
+ /**
496
+ * Styles for the icon slot.
497
+ */ const useIconStyles = makeStyles({
498
+ base: {
499
+ gridColumnStart: 1,
500
+ gridRowStart: 1,
501
+ alignItems: 'center',
502
+ display: 'inline-flex',
503
+ justifyContent: 'center',
504
+ overflow: 'hidden',
505
+ [`& .${iconClassNames.filled}`]: {
506
+ display: 'none'
507
+ },
508
+ [`& .${iconClassNames.regular}`]: {
509
+ display: 'inline'
510
+ }
511
+ },
512
+ // per design, the small and medium font sizes are the same.
513
+ // the size prop only affects spacing.
514
+ small: {
515
+ fontSize: '20px',
516
+ height: '20px',
517
+ width: '20px'
518
+ },
519
+ medium: {
520
+ fontSize: '20px',
521
+ height: '20px',
522
+ width: '20px'
523
+ },
524
+ large: {
525
+ fontSize: '24px',
526
+ height: '24px',
527
+ width: '24px'
528
+ },
529
+ selected: {
530
+ [`& .${iconClassNames.filled}`]: {
531
+ display: 'inline'
532
+ },
533
+ [`& .${iconClassNames.regular}`]: {
534
+ display: 'none'
535
+ }
536
+ }
537
+ });
538
+ /**
539
+ * Styles for the content slot (children)
540
+ */ const useContentStyles = makeStyles({
541
+ base: {
542
+ ...typographyStyles.body1,
543
+ overflow: 'hidden',
544
+ // content padding is the same for medium & small, horizontal & vertical
545
+ padding: `${tokens.spacingVerticalNone} ${tokens.spacingHorizontalXXS}`
546
+ },
547
+ selected: {
548
+ ...typographyStyles.body1Strong
549
+ },
550
+ large: {
551
+ ...typographyStyles.body2
552
+ },
553
+ largeSelected: {
554
+ ...typographyStyles.subtitle2
555
+ },
556
+ noIconBefore: {
557
+ gridColumnStart: 1,
558
+ gridRowStart: 1
559
+ },
560
+ iconBefore: {
561
+ gridColumnStart: 2,
562
+ gridRowStart: 1
563
+ },
564
+ placeholder: {
565
+ visibility: 'hidden'
566
+ }
567
+ });
568
+ /**
569
+ * Apply styling to the Tab slots based on the state
570
+ */ export const useTabStyles_unstable = (state)=>{
571
+ 'use no memo';
572
+ useTabIndicatorStyles_unstable(state);
573
+ useTabButtonStyles_unstable(state, state.root);
574
+ useTabContentStyles_unstable(state);
575
+ return state;
576
+ };
577
+ /**
578
+ * Applies styles for the Tab indicator based on its current state.
579
+ *
580
+ * This hook is typically used internally by `useTabStyles_unstable`. You should
581
+ * only use it directly if you're creating a custom `Tab` component.
582
+ *
583
+ * @param state - The `Tab` component's current state
584
+ * @returns The state object with updated button styles
585
+ */ export const useTabIndicatorStyles_unstable = (state)=>{
586
+ 'use no memo';
587
+ const rootStyles = useRootStyles();
588
+ const pendingIndicatorStyles = usePendingIndicatorStyles();
589
+ const activeIndicatorStyles = useActiveIndicatorStyles();
590
+ const { appearance, disabled, selected, size, vertical } = state;
591
+ const classes = [
592
+ tabClassNames.root,
593
+ rootStyles.root
594
+ ];
595
+ if (appearance !== 'subtle-circular' && appearance !== 'filled-circular') {
596
+ classes.push(// pending indicator (before pseudo element)
597
+ pendingIndicatorStyles.base, size === 'small' && (vertical ? pendingIndicatorStyles.smallVertical : pendingIndicatorStyles.smallHorizontal), size === 'medium' && (vertical ? pendingIndicatorStyles.mediumVertical : pendingIndicatorStyles.mediumHorizontal), size === 'large' && (vertical ? pendingIndicatorStyles.largeVertical : pendingIndicatorStyles.largeHorizontal), disabled && pendingIndicatorStyles.disabled, // active indicator (after pseudo element)
598
+ selected && activeIndicatorStyles.base, selected && !disabled && activeIndicatorStyles.selected, selected && size === 'small' && (vertical ? activeIndicatorStyles.smallVertical : activeIndicatorStyles.smallHorizontal), selected && size === 'medium' && (vertical ? activeIndicatorStyles.mediumVertical : activeIndicatorStyles.mediumHorizontal), selected && size === 'large' && (vertical ? activeIndicatorStyles.largeVertical : activeIndicatorStyles.largeHorizontal), selected && disabled && activeIndicatorStyles.disabled);
599
+ }
600
+ state.root.className = mergeClasses(...classes, state.root.className);
601
+ useTabAnimatedIndicatorStyles_unstable(state);
602
+ return state;
603
+ };
604
+ /**
605
+ * Applies styles to the Tab button slot based on its current state.
606
+ *
607
+ * This hook is typically used internally by `useTabStyles_unstable`. You should
608
+ * only use it directly if you're creating a custom `Tab` component.
609
+ *
610
+ * @param state - The Tab component's current state
611
+ * @param slot - The button slot of the Tab component
612
+ * @returns The state object with updated button styles
613
+ */ export const useTabButtonStyles_unstable = (state, slot)=>{
614
+ 'use no memo';
615
+ const rootStyles = useRootStyles();
616
+ const focusStyles = useFocusStyles();
617
+ const circularStyles = useCircularAppearanceStyles();
618
+ const { appearance, disabled, selected, size, vertical } = state;
619
+ const isSubtleCircular = appearance === 'subtle-circular';
620
+ const isFilledCircular = appearance === 'filled-circular';
621
+ const isCircular = isSubtleCircular || isFilledCircular;
622
+ const circularAppearance = [
623
+ circularStyles.base,
624
+ focusStyles.circular,
625
+ // sizes
626
+ size === 'medium' && circularStyles.medium,
627
+ // subtle-circular appearance
628
+ isSubtleCircular && circularStyles.subtle,
629
+ selected && isSubtleCircular && circularStyles.subtleSelected,
630
+ disabled && isSubtleCircular && circularStyles.subtleDisabled,
631
+ selected && disabled && isSubtleCircular && circularStyles.subtleDisabledSelected,
632
+ // filled-circular appearance
633
+ isFilledCircular && circularStyles.filled,
634
+ selected && isFilledCircular && circularStyles.filledSelected,
635
+ disabled && isFilledCircular && circularStyles.filledDisabled,
636
+ selected && disabled && isFilledCircular && circularStyles.filledDisabledSelected
637
+ ];
638
+ const regularAppearance = [
639
+ focusStyles.base,
640
+ !disabled && appearance === 'subtle' && rootStyles.subtle,
641
+ !disabled && appearance === 'transparent' && rootStyles.transparent,
642
+ !disabled && selected && rootStyles.selected,
643
+ disabled && rootStyles.disabled
644
+ ];
645
+ slot.className = mergeClasses(rootStyles.button, // orientation
646
+ vertical ? rootStyles.vertical : rootStyles.horizontal, // size
647
+ size === 'small' && (vertical ? rootStyles.smallVertical : rootStyles.smallHorizontal), size === 'medium' && (vertical ? rootStyles.mediumVertical : rootStyles.mediumHorizontal), size === 'large' && (vertical ? rootStyles.largeVertical : rootStyles.largeHorizontal), ...isCircular ? circularAppearance : regularAppearance, disabled && rootStyles.disabledCursor, slot.className);
648
+ return state;
649
+ };
650
+ /**
651
+ * Applies styles to the Tab content slot based on its current state.
652
+ *
653
+ * This hook is typically used internally by `useTabStyles_unstable`. You should
654
+ * only use it directly if you're creating a custom `Tab` component.
655
+ *
656
+ * @param state - The Tab component's current state
657
+ * @returns The state object with updated content styles
658
+ */ export const useTabContentStyles_unstable = (state)=>{
659
+ 'use no memo';
660
+ const iconStyles = useIconStyles();
661
+ const contentStyles = useContentStyles();
662
+ const { selected, size } = state;
663
+ if (state.icon) {
664
+ state.icon.className = mergeClasses(tabClassNames.icon, iconStyles.base, iconStyles[size], selected && iconStyles.selected, state.icon.className);
665
+ }
666
+ // This needs to be before state.content.className is updated
667
+ if (state.contentReservedSpace) {
668
+ state.contentReservedSpace.className = mergeClasses(reservedSpaceClassNames.content, contentStyles.base, size === 'large' ? contentStyles.largeSelected : contentStyles.selected, state.icon ? contentStyles.iconBefore : contentStyles.noIconBefore, contentStyles.placeholder, state.content.className);
669
+ // FIXME: this is a deprecated API
670
+ // should be removed in the next major version
671
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
672
+ state.contentReservedSpaceClassName = state.contentReservedSpace.className;
673
+ }
674
+ state.content.className = mergeClasses(tabClassNames.content, contentStyles.base, size === 'large' && contentStyles.large, selected && (size === 'large' ? contentStyles.largeSelected : contentStyles.selected), state.icon ? contentStyles.iconBefore : contentStyles.noIconBefore, state.content.className);
675
+ return state;
676
+ };