@via-profit/ui-kit 0.9.1 → 0.10.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.
Files changed (57) 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 +21 -0
  7. package/Calendar/CalendarControlButton.d.ts +1 -1
  8. package/Calendar/CalendarWeekDayLabel.js +1 -1
  9. package/Menu/MenuContainer.d.ts +26 -0
  10. package/Menu/MenuContainer.js +40 -17
  11. package/Menu/MenuList.d.ts +4 -0
  12. package/Menu/MenuList.js +3 -3
  13. package/Menu/MenuSeparator.d.ts +8 -0
  14. package/Menu/MenuSeparator.js +29 -0
  15. package/Menu/index.d.ts +1 -0
  16. package/Menu/index.js +1 -0
  17. package/Modal/BaseModal/ModalInner.js +2 -1
  18. package/Pagination/PaginationItem.d.ts +1 -1
  19. package/Stepper/Step.d.ts +51 -0
  20. package/Stepper/Step.js +169 -0
  21. package/Stepper/Stepper.d.ts +49 -0
  22. package/Stepper/Stepper.js +74 -0
  23. package/Stepper/context.d.ts +21 -0
  24. package/Stepper/context.js +12 -0
  25. package/Stepper/index.d.ts +6 -0
  26. package/Stepper/index.js +10 -0
  27. package/Surface/SurfaceSubheader.js +4 -3
  28. package/Tabs/Tab.d.ts +8 -0
  29. package/Tabs/Tab.js +75 -32
  30. package/Tabs/TabList.js +24 -5
  31. package/Tabs/Tabs.d.ts +25 -1
  32. package/Tabs/Tabs.js +5 -1
  33. package/Tabs/context.d.ts +18 -0
  34. package/Tabs/context.js +9 -1
  35. package/Tabs/index.d.ts +1 -1
  36. package/ThemeProvider/index.d.ts +6 -1
  37. package/ThemeProvider/tokens.js +1 -0
  38. package/esm/Badge/BadgeContainer.js +1 -1
  39. package/esm/Badge/BadgeTextWrapper.js +3 -1
  40. package/esm/Button/buttonVariants.js +57 -8
  41. package/esm/Calendar/CalendarWeekDayLabel.js +1 -1
  42. package/esm/Menu/MenuContainer.js +40 -17
  43. package/esm/Menu/MenuList.js +3 -3
  44. package/esm/Menu/MenuSeparator.js +23 -0
  45. package/esm/Menu/index.js +1 -0
  46. package/esm/Modal/BaseModal/ModalInner.js +2 -1
  47. package/esm/Stepper/Step.js +164 -0
  48. package/esm/Stepper/Stepper.js +69 -0
  49. package/esm/Stepper/context.js +6 -0
  50. package/esm/Stepper/index.js +3 -0
  51. package/esm/Surface/SurfaceSubheader.js +4 -3
  52. package/esm/Tabs/Tab.js +77 -34
  53. package/esm/Tabs/TabList.js +24 -5
  54. package/esm/Tabs/Tabs.js +5 -1
  55. package/esm/Tabs/context.js +8 -0
  56. package/esm/ThemeProvider/tokens.js +1 -0
  57. package/package.json +31 -1
