@via-profit/ui-kit 0.9.1 → 0.10.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.
Files changed (65) hide show
  1. package/Badge/BadgeContainer.js +1 -1
  2. package/Badge/BadgeTextWrapper.js +3 -1
  3. package/Button/buttonVariants.d.ts +1 -1
  4. package/Button/buttonVariants.js +57 -8
  5. package/Button/index.d.ts +4 -4
  6. package/CHANGELOG.md +27 -0
  7. package/Calendar/CalendarControlButton.d.ts +1 -1
  8. package/Calendar/CalendarWeekDayLabel.js +1 -1
  9. package/DatePicker/index.js +3 -1
  10. package/HintPopover/index.js +1 -1
  11. package/Menu/MenuContainer.d.ts +26 -0
  12. package/Menu/MenuContainer.js +45 -20
  13. package/Menu/MenuList.d.ts +4 -0
  14. package/Menu/MenuList.js +3 -3
  15. package/Menu/MenuSeparator.d.ts +8 -0
  16. package/Menu/MenuSeparator.js +29 -0
  17. package/Menu/index.d.ts +1 -0
  18. package/Menu/index.js +1 -0
  19. package/Modal/BaseModal/ModalInner.js +2 -1
  20. package/Pagination/PaginationItem.d.ts +1 -1
  21. package/Selectbox/SelectboxMenu.js +1 -1
  22. package/Stepper/Step.d.ts +51 -0
  23. package/Stepper/Step.js +169 -0
  24. package/Stepper/Stepper.d.ts +49 -0
  25. package/Stepper/Stepper.js +74 -0
  26. package/Stepper/context.d.ts +21 -0
  27. package/Stepper/context.js +12 -0
  28. package/Stepper/index.d.ts +6 -0
  29. package/Stepper/index.js +10 -0
  30. package/Surface/SurfaceSubheader.js +4 -3
  31. package/Tabs/Tab.d.ts +8 -0
  32. package/Tabs/Tab.js +75 -32
  33. package/Tabs/TabList.js +24 -5
  34. package/Tabs/Tabs.d.ts +25 -1
  35. package/Tabs/Tabs.js +5 -1
  36. package/Tabs/context.d.ts +18 -0
  37. package/Tabs/context.js +9 -1
  38. package/Tabs/index.d.ts +1 -1
  39. package/ThemeProvider/index.d.ts +6 -1
  40. package/ThemeProvider/tokens.js +1 -0
  41. package/esm/Badge/BadgeContainer.js +1 -1
  42. package/esm/Badge/BadgeTextWrapper.js +3 -1
  43. package/esm/Button/buttonVariants.js +57 -8
  44. package/esm/Calendar/CalendarWeekDayLabel.js +1 -1
  45. package/esm/DatePicker/index.js +3 -1
  46. package/esm/HintPopover/index.js +1 -1
  47. package/esm/Menu/MenuContainer.js +45 -20
  48. package/esm/Menu/MenuList.js +3 -3
  49. package/esm/Menu/MenuSeparator.js +23 -0
  50. package/esm/Menu/index.js +1 -0
  51. package/esm/Modal/BaseModal/ModalInner.js +2 -1
  52. package/esm/Selectbox/SelectboxMenu.js +1 -1
  53. package/esm/Stepper/Step.js +164 -0
  54. package/esm/Stepper/Stepper.js +69 -0
  55. package/esm/Stepper/context.js +6 -0
  56. package/esm/Stepper/index.js +3 -0
  57. package/esm/Surface/SurfaceSubheader.js +4 -3
  58. package/esm/Tabs/Tab.js +77 -34
  59. package/esm/Tabs/TabList.js +24 -5
  60. package/esm/Tabs/Tabs.js +5 -1
  61. package/esm/Tabs/context.js +8 -0
  62. package/esm/ThemeProvider/tokens.js +1 -0
  63. package/esm/utils/TabManager.js +1 -1
  64. package/package.json +31 -1
  65. package/utils/TabManager.js +1 -1
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ const react_1 = __importDefault(require("react"));
18
+ const styled_1 = __importDefault(require("@emotion/styled"));
19
+ const react_2 = require("@emotion/react");
20
+ const context_1 = require("./context");
21
+ const Container = styled_1.default.div `
22
+ position: relative;
23
+ display: flex;
24
+ ${({ $orientation, $alternativeLabel, $last }) => $orientation === 'vertical'
25
+ ? (0, react_2.css) `
26
+ flex-direction: column;
27
+ `
28
+ : $alternativeLabel
29
+ ? (0, react_2.css) `
30
+ flex: 1 1 0;
31
+ flex-direction: column;
32
+ align-items: center;
33
+ text-align: center;
34
+ `
35
+ : (0, react_2.css) `
36
+ flex: ${$last ? '0 0 auto' : '1 1 auto'};
37
+ align-items: center;
38
+ `}
39
+ `;
40
+ const Head = styled_1.default.div `
41
+ display: flex;
42
+ align-items: ${({ $alternativeLabel }) => ($alternativeLabel ? 'center' : 'flex-start')};
43
+ flex-direction: ${({ $alternativeLabel }) => ($alternativeLabel ? 'column' : 'row')};
44
+ gap: ${({ $alternativeLabel }) => ($alternativeLabel ? '0.35em' : '0.5em')};
45
+ flex-shrink: 0;
46
+ `;
47
+ /**
48
+ * The circle of the step: the check of the completed one, the number of the others
49
+ */
50
+ const Icon = styled_1.default.span `
51
+ position: relative;
52
+ z-index: 1;
53
+ display: inline-flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ flex-shrink: 0;
57
+ box-sizing: border-box;
58
+ width: 1.6em;
59
+ height: 1.6em;
60
+ border-radius: 50%;
61
+ font-size: 1em;
62
+ font-weight: 600;
63
+ ${({ theme, $state, $color, $contrastColor, $status }) => {
64
+ // the attention (warning, error) colors the step that is not completed
65
+ const main = $status ? theme.color[$status] : $color;
66
+ switch ($state) {
67
+ // done: the pale plate of the color and the check of the color
68
+ case 'completed':
69
+ return (0, react_2.css) `
70
+ background-color: ${$color.alpha(0.15).toString()};
71
+ color: ${$color.toString()};
72
+ `;
73
+ // now: the contrast plate of the color, the border and the number of the color
74
+ case 'active':
75
+ return (0, react_2.css) `
76
+ border: 2px solid ${main.toString()};
77
+ background-color: ${($status ? theme.color.surface : $contrastColor).toString()};
78
+ color: ${main.toString()};
79
+ `;
80
+ // ahead: pale grey
81
+ default:
82
+ return $status
83
+ ? (0, react_2.css) `
84
+ border: 1.5px solid ${main.toString()};
85
+ background-color: ${theme.color.surface.toString()};
86
+ color: ${main.darken(20).toString()};
87
+ `
88
+ : (0, react_2.css) `
89
+ background-color: ${theme.color.textPrimary.alpha(0.06).toString()};
90
+ color: ${theme.color.textSecondary.alpha(0.7).toString()};
91
+ `;
92
+ }
93
+ }}
94
+ & > span {
95
+ font-size: 0.75em;
96
+ }
97
+ & > svg {
98
+ width: 0.85em;
99
+ height: 0.85em;
100
+ }
101
+ `;
102
+ const Label = styled_1.default.span `
103
+ display: flex;
104
+ flex-direction: column;
105
+ padding-top: 0.15em;
106
+ line-height: 1.3;
107
+ font-size: 0.929em;
108
+ font-weight: ${({ $state }) => ($state === 'active' ? 600 : 400)};
109
+ color: ${({ theme, $state }) => ($state === 'pending' ? theme.color.textSecondary : theme.color.textPrimary).toString()};
110
+ `;
111
+ const Caption = styled_1.default.span `
112
+ font-size: 0.857em;
113
+ font-weight: 400;
114
+ color: ${({ theme }) => theme.color.textSecondary.toString()};
115
+ `;
116
+ /**
117
+ * The line to the next step: accented after the completed step
118
+ */
119
+ const Connector = styled_1.default.span `
120
+ background-color: ${({ theme, $done, $color }) => ($done ? $color : theme.color.textPrimary.alpha(0.15)).toString()};
121
+ ${({ $orientation, $alternativeLabel }) => $orientation === 'vertical'
122
+ ? (0, react_2.css) `
123
+ width: 1.5px;
124
+ min-height: 0.75em;
125
+ margin: 0.25em 0 0.25em calc(0.8em - 0.75px);
126
+ flex: 1 1 auto;
127
+ `
128
+ : $alternativeLabel
129
+ ? (0, react_2.css) `
130
+ position: absolute;
131
+ top: calc(0.8em - 0.75px);
132
+ left: calc(50% + 1.1em);
133
+ right: calc(-50% + 1.1em);
134
+ height: 1.5px;
135
+ `
136
+ : (0, react_2.css) `
137
+ flex: 1 1 auto;
138
+ min-width: 1em;
139
+ height: 1.5px;
140
+ margin: 0 0.5em;
141
+ `}
142
+ `;
143
+ const Check = () => (react_1.default.createElement("svg", { viewBox: "0 0 24 24", "aria-hidden": true },
144
+ react_1.default.createElement("path", { d: "M5 12.5l4.5 4.5L19 7.5", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round" })));
145
+ /**
146
+ * One step of the `<Stepper>`
147
+ */
148
+ const Step = (props, ref) => {
149
+ const { children, caption, completed, status, icon } = props, nativeProps = __rest(props, ["children", "caption", "completed", "status", "icon"]);
150
+ const stepper = react_1.default.useContext(context_1.StepperContext);
151
+ const { index, last } = react_1.default.useContext(context_1.StepIndexContext);
152
+ if (!stepper) {
153
+ throw new Error('<Step> should be placed inside <Stepper>');
154
+ }
155
+ const { activeStep, orientation, alternativeLabel, color, contrastColor } = stepper;
156
+ const state = completed || (typeof completed === 'undefined' && index < activeStep)
157
+ ? 'completed'
158
+ : index === activeStep
159
+ ? 'active'
160
+ : 'pending';
161
+ return (react_1.default.createElement(Container, Object.assign({ role: "listitem", "aria-current": state === 'active' ? 'step' : undefined }, nativeProps, { "$orientation": orientation, "$alternativeLabel": alternativeLabel, "$last": last, ref: ref }),
162
+ react_1.default.createElement(Head, { "$alternativeLabel": alternativeLabel },
163
+ react_1.default.createElement(Icon, { "$state": state, "$color": color, "$contrastColor": contrastColor, "$status": status !== null && status !== void 0 ? status : null }, state === 'completed' ? react_1.default.createElement(Check, null) : (icon !== null && icon !== void 0 ? icon : react_1.default.createElement("span", null, index + 1))),
164
+ react_1.default.createElement(Label, { "$state": state },
165
+ children,
166
+ caption && react_1.default.createElement(Caption, null, caption))),
167
+ !last && (react_1.default.createElement(Connector, { "$orientation": orientation, "$alternativeLabel": alternativeLabel, "$done": state === 'completed', "$color": color }))));
168
+ };
169
+ exports.default = react_1.default.forwardRef(Step);
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { StepperOrientation } from './context';
3
+ export type StepperProps = React.HTMLAttributes<HTMLDivElement> & {
4
+ /**
5
+ * The current step from 0: the steps before it are completed. `-1` — nothing is started,
6
+ * the number of the steps — everything is completed
7
+ * Default: `0`
8
+ */
9
+ readonly activeStep?: number;
10
+ /**
11
+ * `horizontal` — the steps in a row, `vertical` — in a column
12
+ * Default: `horizontal`
13
+ */
14
+ readonly orientation?: StepperOrientation;
15
+ /**
16
+ * The labels under the icons (the horizontal stepper only): the steps share the width equally
17
+ * Default: false
18
+ */
19
+ readonly alternativeLabel?: boolean;
20
+ /**
21
+ * The color of the completed and the current steps: the primary, secondary name or any CSS color
22
+ * Default: `primary`
23
+ */
24
+ readonly color?: 'primary' | 'secondary' | string;
25
+ };
26
+ declare const _default: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
27
+ /**
28
+ * The current step from 0: the steps before it are completed. `-1` — nothing is started,
29
+ * the number of the steps — everything is completed
30
+ * Default: `0`
31
+ */
32
+ readonly activeStep?: number;
33
+ /**
34
+ * `horizontal` — the steps in a row, `vertical` — in a column
35
+ * Default: `horizontal`
36
+ */
37
+ readonly orientation?: StepperOrientation;
38
+ /**
39
+ * The labels under the icons (the horizontal stepper only): the steps share the width equally
40
+ * Default: false
41
+ */
42
+ readonly alternativeLabel?: boolean;
43
+ /**
44
+ * The color of the completed and the current steps: the primary, secondary name or any CSS color
45
+ * Default: `primary`
46
+ */
47
+ readonly color?: "primary" | "secondary" | string;
48
+ } & React.RefAttributes<HTMLDivElement>>;
49
+ export default _default;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ const react_1 = __importDefault(require("react"));
18
+ const styled_1 = __importDefault(require("@emotion/styled"));
19
+ const react_2 = require("@emotion/react");
20
+ const Color_1 = __importDefault(require("../Color"));
21
+ const context_1 = require("./context");
22
+ const StyledStepper = styled_1.default.div `
23
+ display: flex;
24
+ ${({ $orientation, $alternativeLabel }) => $orientation === 'vertical'
25
+ ? (0, react_2.css) `
26
+ flex-direction: column;
27
+ `
28
+ : (0, react_2.css) `
29
+ flex-direction: row;
30
+ align-items: ${$alternativeLabel ? 'flex-start' : 'center'};
31
+ `}
32
+ `;
33
+ /**
34
+ * The steps of a process: done, the current one, ahead. Children — the `<Step>` elements
35
+ */
36
+ const Stepper = (props, ref) => {
37
+ const { activeStep = 0, orientation = 'horizontal', alternativeLabel = false, color = 'primary', children } = props, nativeProps = __rest(props, ["activeStep", "orientation", "alternativeLabel", "color", "children"]);
38
+ const theme = (0, react_2.useTheme)();
39
+ const [resolvedColor, contrastColor] = react_1.default.useMemo(() => {
40
+ if (color === 'primary') {
41
+ return [theme.color.accentPrimary, theme.color.accentPrimaryContrast];
42
+ }
43
+ if (color === 'secondary') {
44
+ return [theme.color.accentSecondary, theme.color.accentSecondaryContrast];
45
+ }
46
+ try {
47
+ const own = Color_1.default.fromString(color);
48
+ return [own, own.getContrastColor()];
49
+ }
50
+ catch (_a) {
51
+ return [theme.color.accentPrimary, theme.color.accentPrimaryContrast];
52
+ }
53
+ }, [
54
+ color,
55
+ theme.color.accentPrimary,
56
+ theme.color.accentPrimaryContrast,
57
+ theme.color.accentSecondary,
58
+ theme.color.accentSecondaryContrast,
59
+ ]);
60
+ const context = react_1.default.useMemo(() => ({
61
+ activeStep,
62
+ orientation,
63
+ alternativeLabel: orientation === 'horizontal' && alternativeLabel,
64
+ color: resolvedColor,
65
+ contrastColor,
66
+ }), [activeStep, orientation, alternativeLabel, resolvedColor, contrastColor]);
67
+ const steps = react_1.default.Children.toArray(children).filter(react_1.default.isValidElement);
68
+ return (react_1.default.createElement(context_1.StepperContext.Provider, { value: context },
69
+ react_1.default.createElement(StyledStepper, Object.assign({ role: "list", "aria-orientation": orientation }, nativeProps, { "$orientation": orientation, "$alternativeLabel": context.alternativeLabel, ref: ref }), steps.map((step, index) => {
70
+ var _a;
71
+ return (react_1.default.createElement(context_1.StepIndexContext.Provider, { key: (_a = step.key) !== null && _a !== void 0 ? _a : index, value: { index, last: index === steps.length - 1 } }, step));
72
+ }))));
73
+ };
74
+ exports.default = react_1.default.forwardRef(Stepper);
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import Color from '../Color';
3
+ export type StepperOrientation = 'horizontal' | 'vertical';
4
+ export type StepperContextValue = {
5
+ readonly activeStep: number;
6
+ readonly orientation: StepperOrientation;
7
+ readonly alternativeLabel: boolean;
8
+ readonly color: Color;
9
+ /**
10
+ * The plate of the current step: the contrast color of the theme for the accent
11
+ */
12
+ readonly contrastColor: Color;
13
+ };
14
+ export declare const StepperContext: React.Context<StepperContextValue | null>;
15
+ /**
16
+ * The place of the step in the stepper: set by the stepper for every child
17
+ */
18
+ export declare const StepIndexContext: React.Context<{
19
+ readonly index: number;
20
+ readonly last: boolean;
21
+ }>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.StepIndexContext = exports.StepperContext = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ exports.StepperContext = react_1.default.createContext(null);
9
+ /**
10
+ * The place of the step in the stepper: set by the stepper for every child
11
+ */
12
+ exports.StepIndexContext = react_1.default.createContext({ index: 0, last: true });
@@ -0,0 +1,6 @@
1
+ import Stepper from './Stepper';
2
+ export { default as Step } from './Step';
3
+ export type { StepperProps } from './Stepper';
4
+ export type { StepProps, StepState } from './Step';
5
+ export type { StepperOrientation } from './context';
6
+ export default Stepper;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Step = void 0;
7
+ const Stepper_1 = __importDefault(require("./Stepper"));
8
+ var Step_1 = require("./Step");
9
+ Object.defineProperty(exports, "Step", { enumerable: true, get: function () { return __importDefault(Step_1).default; } });
10
+ exports.default = Stepper_1.default;
@@ -19,11 +19,12 @@ const styled_1 = __importDefault(require("@emotion/styled"));
19
19
  const tokens_1 = require("../ThemeProvider/tokens");
20
20
  const StyledHeader = styled_1.default.div `
21
21
  padding: ${({ $noPadding, theme }) => {
22
- const { y, x } = (0, tokens_1.themePadding)(theme, 'container', 0.9);
22
+ const { y, x } = (0, tokens_1.themePadding)(theme, 'container');
23
23
  return $noPadding ? '0' : `calc(${y} / 2) ${x} 0 ${x}`;
24
24
  }};
25
- font-size: 0.9em;
26
- font-weight: 200;
25
+ /* the size of the text, the secondary color and Light: quieter than the text, but not small */
26
+ font-size: 1em;
27
+ font-weight: 300;
27
28
  color: ${({ theme }) => theme.color.textSecondary.toString()};
28
29
  `;
29
30
  const SurfaceSubheader = (props, ref) => {
package/Tabs/Tab.d.ts CHANGED
@@ -4,11 +4,19 @@ export type TabProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'valu
4
4
  * The value of the tab. The panel with the same value is shown when the tab is selected
5
5
  */
6
6
  readonly value: string;
7
+ /**
8
+ * The counter after the label: the badge of the color of the tabs, grey for zero
9
+ */
10
+ readonly counter?: number;
7
11
  };
8
12
  declare const _default: React.ForwardRefExoticComponent<Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "value"> & {
9
13
  /**
10
14
  * The value of the tab. The panel with the same value is shown when the tab is selected
11
15
  */
12
16
  readonly value: string;
17
+ /**
18
+ * The counter after the label: the badge of the color of the tabs, grey for zero
19
+ */
20
+ readonly counter?: number;
13
21
  } & React.RefAttributes<HTMLButtonElement>>;
14
22
  export default _default;
package/Tabs/Tab.js CHANGED
@@ -17,10 +17,70 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  const react_1 = __importDefault(require("react"));
18
18
  const styled_1 = __importDefault(require("@emotion/styled"));
19
19
  const react_2 = require("@emotion/react");
20
+ const Badge_1 = __importDefault(require("../Badge"));
20
21
  const context_1 = require("./context");
21
22
  const tokens_1 = require("../ThemeProvider/tokens");
23
+ const underlineStyles = ({ theme, $color, $selected, $orientation, }) => $orientation === 'vertical'
24
+ ? (0, react_2.css) `
25
+ /* The indicator of the selected tab on the right edge of the list */
26
+ &::after {
27
+ content: '';
28
+ position: absolute;
29
+ top: 0.3em;
30
+ bottom: 0.3em;
31
+ right: -1px;
32
+ width: 2px;
33
+ border-radius: 2px;
34
+ background-color: ${$color.toString()};
35
+ transform: scaleY(${$selected ? 1 : 0});
36
+ transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
37
+ }
38
+ &:hover:not(:disabled) {
39
+ background-color: ${theme.color.textPrimary.alpha(0.05).toString()};
40
+ }
41
+ `
42
+ : (0, react_2.css) `
43
+ /* The hover is the rounded background over the line, the line is under the text only */
44
+ &::before {
45
+ content: '';
46
+ position: absolute;
47
+ inset: 0.2em 0 0.3em 0;
48
+ z-index: -1;
49
+ border-radius: inherit;
50
+ transition: background-color 150ms ease-out;
51
+ }
52
+ &:hover:not(:disabled)::before {
53
+ background-color: ${theme.color.textPrimary.alpha(0.05).toString()};
54
+ }
55
+ &::after {
56
+ content: '';
57
+ position: absolute;
58
+ left: var(--tab-x);
59
+ right: var(--tab-x);
60
+ bottom: 0;
61
+ height: 2px;
62
+ border-radius: 2px;
63
+ background-color: ${$color.toString()};
64
+ transform: scaleX(${$selected ? 1 : 0});
65
+ transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
66
+ }
67
+ `;
68
+ const segmentedStyles = ({ theme, $color, $selected }) => (0, react_2.css) `
69
+ &:hover:not(:disabled) {
70
+ background-color: ${$selected
71
+ ? $color.alpha(0.2).toString()
72
+ : theme.color.textPrimary.alpha(0.05).toString()};
73
+ }
74
+ ${$selected &&
75
+ (0, react_2.css) `
76
+ background-color: ${$color.alpha(0.14).toString()};
77
+ `}
78
+ `;
22
79
  const StyledTab = styled_1.default.button `
80
+ --tab-x: ${({ $size }) => context_1.TAB_SIZES[$size].x};
23
81
  position: relative;
82
+ /* The own stacking context: the hover background lies under the text, over the panel */
83
+ isolation: isolate;
24
84
  display: inline-flex;
25
85
  align-items: center;
26
86
  justify-content: center;
@@ -28,18 +88,17 @@ const StyledTab = styled_1.default.button `
28
88
  flex-shrink: 0;
29
89
  box-sizing: border-box;
30
90
  margin: 0;
31
- padding: ${({ theme }) => {
32
- const { y, x } = (0, tokens_1.themePadding)(theme, 'item');
33
- return `${y} ${x}`;
34
- }};
91
+ padding: ${({ $size }) => `${context_1.TAB_SIZES[$size].y} var(--tab-x)`};
35
92
  border: 0;
93
+ border-radius: ${({ theme }) => theme.shape.radiusFactor * 2}em;
36
94
  background: none;
37
95
  font: inherit;
38
- font-size: 0.9em;
39
- font-weight: 500;
96
+ font-size: ${({ $size }) => context_1.TAB_SIZES[$size].fontSize};
97
+ /* light: the tabs are the navigation, not the headings */
98
+ font-weight: ${({ $selected }) => ($selected ? 500 : 400)};
40
99
  white-space: nowrap;
41
100
  cursor: pointer;
42
- color: ${({ theme, $color, $selected }) => $selected ? $color.toString() : theme.color.textSecondary.toString()};
101
+ color: ${({ theme, $color, $selected }) => $selected ? $color.darken(20).toString() : theme.color.textSecondary.toString()};
43
102
  transition:
44
103
  color 150ms ease-out,
45
104
  background-color 150ms ease-out;
@@ -54,7 +113,7 @@ const StyledTab = styled_1.default.button `
54
113
  `}
55
114
 
56
115
  &:hover:not(:disabled) {
57
- background-color: ${({ theme }) => theme.color.textPrimary.alpha(0.05).toString()};
116
+ color: ${({ theme, $color, $selected }) => $selected ? $color.darken(20).toString() : theme.color.textPrimary.toString()};
58
117
  }
59
118
 
60
119
  &:focus-visible {
@@ -67,31 +126,11 @@ const StyledTab = styled_1.default.button `
67
126
  opacity: 0.45;
68
127
  }
69
128
 
70
- /* The indicator of the selected tab: under the tab or on its right edge */
71
- &::after {
72
- content: '';
73
- position: absolute;
74
- background-color: ${({ $color }) => $color.toString()};
75
- transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
76
- ${({ $orientation, $selected }) => $orientation === 'vertical'
77
- ? (0, react_2.css) `
78
- top: 0;
79
- bottom: 0;
80
- right: -1px;
81
- width: 2px;
82
- transform: scaleY(${$selected ? 1 : 0});
83
- `
84
- : (0, react_2.css) `
85
- left: 0;
86
- right: 0;
87
- bottom: 0;
88
- height: 2px;
89
- transform: scaleX(${$selected ? 1 : 0});
90
- `}
91
- }
129
+ ${props => (props.$variant === 'segmented' ? segmentedStyles(props) : underlineStyles(props))}
92
130
 
93
131
  @media (prefers-reduced-motion: reduce) {
94
132
  &,
133
+ &::before,
95
134
  &::after {
96
135
  transition: none;
97
136
  }
@@ -99,7 +138,7 @@ const StyledTab = styled_1.default.button `
99
138
  ${({ theme }) => (0, tokens_1.themeFocusRing)(theme)}
100
139
  `;
101
140
  const Tab = (props, ref) => {
102
- const { value, children, onClick, disabled } = props, nativeProps = __rest(props, ["value", "children", "onClick", "disabled"]);
141
+ const { value, counter, children, onClick, disabled } = props, nativeProps = __rest(props, ["value", "counter", "children", "onClick", "disabled"]);
103
142
  const context = (0, context_1.useTabsContext)('Tab');
104
143
  const isSelected = context.value === value;
105
144
  const tabRef = react_1.default.useRef(null);
@@ -126,6 +165,10 @@ const Tab = (props, ref) => {
126
165
  context.select(value, event);
127
166
  }
128
167
  };
129
- return (react_1.default.createElement(StyledTab, Object.assign({ type: "button", role: "tab", id: context.getTabId(value), "aria-controls": context.getPanelId(value), "aria-selected": isSelected, tabIndex: isSelected ? 0 : -1, "data-tab-value": value }, nativeProps, { disabled: disabled, onClick: handleClick, "$color": context.color, "$selected": isSelected, "$orientation": context.orientation, "$fullWidth": context.fullWidth, ref: tabRef }), children));
168
+ return (react_1.default.createElement(StyledTab, Object.assign({ type: "button", role: "tab", id: context.getTabId(value), "aria-controls": context.getPanelId(value), "aria-selected": isSelected, tabIndex: isSelected ? 0 : -1, "data-tab-value": value }, nativeProps, { disabled: disabled, onClick: handleClick, "$color": context.color, "$selected": isSelected, "$orientation": context.orientation, "$variant": context.variant, "$size": context.size, "$fullWidth": context.fullWidth, ref: tabRef }),
169
+ children,
170
+ typeof counter === 'number' && (
171
+ // soft: the pale background of the color — the counter does not argue with the label
172
+ react_1.default.createElement(Badge_1.default, { variant: "soft", color: counter > 0 ? context.color.toString() : 'default' }, String(counter)))));
130
173
  };
131
174
  exports.default = react_1.default.forwardRef(Tab);
package/Tabs/TabList.js CHANGED
@@ -21,27 +21,46 @@ const context_1 = require("./context");
21
21
  const StyledList = styled_1.default.div `
22
22
  display: flex;
23
23
  box-sizing: border-box;
24
- ${({ theme, $orientation }) => $orientation === 'vertical'
24
+ gap: 0.25em;
25
+ ${({ $orientation }) => $orientation === 'vertical'
25
26
  ? (0, react_2.css) `
26
27
  flex-direction: column;
27
28
  flex-shrink: 0;
28
- border-right: 1px solid ${theme.color.textPrimary.alpha(0.12).toString()};
29
29
  `
30
30
  : (0, react_2.css) `
31
31
  flex-direction: row;
32
32
  /* Many tabs on the narrow screen scroll instead of breaking the layout */
33
33
  overflow-x: auto;
34
34
  scrollbar-width: none;
35
- border-bottom: 1px solid ${theme.color.textPrimary.alpha(0.12).toString()};
36
35
  &::-webkit-scrollbar {
37
36
  display: none;
38
37
  }
39
38
  `}
39
+ ${({ theme, $orientation, $variant }) => $variant === 'segmented'
40
+ ? (0, react_2.css) `
41
+ /* The tray: the tabs lie in the pale rounded plate */
42
+ ${$orientation === 'horizontal' &&
43
+ (0, react_2.css) `
44
+ align-self: flex-start;
45
+ display: inline-flex;
46
+ max-width: 100%;
47
+ `}
48
+ padding: 0.25em;
49
+ border-radius: ${theme.shape.radiusFactor * 2.5}em;
50
+ background-color: ${theme.color.textPrimary.alpha(0.05).toString()};
51
+ `
52
+ : $orientation === 'vertical'
53
+ ? (0, react_2.css) `
54
+ border-right: 1px solid ${theme.color.textPrimary.alpha(0.1).toString()};
55
+ `
56
+ : (0, react_2.css) `
57
+ border-bottom: 1px solid ${theme.color.textPrimary.alpha(0.1).toString()};
58
+ `}
40
59
  `;
41
60
  const getEnabledTabs = (list) => Array.from(list.querySelectorAll('[role="tab"]')).filter(tab => tab.closest('[role="tablist"]') === list && !tab.hasAttribute('disabled'));
42
61
  const TabList = (props, ref) => {
43
62
  const { children, onKeyDown } = props, nativeProps = __rest(props, ["children", "onKeyDown"]);
44
- const { orientation, activation, value, isUncontrolledEmpty, selectInitial } = (0, context_1.useTabsContext)('TabList');
63
+ const { orientation, variant, activation, value, isUncontrolledEmpty, selectInitial } = (0, context_1.useTabsContext)('TabList');
45
64
  const listRef = react_1.default.useRef(null);
46
65
  react_1.default.useImperativeHandle(ref, () => listRef.current);
47
66
  // The tab order needs one focusable tab. Normally it is the selected one,
@@ -99,6 +118,6 @@ const TabList = (props, ref) => {
99
118
  target.click();
100
119
  }
101
120
  };
102
- return (react_1.default.createElement(StyledList, Object.assign({ role: "tablist", "aria-orientation": orientation }, nativeProps, { onKeyDown: handleKeyDown, "$orientation": orientation, ref: listRef }), children));
121
+ return (react_1.default.createElement(StyledList, Object.assign({ role: "tablist", "aria-orientation": orientation }, nativeProps, { onKeyDown: handleKeyDown, "$orientation": orientation, "$variant": variant, ref: listRef }), children));
103
122
  };
104
123
  exports.default = react_1.default.forwardRef(TabList);
package/Tabs/Tabs.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { TabsOrientation } from './context';
2
+ import { TabsOrientation, TabsSize, TabsVariant } from './context';
3
3
  export type TabsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'> & {
4
4
  /**
5
5
  * The selected tab of the controlled tabs. Pass it together with `onChange`
@@ -32,6 +32,18 @@ export type TabsProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' |
32
32
  * Default: false
33
33
  */
34
34
  readonly fullWidth?: boolean;
35
+ /**
36
+ * `underline`: the line under the text of the selected tab — the tabs of the page, the list, the window.\
37
+ * `segmented`: the tabs in the pale tray, the selected one is the plate — the short switches
38
+ * («Day / Week / Month»)
39
+ * Default: `underline`
40
+ */
41
+ readonly variant?: TabsVariant;
42
+ /**
43
+ * The height of the tab at the text of 14px: `s` — 32px, `m` — 40px, `l` — 48px
44
+ * Default: `m`
45
+ */
46
+ readonly size?: TabsSize;
35
47
  /**
36
48
  * The color of the selected tab: the primary, default, secondary name of the colors or your specified color value
37
49
  * Default: `default`
@@ -70,6 +82,18 @@ declare const _default: React.ForwardRefExoticComponent<Omit<React.HTMLAttribute
70
82
  * Default: false
71
83
  */
72
84
  readonly fullWidth?: boolean;
85
+ /**
86
+ * `underline`: the line under the text of the selected tab — the tabs of the page, the list, the window.\
87
+ * `segmented`: the tabs in the pale tray, the selected one is the plate — the short switches
88
+ * («Day / Week / Month»)
89
+ * Default: `underline`
90
+ */
91
+ readonly variant?: TabsVariant;
92
+ /**
93
+ * The height of the tab at the text of 14px: `s` — 32px, `m` — 40px, `l` — 48px
94
+ * Default: `m`
95
+ */
96
+ readonly size?: TabsSize;
73
97
  /**
74
98
  * The color of the selected tab: the primary, default, secondary name of the colors or your specified color value
75
99
  * Default: `default`