package/Tabs/Tabs.js CHANGED
@@ -27,7 +27,7 @@ const StyledTabs = styled_1.default.div `
27
27
  */
28
28
  const escapeId = (value) => value.replace(/[^\w-]/g, char => `_${char.charCodeAt(0).toString(16)}`);
29
29
  const Tabs = (props, ref) => {
30
- const { value, defaultValue, onChange, orientation = 'horizontal', activation = 'auto', fullWidth = false, color, children } = props, nativeProps = __rest(props, ["value", "defaultValue", "onChange", "orientation", "activation", "fullWidth", "color", "children"]);
30
+ const { value, defaultValue, onChange, orientation = 'horizontal', activation = 'auto', fullWidth = false, variant = 'underline', size = 'm', color, children } = props, nativeProps = __rest(props, ["value", "defaultValue", "onChange", "orientation", "activation", "fullWidth", "variant", "size", "color", "children"]);
31
31
  const isControlled = typeof value !== 'undefined';
32
32
  const [internalValue, setInternalValue] = react_1.default.useState(defaultValue !== null && defaultValue !== void 0 ? defaultValue : null);
33
33
  const currentValue = isControlled ? value : internalValue;
@@ -60,6 +60,8 @@ const Tabs = (props, ref) => {
60
60
  orientation,
61
61
  activation,
62
62
  fullWidth,
63
+ variant,
64
+ size,
63
65
  color: $color,
64
66
  getTabId: tabValue => `${baseId}-tab-${escapeId(tabValue)}`,
65
67
  getPanelId: tabValue => `${baseId}-panel-${escapeId(tabValue)}`,
@@ -71,6 +73,8 @@ const Tabs = (props, ref) => {
71
73
  orientation,
72
74
  activation,
73
75
  fullWidth,
76
+ variant,
77
+ size,
74
78
  $color,
75
79
  baseId,
76
80
  ]);
package/Tabs/context.d.ts CHANGED
@@ -1,6 +1,22 @@
1
1
  import React from 'react';
2
2
  import Color from '../Color';
3
3
  export type TabsOrientation = 'horizontal' | 'vertical';
4
+ /**
5
+ * `underline` — the line under the selected tab, `segmented` — the tabs in the tray, the selected one is the plate
6
+ */
7
+ export type TabsVariant = 'underline' | 'segmented';
8
+ /**
9
+ * The height of the tab at the text of 14px: `s` — 32px, `m` — 40px, `l` — 48px
10
+ */
11
+ export type TabsSize = 's' | 'm' | 'l';
12
+ /**
13
+ * The size of the text and the paddings of the tab, in the `em` of the tabs
14
+ */
15
+ export declare const TAB_SIZES: Record<TabsSize, {
16
+ readonly fontSize: string;
17
+ readonly y: string;
18
+ readonly x: string;
19
+ }>;
4
20
  export interface TabsContextValue {
5
21
  /**
6
22
  * The selected tab or `null` if nothing is selected
@@ -15,6 +31,8 @@ export interface TabsContextValue {
15
31
  readonly orientation: TabsOrientation;
16
32
  readonly activation: 'auto' | 'manual';
17
33
  readonly fullWidth: boolean;
34
+ readonly variant: TabsVariant;
35
+ readonly size: TabsSize;
18
36
  readonly color: Color;
19
37
  readonly getTabId: (value: string) => string;
20
38
  readonly getPanelId: (value: string) => string;
package/Tabs/context.js CHANGED
@@ -3,8 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.useTabsContext = exports.TabsContext = void 0;
6
+ exports.useTabsContext = exports.TabsContext = exports.TAB_SIZES = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
+ /**
9
+ * The size of the text and the paddings of the tab, in the `em` of the tabs
10
+ */
11
+ exports.TAB_SIZES = {
12
+ s: { fontSize: '0.857em', y: '0.5em', x: '0.7em' },
13
+ m: { fontSize: '0.929em', y: '0.75em', x: '0.9em' },
14
+ l: { fontSize: '1em', y: '1em', x: '1.05em' },
15
+ };
8
16
  exports.TabsContext = react_1.default.createContext(null);
9
17
  const useTabsContext = (component) => {
10
18
  const context = react_1.default.useContext(exports.TabsContext);
package/Tabs/index.d.ts CHANGED
@@ -6,5 +6,5 @@ export type { TabsProps } from './Tabs';
6
6
  export type { TabProps } from './Tab';
7
7
  export type { TabListProps } from './TabList';
8
8
  export type { TabPanelProps } from './TabPanel';
9
- export type { TabsOrientation } from './context';
9
+ export type { TabsOrientation, TabsVariant, TabsSize } from './context';
10
10
  export default Tabs;
@@ -63,9 +63,14 @@ export interface UIThemeOverrideFocusRing {
63
63
  */
64
64
  export interface UIThemeOverrideElevation {
65
65
  /**
66
- * The elements over the page: Menu, Calendar, Toast, Tooltip, the modal windows
66
+ * The elements over the page: Menu, Calendar, Toast, Tooltip
67
67
  */
68
68
  readonly popup?: string;
69
+ /**
70
+ * The modal windows over the page: Dialog, Drawer, MessageBox, ConfirmBox.
71
+ * Without a value the windows get the `popup` shadow of the theme, then the default shadow of the level
72
+ */
73
+ readonly modal?: string;
69
74
  /**
70
75
  * The cards and the panels on the page: Surface, Accordion, Table
71
76
  */
@@ -31,6 +31,7 @@ exports.themeFocusRing = themeFocusRing;
31
31
  */
32
32
  const defaultElevation = (theme) => ({
33
33
  popup: `0 8px 24px ${theme.color.surface.darken(50).alpha(0.6).toString()}`,
34
+ modal: `0 16px 48px -12px ${theme.color.surface.darken(100).alpha(0.5).toString()}`,
34
35
  surface: `0 8px 32px -13px ${theme.color.surface.darken(100).alpha(0.4).toString()}`,
35
36
  control: `0 1px 3px ${theme.color.surface.darken(100).alpha(0.35).toString()}`,
36
37
  });
@@ -12,7 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import React from 'react';
13
13
  import styled from '@emotion/styled';
14
14
  const StyledBadge = styled.span `
15
- border-radius: ${({ theme }) => theme.shape.radiusFactor * 2.5}em;
15
+ border-radius: ${({ theme }) => theme.shape.radiusFactor * 2}em;
16
16
  font-size: 1em;
17
17
  border-width: 0;
18
18
  outline-style: solid;
@@ -13,7 +13,9 @@ import React from 'react';
13
13
  import styled from '@emotion/styled';
14
14
  const TextWrapper = styled.span `
15
15
  font-size: 0.86em;
16
- padding: 0.3em 0.5em;
16
+ /* the badge is about the height of the line of the text around it, not taller */
17
+ line-height: 1.35;
18
+ padding: 0.15em 0.5em;
17
19
  display: flex;
18
20
  align-items: center;
19
21
  user-select: none;
@@ -37,7 +37,7 @@ const getStandardColors = (color, theme) => {
37
37
  }
38
38
  };
39
39
  /**
40
- * The main color of the outlined and plain variants (text and border)
40
+ * The main color of the outlined, plain and soft variants (text and border)
41
41
  */
42
42
  const getAccentColor = (color, theme) => {
43
43
  switch (true) {
@@ -76,11 +76,23 @@ const standardStyles = ({ $color, $background, disabled, theme }) => css `
76
76
  }
77
77
  `}
78
78
  `;
79
- const outlinedStyles = ({ $color, disabled, theme }) => css `
80
- color: ${disabled ? theme.color.textPrimary.alpha(0.4).toString() : $color.darken(30).toString()};
79
+ /**
80
+ * The border of the neutral outlined button: light, as the lines of the tables. The dark border
81
+ * of the text color makes «Отмена», «Сбросить» and the groups of the filters heavy;
82
+ * the colored outlined buttons keep the border of their color
83
+ */
84
+ const neutralBorder = (theme) => theme.color.textPrimary.alpha(0.18).toString();
85
+ const outlinedStyles = ({ $color, $neutral, disabled, theme }) => css `
86
+ color: ${disabled
87
+ ? theme.color.textPrimary.alpha(0.4).toString()
88
+ : $neutral
89
+ ? $color.toString()
90
+ : $color.darken(30).toString()};
81
91
  border-color: ${disabled
82
92
  ? theme.color.surface.darken(80).alpha(0.4).toString()
83
- : $color.toString()};
93
+ : $neutral
94
+ ? neutralBorder(theme)
95
+ : $color.toString()};
84
96
  background-color: transparent;
85
97
  border-style: solid;
86
98
  border-width: ${CONTROL_BORDER};
@@ -88,10 +100,18 @@ const outlinedStyles = ({ $color, disabled, theme }) => css `
88
100
  ${!disabled &&
89
101
  css `
90
102
  &:hover {
91
- background-color: ${$color.darken(20).alpha(0.1).toString()};
103
+ background-color: ${$neutral
104
+ ? $color.alpha(0.05).toString()
105
+ : $color.darken(20).alpha(0.1).toString()};
106
+ ${$neutral &&
107
+ css `
108
+ border-color: ${theme.color.textPrimary.alpha(0.35).toString()};
109
+ `}
92
110
  }
93
111
  &:active {
94
- background-color: ${$color.darken(50).alpha(0.3).toString()};
112
+ background-color: ${$neutral
113
+ ? $color.alpha(0.12).toString()
114
+ : $color.darken(50).alpha(0.3).toString()};
95
115
  }
96
116
  &:focus-visible {
97
117
  outline-color: ${focusOutlineColor($color, theme)};
@@ -120,6 +140,32 @@ const plainStyles = ({ $color, disabled, theme }) => css `
120
140
  }
121
141
  `}
122
142
  `;
143
+ /**
144
+ * The tonal button: the pale background of its color and the text of the same hue,
145
+ * as the plain button under the cursor. Quieter than the standard button, more visible than the plain one
146
+ */
147
+ const softStyles = ({ $color, disabled, theme }) => css `
148
+ box-shadow: none;
149
+ color: ${disabled ? $color.alpha(0.4).toString() : $color.darken(30).toString()};
150
+ background-color: ${disabled ? $color.alpha(0.05).toString() : $color.alpha(0.1).toString()};
151
+
152
+ ${!disabled &&
153
+ css `
154
+ &:hover {
155
+ color: ${$color.darken(50).toString()};
156
+ background-color: ${$color.alpha(0.18).toString()};
157
+ }
158
+ &:active {
159
+ color: ${$color.darken(80).toString()};
160
+ background-color: ${$color.alpha(0.3).toString()};
161
+ }
162
+ &:focus-visible {
163
+ outline-style: solid;
164
+ outline-width: 0.14em;
165
+ outline-color: ${focusOutlineColor($color, theme)};
166
+ }
167
+ `}
168
+ `;
123
169
  /**
124
170
  * Inside the ButtonGroup all the variants have the same border width, so the buttons have the same size
125
171
  * and the selected (standard) button does not shrink. Two adjacent standard buttons are separated by a thin line
@@ -150,13 +196,16 @@ export const buttonVariantStyles = (params) => {
150
196
  const colors = variant === 'standard'
151
197
  ? getStandardColors(color, theme)
152
198
  : { $color: getAccentColor(color, theme), $background: getAccentColor(color, theme) };
153
- const props = Object.assign(Object.assign({}, colors), { $variant: variant, $group: group, disabled, theme });
199
+ const props = Object.assign(Object.assign({}, colors), { $variant: variant, $group: group, $neutral: typeof color === 'undefined' || color === 'default', disabled,
200
+ theme });
154
201
  return css `
155
202
  ${variant === 'outlined'
156
203
  ? outlinedStyles(props)
157
204
  : variant === 'plain'
158
205
  ? plainStyles(props)
159
- : standardStyles(props)}
206
+ : variant === 'soft'
207
+ ? softStyles(props)
208
+ : standardStyles(props)}
160
209
  ${groupStyles(props)}
161
210
  `;
162
211
  };
@@ -4,7 +4,7 @@ const CalendarWeekDayLabel = styled.div `
4
4
  display: flex;
5
5
  align-items: center;
6
6
  justify-content: center;
7
- font-weight: 200;
7
+ font-weight: 300;
8
8
  font-size: 0.8em;
9
9
  user-select: none;
10
10
  width: 3em;
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import useContext, { actionSetmenuState } from './context';
3
3
  import List from './MenuList';
4
+ import Separator from './MenuSeparator';
4
5
  import Popper from '../Popper';
5
6
  import ClickOutside from '../ClickOutside';
6
7
  const onRequestCloseDefault = () => undefined;
@@ -18,11 +19,27 @@ const isEqual = (a, b) => {
18
19
  return keysA.every(key => Object.prototype.hasOwnProperty.call(b, key) && a[key] === b[key]);
19
20
  };
20
21
  const MenuContainer = React.forwardRef((props, ref) => {
21
- const { items, value, anchorElement = null, overrides, children, closeOutsideClick = true, closeOnAnchorClick = false, isOpen = false, anchorPos = 'bottom', alternativePlacements = ['bottom', 'top'], positionStrategy, autoFlip, viewportMargin, offset, multiple = false, autofocus = true, closeOnSelect = !multiple, onRequestClose = onRequestCloseDefault, onAnchorPosChanged, zIndex, onSelectItem, getOptionSelected, maxWidth, } = props;
22
+ const { items, value, anchorElement = null, overrides, children, closeOutsideClick = true, closeOnAnchorClick = false, isOpen = false, anchorPos = 'bottom', alternativePlacements = ['bottom', 'top'], positionStrategy, autoFlip, viewportMargin, offset, multiple = false, autofocus = true, closeOnSelect = !multiple, onRequestClose = onRequestCloseDefault, onAnchorPosChanged, zIndex, onSelectItem, getOptionSelected, maxWidth, maxHeight, isSeparator, } = props;
22
23
  const overridesMap = React.useMemo(() => ({
23
24
  List: (overrides === null || overrides === void 0 ? void 0 : overrides.List) || List,
24
25
  Popper: (overrides === null || overrides === void 0 ? void 0 : overrides.Popper) || Popper,
25
- }), [overrides === null || overrides === void 0 ? void 0 : overrides.List, overrides === null || overrides === void 0 ? void 0 : overrides.Popper]);
26
+ Separator: (overrides === null || overrides === void 0 ? void 0 : overrides.Separator) || Separator,
27
+ }), [overrides === null || overrides === void 0 ? void 0 : overrides.List, overrides === null || overrides === void 0 ? void 0 : overrides.Popper, overrides === null || overrides === void 0 ? void 0 : overrides.Separator]);
28
+ // The separators are not the options: the keyboard skips them, they are not selected
29
+ const isOption = React.useCallback((index) => index >= 0 &&
30
+ index < items.length &&
31
+ !(typeof isSeparator === 'function' && isSeparator(items[index])), [items, isSeparator]);
32
+ /**
33
+ * The nearest option from the index in the direction (1 — down, -1 — up), -1 when there is none
34
+ */
35
+ const findOption = React.useCallback((from, direction) => {
36
+ for (let index = from; index >= 0 && index < items.length; index += direction) {
37
+ if (isOption(index)) {
38
+ return index;
39
+ }
40
+ }
41
+ return -1;
42
+ }, [items.length, isOption]);
26
43
  const menuListRef = React.useRef(null);
27
44
  const menuPopperRef = React.useRef(null);
28
45
  // Starts as `false` so that a menu mounted in the open state is focused and scrolled too
@@ -96,7 +113,7 @@ const MenuContainer = React.forwardRef((props, ref) => {
96
113
  return;
97
114
  const item = items[index];
98
115
  // Falsy items such as 0 or an empty string are valid options
99
- if (item === undefined)
116
+ if (item === undefined || !isOption(index))
100
117
  return;
101
118
  if (typeof onSelectItem === 'function') {
102
119
  if (multiple) {
@@ -117,7 +134,7 @@ const MenuContainer = React.forwardRef((props, ref) => {
117
134
  onRequestClose();
118
135
  }
119
136
  }
120
- }, [onSelectItem, onRequestClose, compareFunc, closeOnSelect, items, value, multiple]);
137
+ }, [onSelectItem, onRequestClose, compareFunc, closeOnSelect, items, value, multiple, isOption]);
121
138
  const highlightIndex = React.useCallback((index) => {
122
139
  const validIndex = Math.max(-1, Math.min(index, items.length - 1));
123
140
  isKeyboardNavigationRef.current = true;
@@ -127,20 +144,21 @@ const MenuContainer = React.forwardRef((props, ref) => {
127
144
  dispatch(actionSetmenuState({ markedIndex: validIndex, hoveredIndex: -1 }));
128
145
  scrollToIndex(validIndex);
129
146
  }, [dispatch, items.length, scrollToIndex]);
147
+ // Without the previous (next) option the highlight stays where it is
130
148
  const highlightPrevItem = React.useCallback(() => {
131
- const newIndex = markedIndex - 1;
132
- highlightIndex(newIndex >= 0 ? newIndex : 0);
133
- }, [markedIndex, highlightIndex]);
149
+ const newIndex = findOption(markedIndex - 1, -1);
150
+ highlightIndex(newIndex >= 0 ? newIndex : findOption(0, 1));
151
+ }, [markedIndex, highlightIndex, findOption]);
134
152
  const highlightNextItem = React.useCallback(() => {
135
- const newIndex = markedIndex + 1;
136
- highlightIndex(newIndex < items.length ? newIndex : items.length - 1);
137
- }, [markedIndex, items.length, highlightIndex]);
153
+ const newIndex = findOption(markedIndex + 1, 1);
154
+ highlightIndex(newIndex >= 0 ? newIndex : findOption(items.length - 1, -1));
155
+ }, [markedIndex, items.length, highlightIndex, findOption]);
138
156
  const highlightFirstItem = React.useCallback(() => {
139
- highlightIndex(0);
140
- }, [highlightIndex]);
157
+ highlightIndex(findOption(0, 1));
158
+ }, [highlightIndex, findOption]);
141
159
  const highlightLastItem = React.useCallback(() => {
142
- highlightIndex(items.length - 1);
143
- }, [highlightIndex, items.length]);
160
+ highlightIndex(findOption(items.length - 1, -1));
161
+ }, [highlightIndex, items.length, findOption]);
144
162
  const selectHighlightedItem = React.useCallback(() => {
145
163
  if (markedIndex > -1 && markedIndex < items.length) {
146
164
  selectItem(markedIndex);
@@ -329,7 +347,10 @@ const MenuContainer = React.forwardRef((props, ref) => {
329
347
  onAnchorPosChanged(newPlacement);
330
348
  }
331
349
  }, [onAnchorPosChanged]);
332
- const renderedChildren = React.useMemo(() => items.map((item, index) => children({ item, index }, {
350
+ const SeparatorComponent = overridesMap.Separator;
351
+ const renderedChildren = React.useMemo(() => items.map((item, index) =>
352
+ // the separator stays among the children: the index of the child is the index of the item
353
+ !isOption(index) ? (React.createElement(SeparatorComponent, { key: index })) : (children({ item, index }, {
333
354
  key: index,
334
355
  onMouseEnter: itemMouseEnterHandler(index),
335
356
  // mouseenter is skipped during the keyboard navigation, so the item under a moving
@@ -339,8 +360,10 @@ const MenuContainer = React.forwardRef((props, ref) => {
339
360
  onClick: itemClickHandler(index),
340
361
  selected: selectedIndexes.includes(index),
341
362
  hovered: markedIndex === index,
342
- })), [
363
+ }))), [
343
364
  items,
365
+ isOption,
366
+ SeparatorComponent,
344
367
  children,
345
368
  selectedIndexes,
346
369
  markedIndex,
@@ -359,7 +382,7 @@ const MenuContainer = React.forwardRef((props, ref) => {
359
382
  const ListComponent = overridesMap.List;
360
383
  return (React.createElement(ClickOutside, { onOutsideClick: onRequestClose, mouseEvent: isOpen && closeOutsideClick ? 'onMouseDown' : false, ignoreElements: closeOnAnchorClick ? undefined : [anchorElement] },
361
384
  React.createElement(PopperComponent, { isOpen: Boolean(isOpen), ref: menuPopperRef, zIndex: zIndex, anchorPos: anchorPos, alternativePlacements: alternativePlacements, anchorElement: anchorElement, onAnchorPosChanged: onAnchorPosChangedMemo, positionStrategy: positionStrategy, autoFlip: autoFlip, viewportMargin: viewportMargin, offset: offset },
362
- React.createElement(ListComponent, { anchorPos: anchorPos, isOpen: Boolean(isOpen), ref: menuListRef, maxWidth: maxWidth, onKeyDown: listKeydownEvent, onMouseMoveCapture: listMouseMoveHandler, onFocus: listFocusHandler, onBlur: listBlurHandler, role: "listbox", "aria-multiselectable": multiple || undefined }, renderedChildren))));
385
+ React.createElement(ListComponent, { anchorPos: anchorPos, isOpen: Boolean(isOpen), ref: menuListRef, maxWidth: maxWidth, maxHeight: maxHeight, onKeyDown: listKeydownEvent, onMouseMoveCapture: listMouseMoveHandler, onFocus: listFocusHandler, onBlur: listBlurHandler, role: "listbox", "aria-multiselectable": multiple || undefined }, renderedChildren))));
363
386
  });
364
387
  MenuContainer.displayName = 'MenuContainer';
365
388
  export default MenuContainer;
@@ -71,11 +71,11 @@ const StyledMenuList = styled.div `
71
71
  box-sizing: border-box;
72
72
  overflow-y: auto;
73
73
  padding: 0.4em;
74
- max-height: 18em;
74
+ max-height: ${({ $maxHeight = '18em' }) => typeof $maxHeight === 'number' ? `${$maxHeight}px` : $maxHeight};
75
75
  `;
76
76
  export const MenuList = React.forwardRef((props, ref) => {
77
- const { isOpen, anchorPos, maxWidth, children } = props, nativeProps = __rest(props, ["isOpen", "anchorPos", "maxWidth", "children"]);
78
- return (React.createElement(StyledMenuList, Object.assign({ tabIndex: -1, "$isOpen": isOpen, "$maxWidth": maxWidth, "$anchorPos": anchorPos }, nativeProps, { ref: ref }), children));
77
+ const { isOpen, anchorPos, maxWidth, maxHeight, children } = props, nativeProps = __rest(props, ["isOpen", "anchorPos", "maxWidth", "maxHeight", "children"]);
78
+ return (React.createElement(StyledMenuList, Object.assign({ tabIndex: -1, "$isOpen": isOpen, "$maxWidth": maxWidth, "$maxHeight": maxHeight, "$anchorPos": anchorPos }, nativeProps, { ref: ref }), children));
79
79
  });
80
80
  MenuList.displayName = 'MenuList';
81
81
  export default MenuList;
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import styled from '@emotion/styled';
3
+ import { themePadding } from '../ThemeProvider/tokens';
4
+ /**
5
+ * The line between the groups of the items. The space around it separates the groups:
6
+ * a line flush with the items is not seen
7
+ */
8
+ const StyledMenuSeparator = styled.div `
9
+ flex: none;
10
+ height: 1px;
11
+ margin: ${({ theme }) => {
12
+ const { y, x } = themePadding(theme, 'item');
13
+ return `${y} ${x}`;
14
+ }};
15
+ background-color: ${({ theme }) => theme.color.textPrimary.alpha(0.12).toString()};
16
+ `;
17
+ /**
18
+ * The separator of the menu: not an option — it is not highlighted, selected or announced
19
+ * (a listbox may contain the options only, so it is hidden from the screen readers)
20
+ */
21
+ export const MenuSeparator = React.forwardRef((props, ref) => (React.createElement(StyledMenuSeparator, Object.assign({ role: "presentation", "aria-hidden": true }, props, { ref: ref }))));
22
+ MenuSeparator.displayName = 'MenuSeparator';
23
+ export default MenuSeparator;
package/esm/Menu/index.js CHANGED
@@ -4,6 +4,7 @@ import MenuContainer from './MenuContainer';
4
4
  export * from './MenuContainer';
5
5
  export * from './MenuItem';
6
6
  export * from './MenuList';
7
+ export * from './MenuSeparator';
7
8
  const Menu = React.forwardRef((props, ref) => (React.createElement(MenuContextProvider, null,
8
9
  React.createElement(MenuContainer, Object.assign({}, props, { ref: ref })))));
9
10
  Menu.displayName = 'Menu';
@@ -20,7 +20,8 @@ const StyledModalInner = styled.div `
20
20
  pointer-events: all;
21
21
  position: relative;
22
22
  background-color: ${({ theme }) => theme.color.surface.toString()};
23
- box-shadow: ${({ theme }) => elevation(theme, 'popup')};
23
+ /* the themes made before the modal level keep the popup shadow for the windows */
24
+ box-shadow: ${({ theme }) => elevation(theme, 'modal', theme.elevation.popup)};
24
25
  border-radius: ${({ theme }) => theme.shape.radiusFactor * 2}em;
25
26
  margin-top: 2em;
26
27
  transition:
@@ -0,0 +1,164 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import React from 'react';
13
+ import styled from '@emotion/styled';
14
+ import { css } from '@emotion/react';
15
+ import { StepIndexContext, StepperContext } from './context';
16
+ const Container = styled.div `
17
+ position: relative;
18
+ display: flex;
19
+ ${({ $orientation, $alternativeLabel, $last }) => $orientation === 'vertical'
20
+ ? css `
21
+ flex-direction: column;
22
+ `
23
+ : $alternativeLabel
24
+ ? css `
25
+ flex: 1 1 0;
26
+ flex-direction: column;
27
+ align-items: center;
28
+ text-align: center;
29
+ `
30
+ : css `
31
+ flex: ${$last ? '0 0 auto' : '1 1 auto'};
32
+ align-items: center;
33
+ `}
34
+ `;
35
+ const Head = styled.div `
36
+ display: flex;
37
+ align-items: ${({ $alternativeLabel }) => ($alternativeLabel ? 'center' : 'flex-start')};
38
+ flex-direction: ${({ $alternativeLabel }) => ($alternativeLabel ? 'column' : 'row')};
39
+ gap: ${({ $alternativeLabel }) => ($alternativeLabel ? '0.35em' : '0.5em')};
40
+ flex-shrink: 0;
41
+ `;
42
+ /**
43
+ * The circle of the step: the check of the completed one, the number of the others
44
+ */
45
+ const Icon = styled.span `
46
+ position: relative;
47
+ z-index: 1;
48
+ display: inline-flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ flex-shrink: 0;
52
+ box-sizing: border-box;
53
+ width: 1.6em;
54
+ height: 1.6em;
55
+ border-radius: 50%;
56
+ font-size: 1em;
57
+ font-weight: 600;
58
+ ${({ theme, $state, $color, $contrastColor, $status }) => {
59
+ // the attention (warning, error) colors the step that is not completed
60
+ const main = $status ? theme.color[$status] : $color;
61
+ switch ($state) {
62
+ // done: the pale plate of the color and the check of the color
63
+ case 'completed':
64
+ return css `
65
+ background-color: ${$color.alpha(0.15).toString()};
66
+ color: ${$color.toString()};
67
+ `;
68
+ // now: the contrast plate of the color, the border and the number of the color
69
+ case 'active':
70
+ return css `
71
+ border: 2px solid ${main.toString()};
72
+ background-color: ${($status ? theme.color.surface : $contrastColor).toString()};
73
+ color: ${main.toString()};
74
+ `;
75
+ // ahead: pale grey
76
+ default:
77
+ return $status
78
+ ? css `
79
+ border: 1.5px solid ${main.toString()};
80
+ background-color: ${theme.color.surface.toString()};
81
+ color: ${main.darken(20).toString()};
82
+ `
83
+ : css `
84
+ background-color: ${theme.color.textPrimary.alpha(0.06).toString()};
85
+ color: ${theme.color.textSecondary.alpha(0.7).toString()};
86
+ `;
87
+ }
88
+ }}
89
+ & > span {
90
+ font-size: 0.75em;
91
+ }
92
+ & > svg {
93
+ width: 0.85em;
94
+ height: 0.85em;
95
+ }
96
+ `;
97
+ const Label = styled.span `
98
+ display: flex;
99
+ flex-direction: column;
100
+ padding-top: 0.15em;
101
+ line-height: 1.3;
102
+ font-size: 0.929em;
103
+ font-weight: ${({ $state }) => ($state === 'active' ? 600 : 400)};
104
+ color: ${({ theme, $state }) => ($state === 'pending' ? theme.color.textSecondary : theme.color.textPrimary).toString()};
105
+ `;
106
+ const Caption = styled.span `
107
+ font-size: 0.857em;
108
+ font-weight: 400;
109
+ color: ${({ theme }) => theme.color.textSecondary.toString()};
110
+ `;
111
+ /**
112
+ * The line to the next step: accented after the completed step
113
+ */
114
+ const Connector = styled.span `
115
+ background-color: ${({ theme, $done, $color }) => ($done ? $color : theme.color.textPrimary.alpha(0.15)).toString()};
116
+ ${({ $orientation, $alternativeLabel }) => $orientation === 'vertical'
117
+ ? css `
118
+ width: 1.5px;
119
+ min-height: 0.75em;
120
+ margin: 0.25em 0 0.25em calc(0.8em - 0.75px);
121
+ flex: 1 1 auto;
122
+ `
123
+ : $alternativeLabel
124
+ ? css `
125
+ position: absolute;
126
+ top: calc(0.8em - 0.75px);
127
+ left: calc(50% + 1.1em);
128
+ right: calc(-50% + 1.1em);
129
+ height: 1.5px;
130
+ `
131
+ : css `
132
+ flex: 1 1 auto;
133
+ min-width: 1em;
134
+ height: 1.5px;
135
+ margin: 0 0.5em;
136
+ `}
137
+ `;
138
+ const Check = () => (React.createElement("svg", { viewBox: "0 0 24 24", "aria-hidden": true },
139
+ React.createElement("path", { d: "M5 12.5l4.5 4.5L19 7.5", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round" })));
140
+ /**
141
+ * One step of the `<Stepper>`
142
+ */
143
+ const Step = (props, ref) => {
144
+ const { children, caption, completed, status, icon } = props, nativeProps = __rest(props, ["children", "caption", "completed", "status", "icon"]);
145
+ const stepper = React.useContext(StepperContext);
146
+ const { index, last } = React.useContext(StepIndexContext);
147
+ if (!stepper) {
148
+ throw new Error('<Step> should be placed inside <Stepper>');
149
+ }
150
+ const { activeStep, orientation, alternativeLabel, color, contrastColor } = stepper;
151
+ const state = completed || (typeof completed === 'undefined' && index < activeStep)
152
+ ? 'completed'
153
+ : index === activeStep
154
+ ? 'active'
155
+ : 'pending';
156
+ return (React.createElement(Container, Object.assign({ role: "listitem", "aria-current": state === 'active' ? 'step' : undefined }, nativeProps, { "$orientation": orientation, "$alternativeLabel": alternativeLabel, "$last": last, ref: ref }),
157
+ React.createElement(Head, { "$alternativeLabel": alternativeLabel },
158
+ React.createElement(Icon, { "$state": state, "$color": color, "$contrastColor": contrastColor, "$status": status !== null && status !== void 0 ? status : null }, state === 'completed' ? React.createElement(Check, null) : (icon !== null && icon !== void 0 ? icon : React.createElement("span", null, index + 1))),
159
+ React.createElement(Label, { "$state": state },
160
+ children,
161
+ caption && React.createElement(Caption, null, caption))),
162
+ !last && (React.createElement(Connector, { "$orientation": orientation, "$alternativeLabel": alternativeLabel, "$done": state === 'completed', "$color": color }))));
163
+ };
164
+ export default React.forwardRef(Step);