From 58e64feb60d3c09bb891c0f50c4f819885ad07be Mon Sep 17 00:00:00 2001 From: Mikhail Shatikhin Date: Wed, 6 Nov 2024 12:27:53 +0500 Subject: [PATCH] feat: wip --- .gitignore | 1 + package.json | 3 +- .../stories/__creevey__/Input.creevey.ts | 2 +- .../StyleGuideWrapper.styles.ts | 62 +- .../StyleGuideWrapper/StyleGuideWrapper.tsx | 16 +- .../Autocomplete/Autocomplete.styles.ts | 28 +- .../components/Autocomplete/Autocomplete.tsx | 39 +- .../components/Button/Button.mixins.ts | 73 +- .../components/Button/Button.styles.ts | 1552 +++++++++-------- .../react-ui/components/Button/Button.tsx | 56 +- .../components/Button/ButtonArrow.tsx | 14 +- .../components/Button/ButtonIcon.styles.ts | 89 +- .../react-ui/components/Button/ButtonIcon.tsx | 9 +- .../components/Button/LoadingButtonIcon.tsx | 7 +- .../components/Calendar/Calendar.styles.ts | 61 +- .../react-ui/components/Calendar/Calendar.tsx | 38 +- .../components/Calendar/CalendarDay.tsx | 12 +- .../components/Calendar/DayCellView.styles.ts | 115 +- .../components/Calendar/DayCellView.tsx | 7 +- .../react-ui/components/Calendar/Month.tsx | 34 +- .../components/Calendar/MonthView.styles.ts | 129 +- .../components/Calendar/MonthView.tsx | 12 +- .../components/Center/Center.styles.ts | 69 +- .../react-ui/components/Center/Center.tsx | 25 +- .../components/Checkbox/Checkbox.mixins.ts | 39 +- .../components/Checkbox/Checkbox.styles.ts | 489 +++--- .../react-ui/components/Checkbox/Checkbox.tsx | 47 +- .../DateInput/DateFragmentsView.styles.ts | 57 +- .../DateInput/DateFragmentsView.tsx | 33 +- .../components/DateInput/DateInput.styles.ts | 21 +- .../components/DateInput/DateInput.tsx | 35 +- .../DatePicker/DatePicker.styles.ts | 49 +- .../components/DatePicker/DatePicker.tsx | 43 +- .../DatePicker/MobilePicker.styles.ts | 15 +- .../components/DatePicker/MobilePicker.tsx | 6 +- .../react-ui/components/Dropdown/Dropdown.tsx | 4 +- .../components/DropdownMenu/DropdownMenu.tsx | 4 +- .../FileUploader/FileUploader.mixins.ts | 9 +- .../FileUploader/FileUploader.styles.ts | 220 +-- .../components/FileUploader/FileUploader.tsx | 73 +- .../react-ui/components/FxInput/FxInput.tsx | 4 +- .../components/FxInput/FxInputRestoreBtn.tsx | 2 +- .../GlobalLoader/GlobalLoaderView.styles.ts | 311 ++-- .../GlobalLoader/GlobalLoaderView.tsx | 12 +- .../react-ui/components/Group/Group.styles.ts | 75 +- packages/react-ui/components/Group/Group.tsx | 28 +- .../react-ui/components/Hint/Hint.styles.ts | 45 +- packages/react-ui/components/Hint/Hint.tsx | 58 +- .../react-ui/components/Input/Input.styles.ts | 543 +++--- packages/react-ui/components/Input/Input.tsx | 43 +- .../Input/InputLayout/InputLayout.styles.ts | 107 +- .../Input/InputLayout/InputLayout.tsx | 7 +- .../Input/InputLayout/InputLayoutAside.tsx | 8 +- .../InputLayout/InputLayoutAsideIcon.tsx | 15 +- .../InputLayout/InputLayoutAsideText.tsx | 14 +- .../Input/InputLayout/PolyfillPlaceholder.tsx | 14 +- .../react-ui/components/Kebab/Kebab.styles.ts | 141 +- packages/react-ui/components/Kebab/Kebab.tsx | 50 +- .../react-ui/components/Link/Link.styles.ts | 320 ++-- packages/react-ui/components/Link/Link.tsx | 36 +- .../react-ui/components/Link/LinkIcon.tsx | 10 +- .../components/Loader/Loader.styles.ts | 119 +- .../react-ui/components/Loader/Loader.tsx | 41 +- .../Loader/__stories__/Loader.stories.tsx | 98 +- .../ColorableInputElement.tsx | 5 +- .../components/MaskedInput/MaskedInput.tsx | 7 +- .../MenuFooter/MenuFooter.mixins.ts | 28 +- .../MenuFooter/MenuFooter.styles.ts | 117 +- .../components/MenuFooter/MenuFooter.tsx | 10 +- .../MenuHeader/MenuHeader.mixins.ts | 28 +- .../MenuHeader/MenuHeader.styles.ts | 117 +- .../components/MenuHeader/MenuHeader.tsx | 10 +- .../components/MenuItem/MenuItem.mixins.ts | 45 +- .../components/MenuItem/MenuItem.styles.ts | 288 +-- .../react-ui/components/MenuItem/MenuItem.tsx | 57 +- .../MenuSeparator/MenuSeparator.styles.ts | 33 +- .../MenuSeparator/MenuSeparator.tsx | 10 +- .../components/MiniModal/MiniModal.styles.ts | 151 +- .../components/MiniModal/MiniModal.tsx | 2 +- .../components/MiniModal/MiniModalBody.tsx | 20 +- .../components/MiniModal/MiniModalFooter.tsx | 12 +- .../components/MiniModal/MiniModalHeader.tsx | 8 +- .../components/MiniModal/MiniModalIndent.tsx | 10 +- .../react-ui/components/Modal/Modal.styles.ts | 259 +-- packages/react-ui/components/Modal/Modal.tsx | 49 +- .../react-ui/components/Modal/ModalBody.tsx | 33 +- .../react-ui/components/Modal/ModalClose.tsx | 10 +- .../react-ui/components/Modal/ModalFooter.tsx | 12 +- .../react-ui/components/Modal/ModalHeader.tsx | 12 +- .../components/Modal/ModalSeparator.tsx | 8 +- .../Modal/__stories__/Modal.stories.tsx | 4 +- .../components/Paging/Paging.styles.ts | 261 +-- .../react-ui/components/Paging/Paging.tsx | 44 +- .../PasswordInput/PasswordInput.styles.ts | 155 +- .../PasswordInput/PasswordInput.tsx | 38 +- .../react-ui/components/Radio/Radio.mixins.ts | 64 +- .../react-ui/components/Radio/Radio.styles.ts | 462 ++--- packages/react-ui/components/Radio/Radio.tsx | 46 +- .../RadioGroup/RadioGroup.styles.ts | 65 +- .../components/RadioGroup/RadioGroup.tsx | 24 +- .../components/ScrollContainer/ScrollBar.tsx | 43 +- .../ScrollContainer/ScrollContainer.styles.ts | 251 +-- .../ScrollContainer/ScrollContainer.tsx | 26 +- .../components/Select/Select.styles.ts | 217 +-- .../react-ui/components/Select/Select.tsx | 56 +- .../components/SidePage/SidePage.styles.ts | 745 ++++---- .../react-ui/components/SidePage/SidePage.tsx | 37 +- .../components/SidePage/SidePageBody.tsx | 41 +- .../SidePage/SidePageCloseButton.tsx | 11 +- .../components/SidePage/SidePageContainer.tsx | 31 +- .../components/SidePage/SidePageFooter.tsx | 32 +- .../components/SidePage/SidePageHeader.tsx | 39 +- .../components/Spinner/Spinner.styles.ts | 191 +- .../react-ui/components/Spinner/Spinner.tsx | 49 +- .../components/Sticky/Sticky.styles.ts | 63 +- .../react-ui/components/Sticky/Sticky.tsx | 25 +- .../components/Switcher/Switcher.styles.ts | 119 +- .../react-ui/components/Switcher/Switcher.tsx | 38 +- .../components/Tabs/Indicator.styles.ts | 67 +- .../react-ui/components/Tabs/Indicator.tsx | 30 +- .../react-ui/components/Tabs/Tab.styles.ts | 320 ++-- packages/react-ui/components/Tabs/Tab.tsx | 35 +- .../react-ui/components/Tabs/Tabs.styles.ts | 52 +- packages/react-ui/components/Tabs/Tabs.tsx | 89 +- .../components/Textarea/Textarea.mixins.ts | 14 +- .../components/Textarea/Textarea.styles.ts | 409 ++--- .../react-ui/components/Textarea/Textarea.tsx | 40 +- .../components/Textarea/TextareaCounter.tsx | 20 +- .../react-ui/components/Toast/Toast.styles.ts | 55 +- packages/react-ui/components/Toast/Toast.tsx | 35 +- .../components/Toast/ToastView.styles.ts | 123 +- .../react-ui/components/Toast/ToastView.tsx | 27 +- .../components/Toggle/Toggle.mixins.ts | 51 +- .../components/Toggle/Toggle.styles.ts | 596 +++---- .../react-ui/components/Toggle/Toggle.tsx | 47 +- .../react-ui/components/Token/Token.mixins.ts | 24 +- .../react-ui/components/Token/Token.styles.ts | 347 ++-- packages/react-ui/components/Token/Token.tsx | 30 +- .../react-ui/components/Token/TokenView.tsx | 11 +- .../components/TokenInput/TextWidthHelper.tsx | 24 +- .../TokenInput/TokenInput.mixins.ts | 10 +- .../TokenInput/TokenInput.styles.ts | 299 ++-- .../components/TokenInput/TokenInput.tsx | 37 +- .../components/Tooltip/Tooltip.styles.ts | 61 +- .../react-ui/components/Tooltip/Tooltip.tsx | 62 +- .../CloseButtonIcon.styles.tsx | 81 +- .../CloseButtonIcon/CloseButtonIcon.tsx | 16 +- .../internal/CommonWrapper/CommonWrapper.tsx | 17 +- .../internal/CustomComboBox/ComboBoxView.tsx | 42 +- .../CustomComboBox/CustomComboBox.styles.ts | 35 +- .../internal/DataTids/DataTids.styles.ts | 83 +- .../react-ui/internal/DataTids/DataTids.tsx | 53 +- .../internal/DateSelect/DateSelect.styles.ts | 97 +- .../internal/DateSelect/DateSelect.tsx | 32 +- .../FileUploaderFile.styles.ts | 101 +- .../FileUploaderFile/FileUploaderFile.tsx | 36 +- .../FileUploaderFileStatusIcon.tsx | 9 +- .../FileUploaderFileList.styles.ts | 41 +- .../FileUploaderFileList.tsx | 18 +- .../HideBodyVerticalScroll.tsx | 44 +- .../internal/InputLikeText/HiddenInput.tsx | 51 +- .../InputLikeText/InputLikeText.styles.ts | 65 +- .../internal/InputLikeText/InputLikeText.tsx | 56 +- .../InternalMaskedInput.styles.ts | 59 +- .../InternalMaskedInput.tsx | 30 +- .../MaskCharLowLine/MaskCharLowLine.styles.ts | 37 +- .../MaskCharLowLine/MaskCharLowLine.tsx | 12 +- .../react-ui/internal/Menu/Menu.styles.ts | 175 +- packages/react-ui/internal/Menu/Menu.tsx | 44 +- .../MenuMessage/MenuMessage.styles.ts | 85 +- .../internal/MenuMessage/MenuMessage.tsx | 17 +- .../MobilePopup/MobilePopup.styles.ts | 53 +- .../internal/MobilePopup/MobilePopup.tsx | 41 +- .../MobilePopupFooter.styles.ts | 17 +- .../MobilePopupFooter/MobilePopupFooter.tsx | 26 +- .../MobilePopupHeader.styles.ts | 41 +- .../MobilePopupHeader/MobilePopupHeader.tsx | 40 +- .../NativeDateInput/NativeDateInput.styles.ts | 17 +- .../NativeDateInput/NativeDateInput.tsx | 40 +- .../react-ui/internal/Popup/Popup.styles.ts | 173 +- packages/react-ui/internal/Popup/Popup.tsx | 63 +- .../react-ui/internal/Popup/PopupHelper.tsx | 10 +- .../internal/Popup/PopupPin.styles.ts | 23 +- packages/react-ui/internal/Popup/PopupPin.tsx | 21 +- .../Popup/__stories__/Popup.stories.tsx | 2 +- .../internal/PopupMenu/PopupMenu.styles.ts | 33 +- .../react-ui/internal/PopupMenu/PopupMenu.tsx | 53 +- .../RenderContainer/RenderContainer.tsx | 34 +- .../internal/RenderLayer/RenderLayer.tsx | 29 +- .../ResizeDetector/ResizeDetector.styles.ts | 69 +- .../ResizeDetector/ResizeDetector.tsx | 23 +- .../SpinnerIcon/SpinnerIcon.styles.ts | 65 +- .../internal/SpinnerIcon/SpinnerIcon.tsx | 20 +- .../TextWidthHelper/TextWidthHelper.styles.ts | 23 +- .../TextWidthHelper/TextWidthHelper.tsx | 20 +- .../ThemePlayground/ComponentsGroup.tsx | 8 +- .../ThemePlayground/Playground.styles.ts | 319 ++-- .../internal/ThemePlayground/Playground.tsx | 39 +- .../ThemePlayground/ShowcaseGroup.tsx | 4 +- .../ThemeContextPlayground.tsx | 41 +- .../internal/ThemePlayground/ThemeEditor.tsx | 22 +- .../ThemePlayground/VariableValue.tsx | 39 +- .../ThemeShowcase/ThemeShowcase.styles.ts | 407 ++--- .../internal/ThemeShowcase/ThemeShowcase.tsx | 366 ++-- .../internal/icons2022/BaseIcon.styles.ts | 27 +- .../react-ui/internal/icons2022/BaseIcon.tsx | 7 +- .../internal/icons2022/LoadingIcon.tsx | 19 +- packages/react-ui/lib/listenFocusOutside.ts | 5 +- .../react-ui/lib/shadowDom/reactShadow.tsx | 180 ++ packages/react-ui/lib/styles/Mixins.ts | 10 +- .../react-ui/lib/styles/StylesContainer.tsx | 26 + .../styles/__tests__/StylesContainer-test.tsx | 18 + .../lib/theming/AnimationKeyframes.ts | 15 +- packages/react-ui/lib/theming/Emotion.ts | 32 +- packages/react-ui/lib/theming/useTheme.ts | 7 - 215 files changed, 9275 insertions(+), 7719 deletions(-) create mode 100644 packages/react-ui/lib/shadowDom/reactShadow.tsx create mode 100644 packages/react-ui/lib/styles/StylesContainer.tsx create mode 100644 packages/react-ui/lib/styles/__tests__/StylesContainer-test.tsx delete mode 100644 packages/react-ui/lib/theming/useTheme.ts diff --git a/.gitignore b/.gitignore index d7bb8fd4309..d48a3f09d0a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .vscode .DS_Store *.code-workspace +*.orig node_modules yarn-debug.log diff --git a/package.json b/package.json index fbf036d5c65..193ebb1787b 100644 --- a/package.json +++ b/package.json @@ -35,5 +35,6 @@ }, "resolutions": { "selenium-webdriver": "4.0.0-beta.4" - } + }, + "packageManager": "yarn@1.22.1" } diff --git a/packages/react-ui-validations/stories/__creevey__/Input.creevey.ts b/packages/react-ui-validations/stories/__creevey__/Input.creevey.ts index 6a4342dceef..7fe412ca0bf 100644 --- a/packages/react-ui-validations/stories/__creevey__/Input.creevey.ts +++ b/packages/react-ui-validations/stories/__creevey__/Input.creevey.ts @@ -3,7 +3,7 @@ import { story, kind, test } from 'creevey'; kind('Input', () => { story('TooltipTopLeft', () => { test('invalidTooltip', async function () { - const input = await this.browser.findElement({ css: '.react-ui-1xb4xgu' }); + const input = await this.browser.findElement({ css: '[data-tid~="test-input"]' }); await this.browser .actions({ bridge: true, diff --git a/packages/react-ui/.styleguide/components/StyleGuideWrapper/StyleGuideWrapper.styles.ts b/packages/react-ui/.styleguide/components/StyleGuideWrapper/StyleGuideWrapper.styles.ts index 8f3715c1251..82063abe2e4 100644 --- a/packages/react-ui/.styleguide/components/StyleGuideWrapper/StyleGuideWrapper.styles.ts +++ b/packages/react-ui/.styleguide/components/StyleGuideWrapper/StyleGuideWrapper.styles.ts @@ -1,9 +1,11 @@ -import { css, memoizeStyle } from '../../../lib/theming/Emotion'; +import { memoizeStyle } from '../../../lib/theming/Emotion'; import { Theme } from '../../../lib/theming/Theme'; +import type { Emotion } from '@emotion/css/create-instance'; -export const styles = memoizeStyle({ - root() { - return css` +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root() { + return emotion.css` font-weight: 400; font-size: 14px; padding-left: 300px; @@ -41,15 +43,15 @@ export const styles = memoizeStyle({ } } `; - }, - darkRoot(t: Theme) { - return css` + }, + darkRoot(t: Theme) { + return emotion.css` background: ${t.bgDefault}; color: ${t.textColorDefault}; `; - }, - wrapper() { - return css` + }, + wrapper() { + return emotion.css` padding: 30px 40px; margin: 0 auto; max-width: 1000px; @@ -59,12 +61,12 @@ export const styles = memoizeStyle({ padding: 16px; } `; - }, - content() { - return css``; - }, - darkContent(t: Theme) { - return css` + }, + content() { + return emotion.css``; + }, + darkContent(t: Theme) { + return emotion.css` h1, h2, h3, @@ -126,14 +128,14 @@ export const styles = memoizeStyle({ border: 1px solid #444; } `; - }, - header() { - return css` + }, + header() { + return emotion.css` padding: 40px 40px 0 !important; `; - }, - sidebar() { - return css` + }, + sidebar() { + return emotion.css` width: 300px; background: #41464e; font-size: 16px; @@ -165,9 +167,9 @@ export const styles = memoizeStyle({ font-weight: normal; } `; - }, - footer() { - return css` + }, + footer() { + return emotion.css` position: fixed; top: 0; right: 0; @@ -175,9 +177,9 @@ export const styles = memoizeStyle({ height: 149px; z-index: 999; `; - }, - footerLink() { - return css` + }, + footerLink() { + return emotion.css` position: relative; right: -37px; top: -22px; @@ -194,5 +196,5 @@ export const styles = memoizeStyle({ transform: rotate(45deg); cursor: pointer; `; - }, -}); + }, + }); diff --git a/packages/react-ui/.styleguide/components/StyleGuideWrapper/StyleGuideWrapper.tsx b/packages/react-ui/.styleguide/components/StyleGuideWrapper/StyleGuideWrapper.tsx index c99c907db1e..c38be3b8b78 100644 --- a/packages/react-ui/.styleguide/components/StyleGuideWrapper/StyleGuideWrapper.tsx +++ b/packages/react-ui/.styleguide/components/StyleGuideWrapper/StyleGuideWrapper.tsx @@ -1,14 +1,13 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import Context from 'react-styleguidist/lib/client/rsg-components/Context'; import { useStyleGuideContext } from 'react-styleguidist/lib/client/rsg-components/Context/Context'; +import { EmotionContext } from '../../../lib/theming/Emotion'; +import { getStyles } from './StyleGuideWrapper.styles'; import ThemeSwitcher from '../ThemeSwitcher/ThemeSwitcher'; -import { cx } from '../../../lib/theming/Emotion'; import { DARK_THEME } from '../../../lib/theming/themes/DarkTheme'; import { DEFAULT_THEME_WRAPPER } from '../ThemeSwitcher/constants'; -import { styles } from './StyleGuideWrapper.styles'; - interface StyleGuideRendererProps { children: React.ReactNode; hasSidebar: boolean; @@ -18,6 +17,7 @@ interface StyleGuideRendererProps { } function StyleGuideRenderer({ children, hasSidebar, toc, title, version }: StyleGuideRendererProps) { + const emotion = useContext(EmotionContext); const { codeRevision, config, slots, displayMode, cssRevision } = useStyleGuideContext(); const [theme, setTheme] = useState(DEFAULT_THEME_WRAPPER); document.body.style.fontFamily = 'Lab Grotesque, Roboto, Helvetica Neue, Arial, sans-serif'; @@ -33,11 +33,15 @@ function StyleGuideRenderer({ children, hasSidebar, toc, title, version }: Style root.style.height = '100%'; } } + + const styles = getStyles(emotion); return ( -
+
-
{children}
+
+ {children} +
{hasSidebar && (
diff --git a/packages/react-ui/components/Autocomplete/Autocomplete.styles.ts b/packages/react-ui/components/Autocomplete/Autocomplete.styles.ts index de2c5f1f8a1..3554cdddf92 100644 --- a/packages/react-ui/components/Autocomplete/Autocomplete.styles.ts +++ b/packages/react-ui/components/Autocomplete/Autocomplete.styles.ts @@ -1,16 +1,20 @@ -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; -export const styles = memoizeStyle({ - root(t: Theme) { - return css` - display: inline-block; - width: ${t.inputWidth}; - `; - }, - noPortal() { - return css` +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root(t: Theme) { + return emotion.css` + display: inline-block; + width: ${t.inputWidth}; + `; + }, + + noPortal() { + return emotion.css` position: relative; `; - }, -}); + }, + }); diff --git a/packages/react-ui/components/Autocomplete/Autocomplete.tsx b/packages/react-ui/components/Autocomplete/Autocomplete.tsx index 3f754414765..96cd2a3e692 100644 --- a/packages/react-ui/components/Autocomplete/Autocomplete.tsx +++ b/packages/react-ui/components/Autocomplete/Autocomplete.tsx @@ -1,12 +1,12 @@ import React, { AriaAttributes, KeyboardEvent } from 'react'; import PropTypes from 'prop-types'; +import type { Emotion } from '@emotion/css/create-instance'; import { MenuMessage } from '../../internal/MenuMessage'; import { locale } from '../../lib/locale/decorators'; import { getRandomID, isNullable } from '../../lib/utils'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme } from '../../lib/theming/Theme'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { isKeyArrowDown, isKeyArrowUp, isKeyEnter, isKeyEscape } from '../../lib/events/keyboard/identifiers'; import { Input, InputProps } from '../Input'; import { Menu } from '../../internal/Menu'; @@ -23,11 +23,12 @@ import { getDOMRect } from '../../lib/dom/getDOMRect'; import { SizeProp } from '../../lib/types/props'; import { Popup } from '../../internal/Popup'; import { getMenuPositions } from '../../lib/getMenuPositions'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { ZIndex } from '../../internal/ZIndex'; -import { styles } from './Autocomplete.styles'; import { AutocompleteLocale, AutocompleteLocaleHelper } from './locale'; import { getAutocompleteTheme } from './getAutocompleteTheme'; +import { getStyles } from './Autocomplete.styles'; function match(pattern: string, items: string[]) { if (!pattern || !items) { @@ -163,6 +164,8 @@ export class Autocomplete extends React.Component; private readonly locale!: AutocompleteLocale; private isMobileLayout!: boolean; private opened = false; @@ -201,18 +204,26 @@ export class Autocomplete extends React.Component - {(theme) => { - this.theme = getAutocompleteTheme(theme); + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); return ( - - - {this.renderMain} - - + + {(theme) => { + this.theme = getAutocompleteTheme(theme); + return ( + + + {this.renderMain} + + + ); + }} + ); }} - + ); } public renderMain = (props: CommonWrapperRestProps) => { @@ -253,8 +264,8 @@ export class Autocomplete extends React.Component { return `${paddingY} ${paddingX} ${paddingY}`; }; -export const buttonUseMixin = ( - btnBackground: string, - btnBackgroundStart: string, - btnBackgroundEnd: string, - color: string, - borderColor: string, - borderWidth: string, -) => { - const hasGradient = btnBackgroundStart !== btnBackgroundEnd; - return css` +export const buttonUseMixin = + (emotion: Emotion) => + ( + btnBackground: string, + btnBackgroundStart: string, + btnBackgroundEnd: string, + color: string, + borderColor: string, + borderWidth: string, + ) => { + const hasGradient = btnBackgroundStart !== btnBackgroundEnd; + return emotion.css` background-color: ${hasGradient ? `initial` : btnBackground}; background-image: ${hasGradient ? `linear-gradient(${btnBackgroundStart}, ${btnBackgroundEnd})` : `none`}; color: ${color}; @@ -28,32 +30,30 @@ export const buttonUseMixin = ( } } `; -}; + }; -export const buttonHoverMixin = ( - btnBackground: string, - btnBackgroundStart: string, - btnBackgroundEnd: string, - color: string, - borderColor: string, - borderWidth: string, -) => { - const hasGradient = btnBackgroundStart !== btnBackgroundEnd; - return css` +export const buttonHoverMixin = + (emotion: Emotion) => + ( + btnBackground: string, + btnBackgroundStart: string, + btnBackgroundEnd: string, + color: string, + borderColor: string, + borderWidth: string, + ) => { + const hasGradient = btnBackgroundStart !== btnBackgroundEnd; + return emotion.css` background-color: ${hasGradient ? `initial` : btnBackground}; background-image: ${hasGradient ? `linear-gradient(${btnBackgroundStart}, ${btnBackgroundEnd})` : `none`}; box-shadow: 0 0 0 ${borderWidth} ${borderColor}; color: ${color}; `; -}; + }; -export const buttonActiveMixin = ( - btnBackground: string, - btnShadow: string, - borderColor: string, - borderWidth: string, -) => { - return css` +export const buttonActiveMixin = + (emotion: Emotion) => (btnBackground: string, btnShadow: string, borderColor: string, borderWidth: string) => { + return emotion.css` &, &:hover { background-image: none !important; // override :hover styles @@ -65,19 +65,20 @@ export const buttonActiveMixin = ( } } `; -}; + }; -export const buttonSizeMixin = (fontSize: string, lineHeight: string, paddingX: string, paddingY: string) => { - return css` +export const buttonSizeMixin = + (emotion: Emotion) => (fontSize: string, lineHeight: string, paddingX: string, paddingY: string) => { + return emotion.css` font-size: ${fontSize}; box-sizing: border-box; padding: ${getBtnPadding(paddingY, paddingX)}; line-height: ${lineHeight}; `; -}; + }; -export const buttonSizeMixinIE11 = (paddingX: string, paddingY: string) => { - return css` +export const buttonSizeMixinIE11 = (emotion: Emotion) => (paddingX: string, paddingY: string) => { + return emotion.css` padding: ${getBtnPadding(paddingY, paddingX)}; line-height: normal; `; diff --git a/packages/react-ui/components/Button/Button.styles.ts b/packages/react-ui/components/Button/Button.styles.ts index e1ac8e9a71f..8272d871ab1 100644 --- a/packages/react-ui/components/Button/Button.styles.ts +++ b/packages/react-ui/components/Button/Button.styles.ts @@ -1,4 +1,6 @@ -import { css, memoizeStyle, prefix } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle, prefix } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { resetButton, resetText } from '../../lib/styles/Mixins'; @@ -19,560 +21,565 @@ export const globalClasses = prefix('button')({ disabled: 'disabled', }); -export const styles = memoizeStyle({ - root(t: Theme) { - return css` - ${resetButton()}; - ${resetText()}; - - transition: background-color ${t.transitionDuration} ${t.transitionTimingFunction} - ${t.btnBorderColorTransition ? `, ${t.btnBorderColorTransition}` : ''}; - - background-clip: ${t.btnBackgroundClip}; - background-position: center; - background-repeat: no-repeat; - background-size: contain; - cursor: pointer; - display: inline-block; - position: relative; - text-align: center; - width: 100%; - height: 100%; // fix height in ie11 - - .${globalClasses.innerShadow} { - content: ''; +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root(t: Theme) { + return emotion.css` + ${resetButton(emotion)}; + ${resetText(emotion)}; + + transition: background-color ${t.transitionDuration} ${t.transitionTimingFunction} + ${t.btnBorderColorTransition ? `, ${t.btnBorderColorTransition}` : ''}; + + background-clip: ${t.btnBackgroundClip}; + background-position: center; + background-repeat: no-repeat; + background-size: contain; + cursor: pointer; + display: inline-block; + position: relative; + text-align: center; + width: 100%; + height: 100%; // fix height in ie11 + + .${globalClasses.innerShadow} { + content: ''; + border-radius: inherit; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + } + + &::-moz-focus-inner { + border: 0; + padding: 0; + } + &::after { + content: ''; + display: inline-block; + vertical-align: baseline; + width: 0; + } + + &:hover svg { + color: ${t.btnIconHoverColor}; + } + &.${globalClasses.disabled} svg { + color: ${t.btnIconDisabledColor}; + } + & svg { + color: ${t.btnIconColor}; + } + `; + }, + + withArrowIconRightSmall(t: Theme) { + return emotion.css` + padding-right: calc( + ${t.btnIconSizeSmall} + ${t.btnWithIconPaddingLeftSmall} + ${t.btnWithIconPaddingLeftSmall} + ); + `; + }, + + withArrowIconRightMedium(t: Theme) { + return emotion.css` + padding-right: calc( + ${t.btnIconSizeMedium} + ${t.btnWithIconPaddingLeftMedium} + ${t.btnWithIconPaddingLeftMedium} + ); + `; + }, + + withArrowIconRightLarge(t: Theme) { + return emotion.css` + padding-right: calc( + ${t.btnIconSizeLarge} + ${t.btnWithIconPaddingLeftLarge} + ${t.btnWithIconPaddingLeftLarge} + ); + `; + }, + + withArrowIconLeftSmall(t: Theme) { + return emotion.css` + padding-left: calc(${t.btnIconSizeSmall} + ${t.btnWithIconPaddingLeftSmall} + ${t.btnWithIconPaddingLeftSmall}); + `; + }, + + withArrowIconLeftMedium(t: Theme) { + return emotion.css` + padding-left: calc( + ${t.btnIconSizeMedium} + ${t.btnWithIconPaddingLeftMedium} + ${t.btnWithIconPaddingLeftMedium} + ); + `; + }, + + withArrowIconLeftLarge(t: Theme) { + return emotion.css` + padding-left: calc(${t.btnIconSizeLarge} + ${t.btnWithIconPaddingLeftLarge} + ${t.btnWithIconPaddingLeftLarge}); + `; + }, + + simulatedPress() { + return emotion.css` + &:active .${globalClasses.caption} { + transform: translateY(1px); + } + `; + }, + + outline() { + return emotion.css` border-radius: inherit; position: absolute; top: 0; bottom: 0; left: 0; right: 0; - } - - &::-moz-focus-inner { - border: 0; - padding: 0; - } - &::after { - content: ''; - display: inline-block; - vertical-align: baseline; - width: 0; - } - - &:hover svg { - color: ${t.btnIconHoverColor}; - } - &.${globalClasses.disabled} svg { - color: ${t.btnIconDisabledColor}; - } - & svg { - color: ${t.btnIconColor}; - } - `; - }, - - withArrowIconRightSmall(t: Theme) { - return css` - padding-right: calc(${t.btnIconSizeSmall} + ${t.btnWithIconPaddingLeftSmall} + ${t.btnWithIconPaddingLeftSmall}); - `; - }, - - withArrowIconRightMedium(t: Theme) { - return css` - padding-right: calc( - ${t.btnIconSizeMedium} + ${t.btnWithIconPaddingLeftMedium} + ${t.btnWithIconPaddingLeftMedium} - ); - `; - }, - - withArrowIconRightLarge(t: Theme) { - return css` - padding-right: calc(${t.btnIconSizeLarge} + ${t.btnWithIconPaddingLeftLarge} + ${t.btnWithIconPaddingLeftLarge}); - `; - }, - - withArrowIconLeftSmall(t: Theme) { - return css` - padding-left: calc(${t.btnIconSizeSmall} + ${t.btnWithIconPaddingLeftSmall} + ${t.btnWithIconPaddingLeftSmall}); - `; - }, - - withArrowIconLeftMedium(t: Theme) { - return css` - padding-left: calc( - ${t.btnIconSizeMedium} + ${t.btnWithIconPaddingLeftMedium} + ${t.btnWithIconPaddingLeftMedium} - ); - `; - }, - - withArrowIconLeftLarge(t: Theme) { - return css` - padding-left: calc(${t.btnIconSizeLarge} + ${t.btnWithIconPaddingLeftLarge} + ${t.btnWithIconPaddingLeftLarge}); - `; - }, - - simulatedPress() { - return css` - &:active .${globalClasses.caption} { - transform: translateY(1px); - } - `; - }, - - outline() { - return css` - border-radius: inherit; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - `; - }, - - outlineWarning(t: Theme) { - return css` - box-shadow: - 0 0 0 ${t.btnOutlineWidth} ${t.btnBorderColorWarning}, - inset 0 0 0 ${t.btnInsetWidth} ${t.btnInsetColor}; - `; - }, - - outlineError(t: Theme) { - return css` - box-shadow: - 0 0 0 ${t.btnOutlineWidth} ${t.btnBorderColorError}, - inset 0 0 0 ${t.btnInsetWidth} ${t.btnInsetColor}; - `; - }, - - outlineLink() { - return css` - box-shadow: none; - left: -2px; - right: -2px; - bottom: -2px; - top: -2px; - `; - }, - - outlineLinkWarning(t: Theme) { - return css` - background-color: ${t.btnWarningSecondary}; - `; - }, - - outlineLinkError(t: Theme) { - return css` - background-color: ${t.btnErrorSecondary}; - `; - }, - - sizeSmall(t: Theme) { - return css` - border-radius: ${t.btnBorderRadiusSmall}; - - ${buttonSizeMixin(t.btnFontSizeSmall, t.btnLineHeightSmall, t.btnPaddingXSmall, t.btnPaddingYSmall)}; - `; - }, - - sizeSmallIE11(t: Theme) { - return css` - ${buttonSizeMixinIE11(t.btnPaddingXSmall, t.btnPaddingYSmall)}; - `; - }, - - sizeMedium(t: Theme) { - return css` - border-radius: ${t.btnBorderRadiusMedium}; - - ${buttonSizeMixin(t.btnFontSizeMedium, t.btnLineHeightMedium, t.btnPaddingXMedium, t.btnPaddingYMedium)}; - `; - }, - - sizeMediumIE11(t: Theme) { - return css` - ${buttonSizeMixinIE11(t.btnPaddingXMedium, t.btnPaddingYMedium)}; - `; - }, - - sizeLarge(t: Theme) { - return css` - border-radius: ${t.btnBorderRadiusLarge}; - - ${buttonSizeMixin(t.btnFontSizeLarge, t.btnLineHeightLarge, t.btnPaddingXLarge, t.btnPaddingYLarge)}; - `; - }, - - sizeLargeIE11(t: Theme) { - return css` - ${buttonSizeMixinIE11(t.btnPaddingXLarge, t.btnPaddingYLarge)}; - `; - }, - - sizeSmallWithIcon(t: Theme) { - return css` - padding-left: ${t.btnWithIconPaddingLeftSmall}; - `; - }, + `; + }, - sizeMediumWithIcon(t: Theme) { - return css` - padding-left: ${t.btnWithIconPaddingLeftMedium}; - `; - }, - - sizeLargeWithIcon(t: Theme) { - return css` - padding-left: ${t.btnWithIconPaddingLeftLarge}; - `; - }, - - sizeSmallWithIconWithoutText(t: Theme) { - return css` - padding-right: ${t.btnWithIconPaddingLeftSmall}; - `; - }, - - sizeMediumWithIconWithoutText(t: Theme) { - return css` - padding-right: ${t.btnWithIconPaddingLeftMedium}; - `; - }, - - sizeLargeWithIconWithoutText(t: Theme) { - return css` - padding-right: ${t.btnWithIconPaddingLeftLarge}; - `; - }, - - link(t: Theme) { - return css` - background: none; - border-radius: ${t.btnLinkBorderRadius}; - border: none; - box-shadow: none; - white-space: nowrap; - color: ${t.btnLinkColor}; - display: inline; - margin: 0; - padding: 0 !important; // override size mixin - height: auto !important; // override size mixin - - &:hover:enabled, - &:active:enabled { - color: ${t.btnLinkHoverColor}; - text-decoration: ${t.btnLinkHoverTextDecoration}; - } - - &:active:enabled { - ${activeStyles.link(t)} - } - `; - }, - - linkLineHeight() { - return css` - line-height: inherit !important; // override size mixin - `; - }, - - linkLineHeightSafariFallback() { - return css` - /* Safari overrides 'underline' and 'border' if 'line-height' is used */ - margin: -1px 0 -2px; - `; - }, - - linkFocus(t: Theme) { - return css` - & { - color: ${t.btnLinkColor}; - text-decoration: ${t.btnLinkHoverTextDecoration}; - } - `; - }, - - linkDisabled(t: Theme) { - return css` - cursor: default; - - &, - &:hover:enabled, - &:active:enabled { - color: ${t.btnLinkDisabledColor}; - } - `; - }, - - focus(t: Theme) { - return css` - position: relative; - z-index: 2; - - &, - &:hover:enabled, - &:active:enabled, - &:active:hover:enabled { + outlineWarning(t: Theme) { + return emotion.css` box-shadow: - inset 0 0 0 ${t.btnInsetWidth} ${t.btnOutlineColorFocus}, - 0 0 0 ${t.btnFocusShadowWidth} ${t.btnBorderColorFocus} !important; // override root:hover style - } - `; - }, - - disabled(t: Theme) { - return css` - cursor: default; - pointer-events: none; - box-shadow: 0 0 0 ${t.btnBorderWidth} ${t.btnDisabledBorderColor}; - - background-image: none; - background-color: ${t.btnDisabledBg}; - color: ${t.btnDisabledTextColor}; - `; - }, - - disabledWithoutOutline(t: Theme) { - return css` - box-shadow: 0 0 0 ${t.btnBorderWidth} ${t.btnDisabledBg}; - `; - }, - - arrowIconRoot() { - return css` - position: absolute; - height: 100%; - top: 0; - right: 0; - display: flex; - align-items: center; - justify-content: center; - box-sizing: content-box; - `; - }, - - arrowIconRootSmall(t: Theme) { - return css` - padding: 0 ${t.btnWithIconPaddingLeftSmall} 0 ${t.btnWithIconPaddingLeftSmall}; - width: ${t.btnIconSizeSmall}; - `; - }, - - arrowIconRootMedium(t: Theme) { - return css` - padding: 0 ${t.btnWithIconPaddingLeftMedium} 0 ${t.btnWithIconPaddingLeftMedium}; - width: ${t.btnIconSizeMedium}; - `; - }, - - arrowIconRootLarge(t: Theme) { - return css` - padding: 0 ${t.btnWithIconPaddingLeftLarge} 0 ${t.btnWithIconPaddingLeftLarge}; - width: ${t.btnIconSizeLarge}; - `; - }, + 0 0 0 ${t.btnOutlineWidth} ${t.btnBorderColorWarning}, + inset 0 0 0 ${t.btnInsetWidth} ${t.btnInsetColor}; + `; + }, - arrowIconLeft() { - return css` - left: 0; - `; - }, - - default(t: Theme) { - return css` - ${buttonUseMixin( - t.btnDefaultBg, - t.btnDefaultBgStart, - t.btnDefaultBgEnd, - t.btnDefaultTextColor, - t.btnDefaultBorderColor, - t.btnBorderWidth, - )}; - - &:hover { - ${buttonHoverMixin( - t.btnDefaultHoverBg, - t.btnDefaultHoverBgStart, - t.btnDefaultHoverBgEnd, - t.btnDefaultHoverTextColor, - t.btnDefaultHoverBorderColor, - t.btnBorderWidth, - )}; - } - - &:active { - ${activeStyles.default(t)}; - } - `; - }, - - primary(t: Theme) { - return css` - ${buttonUseMixin( - t.btnPrimaryBg, - t.btnPrimaryBgStart, - t.btnPrimaryBgEnd, - t.btnPrimaryTextColor, - t.btnPrimaryBorderColor, - t.btnBorderWidth, - )}; - - &:hover { - ${buttonHoverMixin( - t.btnPrimaryHoverBg, - t.btnPrimaryHoverBgStart, - t.btnPrimaryHoverBgEnd, - t.btnPrimaryHoverTextColor, - t.btnPrimaryHoverBorderColor, - t.btnBorderWidth, - )}; - } + outlineError(t: Theme) { + return emotion.css` + box-shadow: + 0 0 0 ${t.btnOutlineWidth} ${t.btnBorderColorError}, + inset 0 0 0 ${t.btnInsetWidth} ${t.btnInsetColor}; + `; + }, - &:active { - ${activeStyles.primary(t)} - } - `; - }, - - success(t: Theme) { - return css` - ${buttonUseMixin( - t.btnSuccessBg, - t.btnSuccessBgStart, - t.btnSuccessBgEnd, - t.btnSuccessTextColor, - t.btnSuccessBorderColor, - t.btnBorderWidth, - )}; - - &:hover { - ${buttonHoverMixin( - t.btnSuccessHoverBg, - t.btnSuccessHoverBgStart, - t.btnSuccessHoverBgEnd, - t.btnSuccessHoverTextColor, - t.btnSuccessHoverBorderColor, + outlineLink() { + return emotion.css` + box-shadow: none; + left: -2px; + right: -2px; + bottom: -2px; + top: -2px; + `; + }, + + outlineLinkWarning(t: Theme) { + return emotion.css` + background-color: ${t.btnWarningSecondary}; + `; + }, + + outlineLinkError(t: Theme) { + return emotion.css` + background-color: ${t.btnErrorSecondary}; + `; + }, + + sizeSmall(t: Theme) { + return emotion.css` + border-radius: ${t.btnBorderRadiusSmall}; + + ${buttonSizeMixin(emotion)(t.btnFontSizeSmall, t.btnLineHeightSmall, t.btnPaddingXSmall, t.btnPaddingYSmall)}; + `; + }, + + sizeSmallIE11(t: Theme) { + return emotion.css` + ${buttonSizeMixinIE11(emotion)(t.btnPaddingXSmall, t.btnPaddingYSmall)}; + `; + }, + + sizeMedium(t: Theme) { + return emotion.css` + border-radius: ${t.btnBorderRadiusMedium}; + + ${buttonSizeMixin(emotion)(t.btnFontSizeMedium, t.btnLineHeightMedium, t.btnPaddingXMedium, t.btnPaddingYMedium)}; + `; + }, + + sizeMediumIE11(t: Theme) { + return emotion.css` + ${buttonSizeMixinIE11(emotion)(t.btnPaddingXMedium, t.btnPaddingYMedium)}; + `; + }, + + sizeLarge(t: Theme) { + return emotion.css` + border-radius: ${t.btnBorderRadiusLarge}; + + ${buttonSizeMixin(emotion)(t.btnFontSizeLarge, t.btnLineHeightLarge, t.btnPaddingXLarge, t.btnPaddingYLarge)}; + `; + }, + + sizeLargeIE11(t: Theme) { + return emotion.css` + ${buttonSizeMixinIE11(emotion)(t.btnPaddingXLarge, t.btnPaddingYLarge)}; + `; + }, + + sizeSmallWithIcon(t: Theme) { + return emotion.css` + padding-left: ${t.btnWithIconPaddingLeftSmall}; + `; + }, + + sizeMediumWithIcon(t: Theme) { + return emotion.css` + padding-left: ${t.btnWithIconPaddingLeftMedium}; + `; + }, + + sizeLargeWithIcon(t: Theme) { + return emotion.css` + padding-left: ${t.btnWithIconPaddingLeftLarge}; + `; + }, + + sizeSmallWithIconWithoutText(t: Theme) { + return emotion.css` + padding-right: ${t.btnWithIconPaddingLeftSmall}; + `; + }, + + sizeMediumWithIconWithoutText(t: Theme) { + return emotion.css` + padding-right: ${t.btnWithIconPaddingLeftMedium}; + `; + }, + + sizeLargeWithIconWithoutText(t: Theme) { + return emotion.css` + padding-right: ${t.btnWithIconPaddingLeftLarge}; + `; + }, + + link(t: Theme) { + return emotion.css` + background: none; + border-radius: ${t.btnLinkBorderRadius}; + border: none; + box-shadow: none; + white-space: nowrap; + color: ${t.btnLinkColor}; + display: inline; + margin: 0; + padding: 0 !important; // override size mixin + height: auto !important; // override size mixin + + &:hover:enabled, + &:active:enabled { + color: ${t.btnLinkHoverColor}; + text-decoration: ${t.btnLinkHoverTextDecoration}; + } + + &:active:enabled { + ${getActiveStyles(emotion).link(t)} + } + `; + }, + + linkLineHeight() { + return emotion.css` + line-height: inherit !important; // override size mixin + `; + }, + + linkLineHeightSafariFallback() { + return emotion.css` + /* Safari overrides 'underline' and 'border' if 'line-height' is used */ + margin: -1px 0 -2px; + `; + }, + + linkFocus(t: Theme) { + return emotion.css` + & { + color: ${t.btnLinkColor}; + text-decoration: ${t.btnLinkHoverTextDecoration}; + } + `; + }, + + linkDisabled(t: Theme) { + return emotion.css` + cursor: default; + + &, + &:hover:enabled, + &:active:enabled { + color: ${t.btnLinkDisabledColor}; + } + `; + }, + + focus(t: Theme) { + return emotion.css` + position: relative; + z-index: 2; + + &, + &:hover:enabled, + &:active:enabled, + &:active:hover:enabled { + box-shadow: + inset 0 0 0 ${t.btnInsetWidth} ${t.btnOutlineColorFocus}, + 0 0 0 ${t.btnFocusShadowWidth} ${t.btnBorderColorFocus} !important; // override root:hover style + } + `; + }, + + disabled(t: Theme) { + return emotion.css` + cursor: default; + pointer-events: none; + box-shadow: 0 0 0 ${t.btnBorderWidth} ${t.btnDisabledBorderColor}; + + background-image: none; + background-color: ${t.btnDisabledBg}; + color: ${t.btnDisabledTextColor}; + `; + }, + + disabledWithoutOutline(t: Theme) { + return emotion.css` + box-shadow: 0 0 0 ${t.btnBorderWidth} ${t.btnDisabledBg}; + `; + }, + + arrowIconRoot() { + return emotion.css` + position: absolute; + height: 100%; + top: 0; + right: 0; + display: flex; + align-items: center; + justify-content: center; + box-sizing: content-box; + `; + }, + + arrowIconRootSmall(t: Theme) { + return emotion.css` + padding: 0 ${t.btnWithIconPaddingLeftSmall} 0 ${t.btnWithIconPaddingLeftSmall}; + width: ${t.btnIconSizeSmall}; + `; + }, + + arrowIconRootMedium(t: Theme) { + return emotion.css` + padding: 0 ${t.btnWithIconPaddingLeftMedium} 0 ${t.btnWithIconPaddingLeftMedium}; + width: ${t.btnIconSizeMedium}; + `; + }, + + arrowIconRootLarge(t: Theme) { + return emotion.css` + padding: 0 ${t.btnWithIconPaddingLeftLarge} 0 ${t.btnWithIconPaddingLeftLarge}; + width: ${t.btnIconSizeLarge}; + `; + }, + + arrowIconLeft() { + return emotion.css` + left: 0; + `; + }, + + default(t: Theme) { + return emotion.css` + ${buttonUseMixin(emotion)( + t.btnDefaultBg, + t.btnDefaultBgStart, + t.btnDefaultBgEnd, + t.btnDefaultTextColor, + t.btnDefaultBorderColor, t.btnBorderWidth, )}; - } - &:active { - ${activeStyles.success(t)} - } - `; - }, - - danger(t: Theme) { - return css` - ${buttonUseMixin( - t.btnDangerBg, - t.btnDangerBgStart, - t.btnDangerBgEnd, - t.btnDangerTextColor, - t.btnDangerBorderColor, - t.btnBorderWidth, - )}; - - &:hover { - ${buttonHoverMixin( - t.btnDangerHoverBg, - t.btnDangerHoverBgStart, - t.btnDangerHoverBgEnd, - t.btnDangerHoverTextColor, - t.btnDangerHoverBorderColor, + &:hover { + ${buttonHoverMixin(emotion)( + t.btnDefaultHoverBg, + t.btnDefaultHoverBgStart, + t.btnDefaultHoverBgEnd, + t.btnDefaultHoverTextColor, + t.btnDefaultHoverBorderColor, + t.btnBorderWidth, + )}; + } + + &:active { + ${getActiveStyles(emotion).default(t)}; + } + `; + }, + + primary(t: Theme) { + return emotion.css` + ${buttonUseMixin(emotion)( + t.btnPrimaryBg, + t.btnPrimaryBgStart, + t.btnPrimaryBgEnd, + t.btnPrimaryTextColor, + t.btnPrimaryBorderColor, t.btnBorderWidth, )}; - } - &:active { - ${activeStyles.danger(t)} - } - `; - }, - - pay(t: Theme) { - return css` - ${buttonUseMixin( - t.btnPayBg, - t.btnPayBgStart, - t.btnPayBgEnd, - t.btnPayTextColor, - t.btnPayBorderColor, - t.btnBorderWidth, - )}; - - &:hover { - ${buttonHoverMixin( - t.btnPayHoverBg, - t.btnPayHoverBgStart, - t.btnPayHoverBgEnd, - t.btnPayHoverTextColor, - t.btnPayHoverBorderColor, + &:hover { + ${buttonHoverMixin(emotion)( + t.btnPrimaryHoverBg, + t.btnPrimaryHoverBgStart, + t.btnPrimaryHoverBgEnd, + t.btnPrimaryHoverTextColor, + t.btnPrimaryHoverBorderColor, + t.btnBorderWidth, + )}; + } + + &:active { + ${getActiveStyles(emotion).primary(t)} + } + `; + }, + + success(t: Theme) { + return emotion.css` + ${buttonUseMixin(emotion)( + t.btnSuccessBg, + t.btnSuccessBgStart, + t.btnSuccessBgEnd, + t.btnSuccessTextColor, + t.btnSuccessBorderColor, t.btnBorderWidth, )}; - } - &:active { - ${activeStyles.pay(t)} - } - `; - }, - - text(t: Theme) { - return css` - &, - &:enabled, - &:hover { - box-shadow: none; - } - - ${buttonUseMixin(t.btnTextBg, '', '', t.btnTextTextColor, t.btnTextBorderColor, t.btnBorderWidth)}; - - &:hover { - ${buttonHoverMixin( - t.btnTextHoverBg, - '', - '', - t.btnTextHoverTextColor, - t.btnTextHoverBorderColor, + &:hover { + ${buttonHoverMixin(emotion)( + t.btnSuccessHoverBg, + t.btnSuccessHoverBgStart, + t.btnSuccessHoverBgEnd, + t.btnSuccessHoverTextColor, + t.btnSuccessHoverBorderColor, + t.btnBorderWidth, + )}; + } + + &:active { + ${getActiveStyles(emotion).success(t)} + } + `; + }, + + danger(t: Theme) { + return emotion.css` + ${buttonUseMixin(emotion)( + t.btnDangerBg, + t.btnDangerBgStart, + t.btnDangerBgEnd, + t.btnDangerTextColor, + t.btnDangerBorderColor, t.btnBorderWidth, )}; - } - &:active { - ${activeStyles.text(t)} - } - `; - }, - - backless(t: Theme) { - return css` - ${buttonUseMixin(t.btnBacklessBg, '', '', t.btnBacklessTextColor, t.btnBacklessBorderColor, t.btnBorderWidth)}; - - color: ${t.btnDefaultTextColor}; - background: transparent; - - &:hover { - ${buttonHoverMixin( - t.btnBacklessHoverBg, - '', - '', - t.btnBacklessHoverTextColor, - t.btnBacklessHoverBorderColor, + &:hover { + ${buttonHoverMixin(emotion)( + t.btnDangerHoverBg, + t.btnDangerHoverBgStart, + t.btnDangerHoverBgEnd, + t.btnDangerHoverTextColor, + t.btnDangerHoverBorderColor, + t.btnBorderWidth, + )}; + } + + &:active { + ${getActiveStyles(emotion).danger(t)} + } + `; + }, + + pay(t: Theme) { + return emotion.css` + ${buttonUseMixin(emotion)( + t.btnPayBg, + t.btnPayBgStart, + t.btnPayBgEnd, + t.btnPayTextColor, + t.btnPayBorderColor, t.btnBorderWidth, )}; - } - &:active { - ${activeStyles.backless(t)} - } - `; - }, - - checked(t: Theme) { - const checkedStyles = ` + &:hover { + ${buttonHoverMixin(emotion)( + t.btnPayHoverBg, + t.btnPayHoverBgStart, + t.btnPayHoverBgEnd, + t.btnPayHoverTextColor, + t.btnPayHoverBorderColor, + t.btnBorderWidth, + )}; + } + + &:active { + ${getActiveStyles(emotion).pay(t)} + } + `; + }, + + text(t: Theme) { + return emotion.css` + &, + &:enabled, + &:hover { + box-shadow: none; + } + + ${buttonUseMixin(emotion)(t.btnTextBg, '', '', t.btnTextTextColor, t.btnTextBorderColor, t.btnBorderWidth)}; + + &:hover { + ${buttonHoverMixin(emotion)( + t.btnTextHoverBg, + '', + '', + t.btnTextHoverTextColor, + t.btnTextHoverBorderColor, + t.btnBorderWidth, + )}; + } + + &:active { + ${getActiveStyles(emotion).text(t)} + } + `; + }, + + backless(t: Theme) { + return emotion.css` + ${buttonUseMixin(emotion)(t.btnBacklessBg, '', '', t.btnBacklessTextColor, t.btnBacklessBorderColor, t.btnBorderWidth)}; + + color: ${t.btnDefaultTextColor}; + background: transparent; + + &:hover { + ${buttonHoverMixin(emotion)( + t.btnBacklessHoverBg, + '', + '', + t.btnBacklessHoverTextColor, + t.btnBacklessHoverBorderColor, + t.btnBorderWidth, + )}; + } + + &:active { + ${getActiveStyles(emotion).backless(t)} + } + `; + }, + + checked(t: Theme) { + const checkedStyles = ` background-image: none; box-shadow: 0 0 0 ${t.btnBorderWidth} ${t.btnDefaultCheckedBorderColor} !important; background-color: ${t.btnCheckedBg} !important; @@ -590,248 +597,249 @@ export const styles = memoizeStyle({ } `; - return css` - ${checkedStyles} - - &:hover:enabled, - &:active:enabled, - &:hover:active:enabled { + return emotion.css` ${checkedStyles} - } - `; - }, - checkedFocused(t: Theme) { - return css` - &:hover:enabled, + &:hover:enabled, + &:active:enabled, &:hover:active:enabled { - box-shadow: - inset 0 0 0 ${t.btnInsetWidth} ${t.btnOutlineColorFocus}, - 0 0 0 ${t.btnFocusShadowWidth} ${t.btnBorderColorFocus} !important; - border-color: ${t.btnBorderColorFocus} !important; - } - `; - }, - - checkedDisabled(t: Theme) { - return css` - box-shadow: 0 0 0 ${t.btnBorderWidth} ${t.btnCheckedDisabledBorderColor}; - background-color: ${t.btnCheckedDisabledBg}; - color: ${t.btnCheckedDisabledColor}; - - .${globalClasses.innerShadow} { - box-shadow: ${t.btnCheckedDisabledShadow}; - } - - svg { - color: ${t.btnCheckedDisabledColor} !important; - } - `; - }, - - caption() { - return css` - position: relative; - white-space: nowrap; - display: inline-block; - width: 100%; - vertical-align: top; - `; - }, - - captionLink() { - return css` - display: inline; - transform: none !important; // override root:active style - `; - }, - - captionDisabled() { - return css` - transform: none !important; // override root:active style - `; - }, - - wrap(t: Theme) { - return css` - box-sizing: border-box; - display: inline-block; - line-height: normal; - padding: ${t.btnBorderWidth}; - `; - }, - - wrapSmall(t: Theme) { - return css` - height: ${t.btnHeightSmall}; - `; - }, - - wrapMedium(t: Theme) { - return css` - height: ${t.btnHeightMedium}; - `; - }, - - wrapLarge(t: Theme) { - return css` - height: ${t.btnHeightLarge}; - `; - }, - - narrow() { - return css` - padding-left: 5px; - padding-right: 5px; - `; - }, - - noPadding() { - return css` - padding-left: 0; - padding-right: 0; - `; - }, - - noRightPadding() { - return css` - padding-right: 0; - `; - }, - - wrapLink() { - return css` - padding: 0; - `; - }, - - borderless() { - return css` - &, - &:active:hover, - &:hover { - box-shadow: none !important; // override root:hover style - } - `; - }, - - backlessDisabled(t: Theme) { - return css` - box-shadow: 0 0 0 1px ${t.btnBacklessDisabledBorderColor}; - background-color: transparent; - `; - }, - - textDisabled() { - return css` - background-color: transparent; - `; - }, - - loading() { - return css` - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - border-radius: inherit; - display: flex; - align-items: center; - justify-content: center; - z-index: 10; - `; - }, - - visibilityHidden() { - return css` - visibility: hidden; - `; - }, -}); - -export const activeStyles = memoizeStyle({ - default(t: Theme) { - return css` - & { - ${buttonActiveMixin( - t.btnDefaultActiveBg, - t.btnDefaultActiveShadow, - t.btnDefaultActiveBorderColor, - t.btnBorderWidth, - )}; - } - `; - }, - - primary(t: Theme) { - return css` - & { - ${buttonActiveMixin( - t.btnPrimaryActiveBg, - t.btnPrimaryActiveShadow, - t.btnPrimaryActiveBorderColor, - t.btnBorderWidth, - )}; - } - `; - }, - - success(t: Theme) { - return css` - & { - ${buttonActiveMixin( - t.btnSuccessActiveBg, - t.btnSuccessActiveShadow, - t.btnSuccessActiveBorderColor, - t.btnBorderWidth, - )}; - } - `; - }, - - danger(t: Theme) { - return css` - & { - ${buttonActiveMixin( - t.btnDangerActiveBg, - t.btnDangerActiveShadow, - t.btnDangerActiveBorderColor, - t.btnBorderWidth, - )}; - } - `; - }, - - pay(t: Theme) { - return css` - & { - ${buttonActiveMixin(t.btnPayActiveBg, t.btnPayActiveShadow, t.btnPayActiveBorderColor, t.btnBorderWidth)}; - } - `; - }, - - link(t: Theme) { - return css` - & { - color: ${t.btnLinkActiveColor}; - } - `; - }, - - text(t: Theme) { - return css` - & { - ${buttonActiveMixin(t.btnTextActiveBg, '', t.btnTextActiveBg, t.btnBorderWidth)}; - } - `; - }, - - backless(t: Theme) { - return css` - & { - ${buttonActiveMixin(t.btnBacklessActiveBg, '', t.btnBacklessActiveBorderColor, t.btnBorderWidth)} - } - `; - }, -}); + ${checkedStyles} + } + `; + }, + + checkedFocused(t: Theme) { + return emotion.css` + &:hover:enabled, + &:hover:active:enabled { + box-shadow: + inset 0 0 0 ${t.btnInsetWidth} ${t.btnOutlineColorFocus}, + 0 0 0 ${t.btnFocusShadowWidth} ${t.btnBorderColorFocus} !important; + border-color: ${t.btnBorderColorFocus} !important; + } + `; + }, + + checkedDisabled(t: Theme) { + return emotion.css` + box-shadow: 0 0 0 ${t.btnBorderWidth} ${t.btnCheckedDisabledBorderColor}; + background-color: ${t.btnCheckedDisabledBg}; + color: ${t.btnCheckedDisabledColor}; + + .${globalClasses.innerShadow} { + box-shadow: ${t.btnCheckedDisabledShadow}; + } + + svg { + color: ${t.btnCheckedDisabledColor} !important; + } + `; + }, + + caption() { + return emotion.css` + position: relative; + white-space: nowrap; + display: inline-block; + width: 100%; + vertical-align: top; + `; + }, + + captionLink() { + return emotion.css` + display: inline; + transform: none !important; // override root:active style + `; + }, + + captionDisabled() { + return emotion.css` + transform: none !important; // override root:active style + `; + }, + + wrap(t: Theme) { + return emotion.css` + box-sizing: border-box; + display: inline-block; + line-height: normal; + padding: ${t.btnBorderWidth}; + `; + }, + + wrapSmall(t: Theme) { + return emotion.css` + height: ${t.btnHeightSmall}; + `; + }, + + wrapMedium(t: Theme) { + return emotion.css` + height: ${t.btnHeightMedium}; + `; + }, + + wrapLarge(t: Theme) { + return emotion.css` + height: ${t.btnHeightLarge}; + `; + }, + + narrow() { + return emotion.css` + padding-left: 5px; + padding-right: 5px; + `; + }, + + noPadding() { + return emotion.css` + padding-left: 0; + padding-right: 0; + `; + }, + + noRightPadding() { + return emotion.css` + padding-right: 0; + `; + }, + + wrapLink() { + return emotion.css` + padding: 0; + `; + }, + + borderless() { + return emotion.css` + &, + &:active:hover, + &:hover { + box-shadow: none !important; // override root:hover style + } + `; + }, + + backlessDisabled(t: Theme) { + return emotion.css` + box-shadow: 0 0 0 1px ${t.btnBacklessDisabledBorderColor}; + background-color: transparent; + `; + }, + + textDisabled() { + return emotion.css` + background-color: transparent; + `; + }, + + loading() { + return emotion.css` + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + border-radius: inherit; + display: flex; + align-items: center; + justify-content: center; + z-index: 10; + `; + }, + + visibilityHidden() { + return emotion.css` + visibility: hidden; + `; + }, + }); + +export const getActiveStyles = (emotion: Emotion) => + memoizeStyle({ + default(t: Theme) { + return emotion.css` + & { + ${buttonActiveMixin(emotion)( + t.btnDefaultActiveBg, + t.btnDefaultActiveShadow, + t.btnDefaultActiveBorderColor, + t.btnBorderWidth, + )}; + } + `; + }, + + primary(t: Theme) { + return emotion.css` + & { + ${buttonActiveMixin(emotion)( + t.btnPrimaryActiveBg, + t.btnPrimaryActiveShadow, + t.btnPrimaryActiveBorderColor, + t.btnBorderWidth, + )}; + } + `; + }, + + success(t: Theme) { + return emotion.css` + & { + ${buttonActiveMixin(emotion)( + t.btnSuccessActiveBg, + t.btnSuccessActiveShadow, + t.btnSuccessActiveBorderColor, + t.btnBorderWidth, + )}; + } + `; + }, + + danger(t: Theme) { + return emotion.css` + & { + ${buttonActiveMixin(emotion)( + t.btnDangerActiveBg, + t.btnDangerActiveShadow, + t.btnDangerActiveBorderColor, + t.btnBorderWidth, + )}; + } + `; + }, + + pay(t: Theme) { + return emotion.css` + & { + ${buttonActiveMixin(emotion)(t.btnPayActiveBg, t.btnPayActiveShadow, t.btnPayActiveBorderColor, t.btnBorderWidth)}; + } + `; + }, + + link(t: Theme) { + return emotion.css` + & { + color: ${t.btnLinkActiveColor}; + } + `; + }, + + text(t: Theme) { + return emotion.css` + & { + ${buttonActiveMixin(emotion)(t.btnTextActiveBg, '', t.btnTextActiveBg, t.btnBorderWidth)}; + } + `; + }, + + backless(t: Theme) { + return emotion.css` + & { + ${buttonActiveMixin(emotion)(t.btnBacklessActiveBg, '', t.btnBacklessActiveBorderColor, t.btnBorderWidth)} + } + `; + }, + }); diff --git a/packages/react-ui/components/Button/Button.tsx b/packages/react-ui/components/Button/Button.tsx index 4722b68777d..7d4738a9b12 100644 --- a/packages/react-ui/components/Button/Button.tsx +++ b/packages/react-ui/components/Button/Button.tsx @@ -1,5 +1,6 @@ import React, { HTMLAttributes } from 'react'; import { globalObject } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { ButtonLinkAllowedValues } from '../../lib/types/button-link'; import { isKonturIcon, isReactUIComponent } from '../../lib/utils'; @@ -8,7 +9,7 @@ import { keyListener } from '../../lib/events/keyListener'; import { Theme, ThemeIn } from '../../lib/theming/Theme'; import { ThemeContext } from '../../lib/theming/ThemeContext'; import { CommonWrapper, CommonProps, CommonWrapperRestProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { ThemeFactory } from '../../lib/theming/ThemeFactory'; import { createPropsGetter } from '../../lib/createPropsGetter'; @@ -16,7 +17,7 @@ import { Link } from '../Link'; import { SizeProp } from '../../lib/types/props'; import { PolymorphicPropsWithoutRef } from '../../lib/types/polymorphic-component'; -import { styles, activeStyles, globalClasses } from './Button.styles'; +import { getStyles, getActiveStyles, globalClasses } from './Button.styles'; import { ButtonIcon, ButtonIconProps, getButtonIconSizes } from './ButtonIcon'; import { useButtonArrow } from './ButtonArrow'; import { getInnerLinkTheme } from './getInnerLinkTheme'; @@ -194,6 +195,8 @@ export class Button; private node: HTMLElement | null = null; private setRootNode!: TSetRootNode; @@ -231,16 +234,24 @@ export class Button - {(theme) => { - this.theme = this.props.theme ? ThemeFactory.create(this.props.theme as Theme, theme) : theme; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); return ( - - {this.renderMain} - + + {(theme) => { + this.theme = this.props.theme ? ThemeFactory.create(this.props.theme as Theme, theme) : theme; + return ( + + {this.renderMain} + + ); + }} + ); }} - + ); } @@ -290,15 +301,16 @@ export class Button @@ -460,22 +472,23 @@ export class Button = ({ arrow, size = Button.defaultProps.size }) => { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); const getArrowIconRootClassName = () => { - return cx(styles.arrowIconRoot(), globalClasses.arrow, { + return emotion.cx(styles.arrowIconRoot(), globalClasses.arrow, { [styles.arrowIconRootSmall(theme)]: size === 'small', [styles.arrowIconRootMedium(theme)]: size === 'medium', [styles.arrowIconRootLarge(theme)]: size === 'large', @@ -43,13 +46,14 @@ const ButtonArrow: React.FunctionComponent = ({ arrow, size = return arrowNode; }; -export function useButtonArrow(props: ButtonArrowProps, theme: Theme): [string, React.ReactNode] { +export function useButtonArrow(props: ButtonArrowProps, theme: Theme, emotion: Emotion): [string, React.ReactNode] { const { arrow, size, use } = props; + const styles = getStyles(emotion); const canRender = use !== 'link' && (arrow === true || arrow === 'left'); const rootClassName = canRender - ? cx( + ? emotion.cx( arrow === true && size === 'small' && styles.withArrowIconRightSmall(theme), arrow === true && size === 'medium' && styles.withArrowIconRightMedium(theme), arrow === true && size === 'large' && styles.withArrowIconRightLarge(theme), diff --git a/packages/react-ui/components/Button/ButtonIcon.styles.ts b/packages/react-ui/components/Button/ButtonIcon.styles.ts index 02ff3873115..ab79485a3e3 100644 --- a/packages/react-ui/components/Button/ButtonIcon.styles.ts +++ b/packages/react-ui/components/Button/ButtonIcon.styles.ts @@ -1,89 +1,92 @@ +import type { Emotion } from '@emotion/css/create-instance'; + import { ZERO_WIDTH_SPACE_CSS } from '../../lib/chars'; -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; -export const styles = memoizeStyle({ - icon() { - const space = `'${ZERO_WIDTH_SPACE_CSS}'`; - return css` +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + icon() { + const space = `'${ZERO_WIDTH_SPACE_CSS}'`; + return emotion.css` display: inline-block; &::before { content: ${space}; } `; - }, + }, - iconSmall(t: Theme) { - return css` + iconSmall(t: Theme) { + return emotion.css` width: ${t.btnIconSizeSmall}; `; - }, + }, - iconSmallLeft(t: Theme) { - return css` + iconSmallLeft(t: Theme) { + return emotion.css` margin-right: ${t.btnIconGapSmallLeft}; `; - }, + }, - iconSmallRight(t: Theme) { - return css` + iconSmallRight(t: Theme) { + return emotion.css` margin-left: ${t.btnIconGapSmallRight}; `; - }, + }, - iconMedium(t: Theme) { - return css` + iconMedium(t: Theme) { + return emotion.css` width: ${t.btnIconSizeMedium}; `; - }, + }, - iconMediumLeft(t: Theme) { - return css` + iconMediumLeft(t: Theme) { + return emotion.css` margin-right: ${t.btnIconGapMediumRight}; `; - }, + }, - iconMediumRight(t: Theme) { - return css` + iconMediumRight(t: Theme) { + return emotion.css` margin-left: ${t.btnIconGapMediumRight}; `; - }, + }, - iconLarge(t: Theme) { - return css` + iconLarge(t: Theme) { + return emotion.css` width: ${t.btnIconSizeLarge}; `; - }, + }, - iconLargeLeft(t: Theme) { - return css` + iconLargeLeft(t: Theme) { + return emotion.css` margin-right: ${t.btnIconGapLargeLeft}; `; - }, + }, - iconLargeRight(t: Theme) { - return css` + iconLargeRight(t: Theme) { + return emotion.css` margin-left: ${t.btnIconGapLargeRight}; `; - }, + }, - iconLeftLink(t: Theme) { - return css` + iconLeftLink(t: Theme) { + return emotion.css` margin-right: ${t.btnLinkIconMarginRight}; `; - }, + }, - iconRightLink(t: Theme) { - return css` + iconRightLink(t: Theme) { + return emotion.css` margin-left: ${t.btnLinkIconMarginLeft}; `; - }, + }, - iconNoMargin() { - return css` + iconNoMargin() { + return emotion.css` margin-right: 0; margin-left: 0; `; - }, -}); + }, + }); diff --git a/packages/react-ui/components/Button/ButtonIcon.tsx b/packages/react-ui/components/Button/ButtonIcon.tsx index 71b1267cacb..64a03ab57a8 100644 --- a/packages/react-ui/components/Button/ButtonIcon.tsx +++ b/packages/react-ui/components/Button/ButtonIcon.tsx @@ -2,12 +2,12 @@ import React, { useContext } from 'react'; import { Theme } from '../../lib/theming/Theme'; import { isKonturIcon } from '../../lib/utils'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { ThemeContext } from '../../lib/theming/ThemeContext'; import { SizeProp } from '../../lib/types/props'; import { ButtonInnerProps } from './Button'; -import { styles } from './ButtonIcon.styles'; +import { getStyles } from './ButtonIcon.styles'; import { LoadingButtonIcon } from './LoadingButtonIcon'; export interface ButtonIconProps extends Pick { @@ -44,7 +44,10 @@ export const ButtonIcon: React.FunctionComponent = ({ size = 'small', }) => { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const isLink = use === 'link'; + const styles = getStyles(emotion); const getSizeIconClassName = () => { switch (size) { @@ -77,7 +80,7 @@ export const ButtonIcon: React.FunctionComponent = ({ return ( { + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); return (
{} diff --git a/packages/react-ui/components/Calendar/Calendar.styles.ts b/packages/react-ui/components/Calendar/Calendar.styles.ts index a4700654df6..b83e0e18adf 100644 --- a/packages/react-ui/components/Calendar/Calendar.styles.ts +++ b/packages/react-ui/components/Calendar/Calendar.styles.ts @@ -1,33 +1,36 @@ -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; -export const styles = memoizeStyle({ - root(t: Theme) { - const width = parseInt(t.calendarCellWidth) * 7; - return css` - background: ${t.calendarBg}; - box-sizing: content-box; - border-radius: ${t.calendarBorderRadius}; - color: ${t.textColorDefault}; - display: block; - padding: 0 ${t.calendarPaddingX}; - width: ${width}px; - touch-action: none; - `; - }, +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root(t: Theme) { + const width = parseInt(t.calendarCellWidth) * 7; + return emotion.css` + background: ${t.calendarBg}; + box-sizing: content-box; + border-radius: ${t.calendarBorderRadius}; + color: ${t.textColorDefault}; + display: block; + padding: 0 ${t.calendarPaddingX}; + width: ${width}px; + touch-action: none; + `; + }, - wrapper() { - return css` - font-size: 14px; - position: relative; - overflow: hidden; - `; - }, + wrapper() { + return emotion.css` + font-size: 14px; + position: relative; + overflow: hidden; + `; + }, - separator(t: Theme) { - return css` - border-bottom: ${t.calendarBottomSeparatorBorder}; - margin: 0 ${t.calendarMonthTitleMarginX}; - `; - }, -}); + separator(t: Theme) { + return emotion.css` + border-bottom: ${t.calendarBottomSeparatorBorder}; + margin: 0 ${t.calendarMonthTitleMarginX}; + `; + }, + }); diff --git a/packages/react-ui/components/Calendar/Calendar.tsx b/packages/react-ui/components/Calendar/Calendar.tsx index d2fa01d6c4d..081b0e5b43e 100644 --- a/packages/react-ui/components/Calendar/Calendar.tsx +++ b/packages/react-ui/components/Calendar/Calendar.tsx @@ -3,29 +3,30 @@ import normalizeWheel from 'normalize-wheel'; import throttle from 'lodash.throttle'; import shallowEqual from 'shallowequal'; import { globalObject, SafeTimer } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { isInstanceOf } from '../../lib/isInstanceOf'; import { InternalDate } from '../../lib/date/InternalDate'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; import { MAX_DATE, MAX_MONTH, MAX_YEAR, MIN_DATE, MIN_MONTH, MIN_YEAR } from '../../lib/date/constants'; import { Nullable, Range } from '../../typings/utility-types'; import { Theme } from '../../lib/theming/Theme'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { animation } from '../../lib/animation'; import { isMobile } from '../../lib/client'; import { createPropsGetter } from '../../lib/createPropsGetter'; import { InternalDateTransformer } from '../../lib/date/InternalDateTransformer'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { themeConfig } from './config'; import { MonthViewModel } from './MonthViewModel'; import * as CalendarScrollEvents from './CalendarScrollEvents'; import { Month } from './Month'; -import { styles } from './Calendar.styles'; import { CalendarDateShape, create, isGreater, isLess } from './CalendarDateShape'; import * as CalendarUtils from './CalendarUtils'; import { CalendarContext, CalendarContextProps } from './CalendarContext'; +import { getStyles } from './Calendar.styles'; import { CalendarDay, CalendarDayProps } from './CalendarDay'; export interface CalendarProps extends CommonProps { @@ -133,6 +134,8 @@ export class Calendar extends React.Component { private getProps = createPropsGetter(Calendar.defaultProps); private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private wheelEndTimeout: SafeTimer; private root: Nullable; private animation = animation(); @@ -196,12 +199,20 @@ export class Calendar extends React.Component { public render() { return ( - - {(theme) => { - this.theme = theme; - return this.renderMain(); + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( + + {(theme) => { + this.theme = theme; + return this.renderMain(); + }} + + ); }} - + ); } @@ -336,7 +347,6 @@ export class Calendar extends React.Component { const wrapperStyle = { height: themeConfig(this.theme).WRAPPER_HEIGHT }; const props = this.getProps(); - const context: CalendarContextProps = { value: this.getDateInNativeFormat(props.value), minDate: this.getDateInNativeFormat(props.minDate), @@ -349,13 +359,17 @@ export class Calendar extends React.Component { return ( -
-
+
+
{monthsForRender.map(this.renderMonth, this)}
-
+
); diff --git a/packages/react-ui/components/Calendar/CalendarDay.tsx b/packages/react-ui/components/Calendar/CalendarDay.tsx index 8e491891ee5..dd3c01887d8 100644 --- a/packages/react-ui/components/Calendar/CalendarDay.tsx +++ b/packages/react-ui/components/Calendar/CalendarDay.tsx @@ -1,15 +1,15 @@ -import React, { PropsWithChildren, useContext, memo } from 'react'; +import React, { memo, PropsWithChildren, useContext } from 'react'; import { useLocaleForControl } from '../../lib/locale/useLocaleForControl'; import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { DatePickerLocaleHelper } from '../DatePicker/locale'; import { InternalDate } from '../../lib/date/InternalDate'; import { LocaleContext } from '../../lib/locale'; import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/utils/getVisualStateDataAttributes'; import { forwardRefAndName } from '../../lib/forwardRefAndName'; -import { styles } from './DayCellView.styles'; +import { getStyles } from './DayCellView.styles'; import { CalendarDataTids } from './Calendar'; export interface CalendarDayProps extends React.HTMLAttributes { @@ -40,6 +40,7 @@ export const CalendarDay = memo( ref: React.Ref, ) { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); const { langCode } = useContext(LocaleContext); const internalDate = new InternalDate({ langCode, value: date }); @@ -49,6 +50,7 @@ export const CalendarDay = memo( const { date: day } = internalDate.getComponentsLikeNumber(); const caption = children ?? day; + const styles = getStyles(emotion); return ( ); }, diff --git a/packages/react-ui/components/Calendar/DayCellView.styles.ts b/packages/react-ui/components/Calendar/DayCellView.styles.ts index 71914ec1fbc..1a4139f92f4 100644 --- a/packages/react-ui/components/Calendar/DayCellView.styles.ts +++ b/packages/react-ui/components/Calendar/DayCellView.styles.ts @@ -1,62 +1,67 @@ -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { resetButton } from '../../lib/styles/Mixins'; -export const styles = memoizeStyle({ - cell(t: Theme) { - return css` - flex: 1 1 ${t.calendarCellWidth}; - height: ${t.calendarCellHeight}; - `; - }, - day(t: Theme) { - return css` - ${resetButton()}; - width: 100%; - height: 100%; - - background: ${t.calendarCellBg}; - border: 1px solid transparent; - font-size: ${t.calendarCellFontSize}; - padding: 0; - text-align: center; - user-select: none; - position: relative; - line-height: ${t.calendarCellLineHeight}; - border-radius: ${t.calendarCellBorderRadius}; - - &:hover { - background-color: ${t.calendarCellHoverBgColor}; - color: ${t.calendarCellHoverColor}; - cursor: pointer; - } - &:disabled { - opacity: 0.5; - pointer-events: none; - } - &:active:hover:enabled { - color: ${t.calendarCellActiveHoverColor}; - } +export const getStyles = (emotion: Emotion) => { + return memoizeStyle({ + cell(t: Theme) { + return emotion.css` + flex: 1 1 ${t.calendarCellWidth}; + height: ${t.calendarCellHeight}; `; - }, + }, - selected(t: Theme) { - return css` - background-color: ${t.calendarCellSelectedBgColor}; - color: ${t.calendarCellSelectedFontColor}; - `; - }, + day(t: Theme) { + return emotion.css` + ${resetButton(emotion)}; + width: 100%; + height: 100%; - weekend(t: Theme) { - return css` - color: ${t.calendarCellWeekendColor}; - `; - }, + background: ${t.calendarCellBg}; + border: 1px solid transparent; + font-size: ${t.calendarCellFontSize}; + padding: 0; + text-align: center; + user-select: none; + position: relative; + line-height: ${t.calendarCellLineHeight}; + border-radius: ${t.calendarCellBorderRadius}; - todayCaption(t: Theme) { - return css` - padding-bottom: 2px; - border-bottom: ${t.calendarCellTodayBorder}; - `; - }, -}); + &:hover { + background-color: ${t.calendarCellHoverBgColor}; + color: ${t.calendarCellHoverColor}; + cursor: pointer; + } + &:disabled { + opacity: 0.5; + pointer-events: none; + } + &:active:hover:enabled { + color: ${t.calendarCellActiveHoverColor}; + } + `; + }, + + selected(t: Theme) { + return emotion.css` + background-color: ${t.calendarCellSelectedBgColor}; + color: ${t.calendarCellSelectedFontColor}; + `; + }, + + weekend(t: Theme) { + return emotion.css` + color: ${t.calendarCellWeekendColor}; + `; + }, + + todayCaption(t: Theme) { + return emotion.css` + padding-bottom: 2px; + border-bottom: ${t.calendarCellTodayBorder}; + `; + }, + }); +}; diff --git a/packages/react-ui/components/Calendar/DayCellView.tsx b/packages/react-ui/components/Calendar/DayCellView.tsx index 2a2b22efe32..07f506dcd46 100644 --- a/packages/react-ui/components/Calendar/DayCellView.tsx +++ b/packages/react-ui/components/Calendar/DayCellView.tsx @@ -1,9 +1,10 @@ import React, { useContext } from 'react'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { InternalDateTransformer } from '../../lib/date/InternalDateTransformer'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './DayCellView.styles'; +import { getStyles } from './DayCellView.styles'; import { CalendarContext } from './CalendarContext'; import { DayCellViewModel } from './DayCellViewModel'; import * as CDS from './CalendarDateShape'; @@ -16,6 +17,7 @@ export interface DayCellViewProps { export const DayCellView = (props: DayCellViewProps) => { const { date } = props; const { value, minDate, maxDate, isHoliday, renderDay, today, onDateClick } = useContext(CalendarContext); + const emotion = useContext(EmotionContext); const theme = useContext(ThemeContext); const handleClick = () => { @@ -23,6 +25,7 @@ export const DayCellView = (props: DayCellViewProps) => { }; const humanDateString = InternalDateTransformer.dateToHumanString(date); + const styles = getStyles(emotion); const dayProps: CalendarDayProps = { isToday: Boolean(today && CDS.isEqual(date, today)), diff --git a/packages/react-ui/components/Calendar/Month.tsx b/packages/react-ui/components/Calendar/Month.tsx index 293322ee42b..ff079792f0e 100644 --- a/packages/react-ui/components/Calendar/Month.tsx +++ b/packages/react-ui/components/Calendar/Month.tsx @@ -1,16 +1,18 @@ import React from 'react'; +import type { Emotion } from '@emotion/css/create-instance'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme } from '../../lib/theming/Theme'; import { DateSelect } from '../../internal/DateSelect'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { MonthViewModel } from './MonthViewModel'; import { DayCellViewModel } from './DayCellViewModel'; import { MonthView } from './MonthView'; import { DayCellView } from './DayCellView'; import * as CalendarScrollEvents from './CalendarScrollEvents'; -import { styles } from './MonthView.styles'; -import { styles as cellStyles } from './DayCellView.styles'; +import { getStyles } from './MonthView.styles'; +import { getStyles as getCellStyles } from './DayCellView.styles'; interface MonthProps { top: number; @@ -20,7 +22,6 @@ interface MonthProps { export class Month extends React.Component { private theme!: Theme; - private monthSelect: DateSelect | null = null; private yearSelect: DateSelect | null = null; @@ -107,6 +108,8 @@ interface MonthDayGridProps { class MonthDayGrid extends React.Component { private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; public shouldComponentUpdate(nextProps: MonthDayGridProps) { return this.props.days !== nextProps.days; @@ -114,16 +117,25 @@ class MonthDayGrid extends React.Component { public render() { return ( - - {(theme) => { - this.theme = theme; - return this.renderMain(); + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( + + {(theme) => { + this.theme = theme; + return this.renderMain(); + }} + + ); }} - + ); } public renderMain() { + const cellStyles = getCellStyles(this.emotion); const leadingDays = Array.from({ length: this.props.offset }, (_, i) => (
)); @@ -136,9 +148,9 @@ class MonthDayGrid extends React.Component { }); const weeks = divideToWeeks(leadingDays.concat(days, trailingDays)); return ( -
+
{weeks.map((week, i) => ( -
+
{week}
))} diff --git a/packages/react-ui/components/Calendar/MonthView.styles.ts b/packages/react-ui/components/Calendar/MonthView.styles.ts index 8f8a597e32b..83d4fab69b6 100644 --- a/packages/react-ui/components/Calendar/MonthView.styles.ts +++ b/packages/react-ui/components/Calendar/MonthView.styles.ts @@ -1,74 +1,77 @@ -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; -export const styles = memoizeStyle({ - headerMonth(t: Theme) { - return css` - display: inline-block; - padding: ${t.calendarMonthTitlePaddingTop} 0 ${t.calendarMonthTitlePaddingBottom}; - `; - }, +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + headerMonth(t: Theme) { + return emotion.css` + display: inline-block; + padding: ${t.calendarMonthTitlePaddingTop} 0 ${t.calendarMonthTitlePaddingBottom}; + `; + }, - headerYear(t: Theme) { - return css` - display: inline-block; - position: absolute; - right: 0; - padding: ${t.calendarMonthTitlePaddingTop} 0 ${t.calendarMonthTitlePaddingBottom}; - `; - }, + headerYear(t: Theme) { + return emotion.css` + display: inline-block; + position: absolute; + right: 0; + padding: ${t.calendarMonthTitlePaddingTop} 0 ${t.calendarMonthTitlePaddingBottom}; + `; + }, - month(t: Theme) { - const width = parseInt(t.calendarCellWidth) * 7; - return css` - position: absolute; - width: ${width}px; - `; - }, + month(t: Theme) { + const width = parseInt(t.calendarCellWidth) * 7; + return emotion.css` + position: absolute; + width: ${width}px; + `; + }, - monthMobile() { - return css` - width: 100%; - `; - }, + monthMobile() { + return emotion.css` + width: 100%; + `; + }, - header() { - return css` - position: relative; - `; - }, + header() { + return emotion.css` + position: relative; + `; + }, - headerSticky(t: Theme) { - return css` - background-color: ${t.calendarMonthHeaderStickedBgColor}; - z-index: 2; - `; - }, + headerSticky(t: Theme) { + return emotion.css` + background-color: ${t.calendarMonthHeaderStickedBgColor}; + z-index: 2; + `; + }, - monthTitle(t: Theme) { - return css` - border-bottom: 1px solid ${t.calendarMonthTitleBorderBottomColor}; - font-weight: ${t.dateSelectFontWeight}; - margin: 0 ${t.calendarMonthTitleMarginX} ${t.calendarMonthTitleMarginBottom}; - line-height: ${t.calendarMonthTitleLineHeight}; - `; - }, + monthTitle(t: Theme) { + return emotion.css` + border-bottom: 1px solid ${t.calendarMonthTitleBorderBottomColor}; + font-weight: ${t.dateSelectFontWeight}; + margin: 0 ${t.calendarMonthTitleMarginX} ${t.calendarMonthTitleMarginBottom}; + line-height: ${t.calendarMonthTitleLineHeight}; + `; + }, - /* Note: this could've been - * display: grid; - * grid-template-columns: repeat(7, minmax(0, 1fr)); - */ - monthDayGrid(t: Theme) { - return css` - line-height: ${t.calendarCellLineHeight}; + /* Note: this could've been + * display: grid; + * grid-template-columns: repeat(7, minmax(0, 1fr)); + */ + monthDayGrid(t: Theme) { + return emotion.css` + line-height: ${t.calendarCellLineHeight}; display: flex; flex-direction: column; padding: ${t.calendarGridRowSpacing} 0px; `; - }, + }, - monthDayRow(t: Theme) { - return css` + monthDayRow(t: Theme) { + return emotion.css` display: flex; justify-content: space-between; @@ -76,10 +79,10 @@ export const styles = memoizeStyle({ margin-top: ${t.calendarGridRowSpacing}; } `; - }, + }, - nativeSelect() { - return css` + nativeSelect() { + return emotion.css` position: absolute; top: -12px; right: 0; @@ -89,6 +92,6 @@ export const styles = memoizeStyle({ appearance: none; opacity: 0; border: none; - `; - }, -}); + `; + }, + }); diff --git a/packages/react-ui/components/Calendar/MonthView.tsx b/packages/react-ui/components/Calendar/MonthView.tsx index dd12971f193..d01e249a698 100644 --- a/packages/react-ui/components/Calendar/MonthView.tsx +++ b/packages/react-ui/components/Calendar/MonthView.tsx @@ -1,13 +1,13 @@ import React, { useContext } from 'react'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import * as ColorFunctions from '../../lib/styles/ColorFunctions'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { useResponsiveLayout } from '../../components/ResponsiveLayout'; import { Nullable } from '../..//typings/utility-types'; import { DateSelect } from '../../internal/DateSelect'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './MonthView.styles'; +import { getStyles } from './MonthView.styles'; import { themeConfig } from './config'; import * as CDS from './CalendarDateShape'; import { CalendarDataTids } from './Calendar'; @@ -52,6 +52,7 @@ interface MonthViewProps { export function MonthView(props: MonthViewProps) { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); const { minDate, maxDate } = useContext(CalendarContext); const { isMobile } = useResponsiveLayout(); @@ -79,16 +80,17 @@ export function MonthView(props: MonthViewProps) { const monthSelectDisabled = top > 52 || headerTop < 0 || headerTop >= height - themeConfig(theme).MONTH_TITLE_HEIGHT; const yearSelectDisabled = top > 52 || (isLastInYear && top < -height + themeConfig(theme).MONTH_TITLE_HEIGHT); + const styles = getStyles(emotion); return (
+ memoizeStyle({ + root() { + return emotion.css` + height: 100%; + text-align: center; + `; + }, - rootAlignRight() { - return css` - text-align: right; - `; - }, + rootAlignLeft() { + return emotion.css` + text-align: left; + `; + }, - spring() { - return css` - display: inline-block; - height: 100%; - vertical-align: middle; - `; - }, + rootAlignRight() { + return emotion.css` + text-align: right; + `; + }, - container() { - return css` - display: inline-block; - text-align: left; - vertical-align: middle; - `; - }, -}); + spring() { + return emotion.css` + display: inline-block; + height: 100%; + vertical-align: middle; + `; + }, + + container() { + return emotion.css` + display: inline-block; + text-align: left; + vertical-align: middle; + `; + }, + }); diff --git a/packages/react-ui/components/Center/Center.tsx b/packages/react-ui/components/Center/Center.tsx index b14701260ce..a9ce80999c9 100644 --- a/packages/react-ui/components/Center/Center.tsx +++ b/packages/react-ui/components/Center/Center.tsx @@ -1,12 +1,13 @@ import React from 'react'; +import type { Emotion } from '@emotion/css/create-instance'; import { Override } from '../../typings/utility-types'; import { CommonProps, CommonWrapper, CommonWrapperRestProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { createPropsGetter, DefaultizedProps } from '../../lib/createPropsGetter'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; -import { styles } from './Center.styles'; +import { getStyles } from './Center.styles'; export type HorizontalAlign = 'left' | 'center' | 'right'; @@ -44,23 +45,33 @@ export class Center extends React.Component { }; private getProps = createPropsGetter(Center.defaultProps); + private emotion!: Emotion; + private styles!: ReturnType; private setRootNode!: TSetRootNode; public render() { return ( - - {this.renderMain} - + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( + + {this.renderMain} + + ); + }} + ); } private renderMain = (props: CommonWrapperRestProps) => { const { align, ...rest } = props; - + const styles = this.styles; return (
{ - return css` +export const checkboxSizeMixin = + (emotion: Emotion) => (fontSize: string, lineHeight: string, paddingY: string, checkboxBoxSize: string) => { + return emotion.css` line-height: ${lineHeight}; font-size: ${fontSize}; padding: ${paddingY} 0; @@ -12,26 +14,23 @@ export const checkboxSizeMixin = (fontSize: string, lineHeight: string, paddingY width: ${checkboxBoxSize}; } `; -}; + }; -export const boxWrapperSizeMixin = ( - labGrotesqueBaselineCompensation: string, - fontSize: string, - checkboxBoxSize: string, - checkboxBoxOffsetY: string, -) => { - const labGrotesqueCompenstation = parseInt(labGrotesqueBaselineCompensation); - const boxFontSize = parseInt(fontSize); - const baselineCompensation = getLabGrotesqueBaselineCompensation( - boxFontSize, - labGrotesqueCompenstation, - isChrome, - isFirefox, - ); +export const boxWrapperSizeMixin = + (emotion: Emotion) => + (labGrotesqueBaselineCompensation: string, fontSize: string, checkboxBoxSize: string, checkboxBoxOffsetY: string) => { + const labGrotesqueCompenstation = parseInt(labGrotesqueBaselineCompensation); + const boxFontSize = parseInt(fontSize); + const baselineCompensation = getLabGrotesqueBaselineCompensation( + boxFontSize, + labGrotesqueCompenstation, + isChrome, + isFirefox, + ); - return css` + return emotion.css` width: ${checkboxBoxSize}; height: ${checkboxBoxSize}; margin-top: calc(${checkboxBoxOffsetY} + ${baselineCompensation}px); `; -}; + }; diff --git a/packages/react-ui/components/Checkbox/Checkbox.styles.ts b/packages/react-ui/components/Checkbox/Checkbox.styles.ts index eda5f0dc014..13263ec1918 100644 --- a/packages/react-ui/components/Checkbox/Checkbox.styles.ts +++ b/packages/react-ui/components/Checkbox/Checkbox.styles.ts @@ -1,4 +1,6 @@ -import { css, memoizeStyle, prefix } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle, prefix } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { boxWrapperSizeMixin, checkboxSizeMixin } from './Checkbox.mixins'; @@ -7,113 +9,114 @@ export const globalClasses = prefix('checkbox')({ box: 'box', }); -export const styles = memoizeStyle({ - root(t: Theme) { - return css` - display: inline-flex; - align-items: baseline; - cursor: pointer; - position: relative; - - &::before { - // non-breaking space. - // makes a correct space for absolutely positioned box, - // and also height and baseline for checkbox without caption. - content: '\\00A0'; - display: inline-block; - flex: 0 0 auto; - } - - .${globalClasses.box} { - transition: - background ${t.transitionDuration} ${t.transitionTimingFunction}, - box-shadow ${t.transitionDuration} ${t.transitionTimingFunction}; - } - - &:hover .${globalClasses.box} { - background: ${t.checkboxHoverBg}; - box-shadow: ${t.checkboxShadowHover}; - } - - &:active .${globalClasses.box} { - box-shadow: ${t.checkboxShadowActive}; - background: ${t.checkboxActiveBg}; - } - `; - }, - - rootSmall(t: Theme) { - return css` - ${checkboxSizeMixin( - t.checkboxFontSizeSmall, - t.checkboxLineHeightSmall, - t.checkboxPaddingYSmall, - t.checkboxBoxSizeSmall, - )}; - `; - }, - - rootMedium(t: Theme) { - return css` - ${checkboxSizeMixin( - t.checkboxFontSizeMedium, - t.checkboxLineHeightMedium, - t.checkboxPaddingYMedium, - t.checkboxBoxSizeMedium, - )}; - `; - }, - - rootLarge(t: Theme) { - return css` - ${checkboxSizeMixin( - t.checkboxFontSizeLarge, - t.checkboxLineHeightLarge, - t.checkboxPaddingYLarge, - t.checkboxBoxSizeLarge, - )}; - `; - }, - - rootDisableTextSelect() { - return css` - user-select: none; - `; - }, - - rootChecked(t: Theme) { - return css` - &:hover .${globalClasses.box} { - box-shadow: ${t.checkboxCheckedHoverShadow}; - background: ${t.checkboxCheckedHoverBg}; - } - - &:active .${globalClasses.box} { - background: ${t.checkboxCheckedActiveBg}; - box-shadow: ${t.checkboxCheckedActiveShadow}; - } - `; - }, - - rootFallback() { - return css` - display: inline-table; - - & > * { - // fix root's :active state in IE11 that gets blocked by nested elements - pointer-events: none; - } - `; - }, - - rootWrapperIE11() { - return css` - display: inline; - `; - }, - - boxWrapper(t: Theme) { - return css` +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root(t: Theme) { + return emotion.css` + display: inline-flex; + align-items: baseline; + cursor: pointer; + position: relative; + + &::before { + // non-breaking space. + // makes a correct space for absolutely positioned box, + // and also height and baseline for checkbox without caption. + content: '\\00A0'; + display: inline-block; + flex: 0 0 auto; + } + + .${globalClasses.box} { + transition: + background ${t.transitionDuration} ${t.transitionTimingFunction}, + box-shadow ${t.transitionDuration} ${t.transitionTimingFunction}; + } + + &:hover .${globalClasses.box} { + background: ${t.checkboxHoverBg}; + box-shadow: ${t.checkboxShadowHover}; + } + + &:active .${globalClasses.box} { + box-shadow: ${t.checkboxShadowActive}; + background: ${t.checkboxActiveBg}; + } + `; + }, + + rootSmall(t: Theme) { + return emotion.css` + ${checkboxSizeMixin(emotion)( + t.checkboxFontSizeSmall, + t.checkboxLineHeightSmall, + t.checkboxPaddingYSmall, + t.checkboxBoxSizeSmall, + )}; + `; + }, + + rootMedium(t: Theme) { + return emotion.css` + ${checkboxSizeMixin(emotion)( + t.checkboxFontSizeMedium, + t.checkboxLineHeightMedium, + t.checkboxPaddingYMedium, + t.checkboxBoxSizeMedium, + )}; + `; + }, + + rootLarge(t: Theme) { + return emotion.css` + ${checkboxSizeMixin(emotion)( + t.checkboxFontSizeLarge, + t.checkboxLineHeightLarge, + t.checkboxPaddingYLarge, + t.checkboxBoxSizeLarge, + )}; + `; + }, + + rootDisableTextSelect() { + return emotion.css` + user-select: none; + `; + }, + + rootChecked(t: Theme) { + return emotion.css` + &:hover .${globalClasses.box} { + box-shadow: ${t.checkboxCheckedHoverShadow}; + background: ${t.checkboxCheckedHoverBg}; + } + + &:active .${globalClasses.box} { + background: ${t.checkboxCheckedActiveBg}; + box-shadow: ${t.checkboxCheckedActiveShadow}; + } + `; + }, + + rootFallback() { + return emotion.css` + display: inline-table; + + & > * { + // fix root's :active state in IE11 that gets blocked by nested elements + pointer-events: none; + } + `; + }, + + rootWrapperIE11() { + return emotion.css` + display: inline; + `; + }, + + boxWrapper(t: Theme) { + return emotion.css` position: absolute; box-sizing: border-box; padding: ${t.checkboxBorderWidth}; @@ -123,138 +126,138 @@ export const styles = memoizeStyle({ left: 0; )}; `; - }, - - boxWrapperSmall(t: Theme) { - return css` - ${boxWrapperSizeMixin( - t.labGrotesqueBaselineCompensation, - t.checkboxFontSizeSmall, - t.checkboxBoxSizeSmall, - t.checkboxBoxOffsetY, - )}; - `; - }, - - boxWrapperMedium(t: Theme) { - return css` - ${boxWrapperSizeMixin( - t.labGrotesqueBaselineCompensation, - t.checkboxFontSizeMedium, - t.checkboxBoxSizeMedium, - t.checkboxBoxOffsetY, - )}; - `; - }, - - boxWrapperLarge(t: Theme) { - return css` - ${boxWrapperSizeMixin( - t.labGrotesqueBaselineCompensation, - t.checkboxFontSizeLarge, - t.checkboxBoxSizeLarge, - t.checkboxBoxOffsetY, - )}; - `; - }, - - box(t: Theme) { - return css` - color: ${t.checkboxTextColorDefault}; - box-shadow: ${t.checkboxShadow}; - background: ${t.checkboxBg}; - border-radius: ${t.checkboxBorderRadius}; - height: 100%; - `; - }, - - input() { - return css` - display: inline-block; - height: 0; - opacity: 0; - position: absolute; - width: 0; - z-index: -1; - `; - }, - - boxWarning(t: Theme) { - return css` - box-shadow: - inset 0 0 0 1px ${t.checkboxOutlineColorFocus}, - 0 0 0 ${t.checkboxOutlineWidth} ${t.checkboxBorderColorWarning} !important; // override hover and active - `; - }, - - boxError(t: Theme) { - return css` - box-shadow: - inset 0 0 0 1px ${t.checkboxOutlineColorFocus}, - 0 0 0 ${t.checkboxOutlineWidth} ${t.checkboxBorderColorError} !important; // override hover and active - `; - }, - - boxChecked(t: Theme) { - return css` - background: ${t.checkboxCheckedBg}; - color: ${t.checkboxCheckedColor}; - box-shadow: ${t.checkboxCheckedShadow}; - `; - }, - - boxFocus(t: Theme) { - return css` - box-shadow: - inset 0 0 0 1px ${t.checkboxOutlineColorFocus}, - 0 0 0 ${t.checkboxOutlineWidth} ${t.checkboxBorderColorFocus} !important; // override hover and active - `; - }, - - boxDisabled(t: Theme) { - return css` - box-shadow: ${t.checkboxShadowDisabled} !important; // override hover and active - background: ${t.checkboxBgDisabled} !important; // override hover and active - color: ${t.checkboxTextColorDisabled}; - `; - }, - - disabled(t: Theme) { - return css` - color: ${t.checkboxTextColorDisabled}; - cursor: default; - `; - }, - - icon() { - return css` - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - display: flex; - justify-content: center; - align-items: center; - `; - }, - - iconUnchecked() { - return css` - color: transparent; - `; - }, - - caption(t: Theme) { - return css` - color: ${t.checkboxTextColorDefault}; - padding-left: ${t.checkboxCaptionGap}; - `; - }, - - captionIE11() { - return css` - display: table-cell; - `; - }, -}); + }, + + boxWrapperSmall(t: Theme) { + return emotion.css` + ${boxWrapperSizeMixin(emotion)( + t.labGrotesqueBaselineCompensation, + t.checkboxFontSizeSmall, + t.checkboxBoxSizeSmall, + t.checkboxBoxOffsetY, + )}; + `; + }, + + boxWrapperMedium(t: Theme) { + return emotion.css` + ${boxWrapperSizeMixin(emotion)( + t.labGrotesqueBaselineCompensation, + t.checkboxFontSizeMedium, + t.checkboxBoxSizeMedium, + t.checkboxBoxOffsetY, + )}; + `; + }, + + boxWrapperLarge(t: Theme) { + return emotion.css` + ${boxWrapperSizeMixin(emotion)( + t.labGrotesqueBaselineCompensation, + t.checkboxFontSizeLarge, + t.checkboxBoxSizeLarge, + t.checkboxBoxOffsetY, + )}; + `; + }, + + box(t: Theme) { + return emotion.css` + color: ${t.checkboxTextColorDefault}; + box-shadow: ${t.checkboxShadow}; + background: ${t.checkboxBg}; + border-radius: ${t.checkboxBorderRadius}; + height: 100%; + `; + }, + + input() { + return emotion.css` + display: inline-block; + height: 0; + opacity: 0; + position: absolute; + width: 0; + z-index: -1; + `; + }, + + boxWarning(t: Theme) { + return emotion.css` + box-shadow: + inset 0 0 0 1px ${t.checkboxOutlineColorFocus}, + 0 0 0 ${t.checkboxOutlineWidth} ${t.checkboxBorderColorWarning} !important; // override hover and active + `; + }, + + boxError(t: Theme) { + return emotion.css` + box-shadow: + inset 0 0 0 1px ${t.checkboxOutlineColorFocus}, + 0 0 0 ${t.checkboxOutlineWidth} ${t.checkboxBorderColorError} !important; // override hover and active + `; + }, + + boxChecked(t: Theme) { + return emotion.css` + background: ${t.checkboxCheckedBg}; + color: ${t.checkboxCheckedColor}; + box-shadow: ${t.checkboxCheckedShadow}; + `; + }, + + boxFocus(t: Theme) { + return emotion.css` + box-shadow: + inset 0 0 0 1px ${t.checkboxOutlineColorFocus}, + 0 0 0 ${t.checkboxOutlineWidth} ${t.checkboxBorderColorFocus} !important; // override hover and active + `; + }, + + boxDisabled(t: Theme) { + return emotion.css` + box-shadow: ${t.checkboxShadowDisabled} !important; // override hover and active + background: ${t.checkboxBgDisabled} !important; // override hover and active + color: ${t.checkboxTextColorDisabled}; + `; + }, + + disabled(t: Theme) { + return emotion.css` + color: ${t.checkboxTextColorDisabled}; + cursor: default; + `; + }, + + icon() { + return emotion.css` + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + `; + }, + + iconUnchecked() { + return emotion.css` + color: transparent; + `; + }, + + caption(t: Theme) { + return emotion.css` + color: ${t.checkboxTextColorDefault}; + padding-left: ${t.checkboxCaptionGap}; + `; + }, + + captionIE11() { + return emotion.css` + display: table-cell; + `; + }, + }); diff --git a/packages/react-ui/components/Checkbox/Checkbox.tsx b/packages/react-ui/components/Checkbox/Checkbox.tsx index 397ef7ca63d..f438be08c8b 100644 --- a/packages/react-ui/components/Checkbox/Checkbox.tsx +++ b/packages/react-ui/components/Checkbox/Checkbox.tsx @@ -1,21 +1,22 @@ import React, { AriaAttributes } from 'react'; import PropTypes from 'prop-types'; import { globalObject } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { Override } from '../../typings/utility-types'; import { keyListener } from '../../lib/events/keyListener'; import { Theme } from '../../lib/theming/Theme'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { isEdge, isIE11 } from '../../lib/client'; -import { CommonWrapper, CommonProps, CommonWrapperRestProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { CommonProps, CommonWrapper, CommonWrapperRestProps } from '../../internal/CommonWrapper'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { fixFirefoxModifiedClickOnLabel } from '../../lib/events/fixFirefoxModifiedClickOnLabel'; import { createPropsGetter } from '../../lib/createPropsGetter'; import { SizeProp } from '../../lib/types/props'; import { FocusControlWrapper } from '../../internal/FocusControlWrapper'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles, globalClasses } from './Checkbox.styles'; +import { getStyles, globalClasses } from './Checkbox.styles'; import { CheckedIcon } from './CheckedIcon'; import { IndeterminateIcon } from './IndeterminateIcon'; @@ -90,6 +91,7 @@ export class Checkbox extends React.PureComponent private getProps = createPropsGetter(Checkbox.defaultProps); private getRootSizeClassName() { + const styles = this.styles; switch (this.getProps().size) { case 'large': return styles.rootLarge(this.theme); @@ -102,6 +104,7 @@ export class Checkbox extends React.PureComponent } private getBoxWrapperSizeClassName() { + const styles = this.styles; switch (this.getProps().size) { case 'large': return styles.boxWrapperLarge(this.theme); @@ -144,6 +147,8 @@ export class Checkbox extends React.PureComponent }; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private input = React.createRef(); private handleShiftPress = (e: KeyboardEvent) => { @@ -186,16 +191,24 @@ export class Checkbox extends React.PureComponent public render() { return ( - - {(theme) => { - this.theme = theme; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); return ( - - {this.renderMain} - + + {(theme) => { + this.theme = theme; + return ( + + {this.renderMain} + + ); + }} + ); }} - + ); } @@ -258,8 +271,8 @@ export class Checkbox extends React.PureComponent ...rest } = props; const isIndeterminate = this.state.indeterminate; - - const iconClass = cx(styles.icon(), !props.checked && !isIndeterminate && styles.iconUnchecked()); + const styles = this.styles; + const iconClass = this.emotion.cx(styles.icon(), !props.checked && !isIndeterminate && styles.iconUnchecked()); const iconSize = parseInt(this.getCheckboxBoxSize()); const IconCheck = ( @@ -273,7 +286,7 @@ export class Checkbox extends React.PureComponent ); - const rootClass = cx(this.getRootSizeClassName(), { + const rootClass = this.emotion.cx(this.getRootSizeClassName(), { [styles.root(this.theme)]: true, [styles.rootFallback()]: isIE11 || isEdge, [styles.rootChecked(this.theme)]: props.checked || isIndeterminate, @@ -294,7 +307,7 @@ export class Checkbox extends React.PureComponent let caption = null; if (this.props.children) { - const captionClass = cx({ + const captionClass = this.emotion.cx({ [styles.caption(this.theme)]: true, [styles.captionIE11()]: isIE11 || isEdge, [styles.disabled(this.theme)]: Boolean(props.disabled), @@ -304,12 +317,12 @@ export class Checkbox extends React.PureComponent const box = (
+ memoizeStyle({ + root() { + return emotion.css` + cursor: text; + `; + }, - selected(t: Theme) { - const getSelection = (background: string, color: string) => - (background || color) && - `& ::selection { + selected(t: Theme) { + const getSelection = (background: string, color: string) => + (background || color) && + `& ::selection { background: ${background}; color: ${color}; }`; - return css` - cursor: text; + return emotion.css` + cursor: text; - ${getSelection(t.dateInputComponentSelectedBgColor, t.dateInputComponentSelectedTextColor)} - `; - }, + ${getSelection(t.dateInputComponentSelectedBgColor, t.dateInputComponentSelectedTextColor)} + `; + }, - mask(t: Theme) { - return css` - color: ${t.dateInputMaskColor}; - `; - }, + mask(t: Theme) { + return emotion.css` + color: ${t.dateInputMaskColor}; + `; + }, - delimiterFilled() { - return css` - color: inherit; - `; - }, -}); + delimiterFilled() { + return emotion.css` + color: inherit; + `; + }, + }); diff --git a/packages/react-ui/components/DateInput/DateFragmentsView.tsx b/packages/react-ui/components/DateInput/DateFragmentsView.tsx index 0391f2010e9..f44b773fa6b 100644 --- a/packages/react-ui/components/DateInput/DateFragmentsView.tsx +++ b/packages/react-ui/components/DateInput/DateFragmentsView.tsx @@ -1,14 +1,15 @@ import React from 'react'; import { globalObject } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { MaskCharLowLine } from '../../internal/MaskCharLowLine'; import { InternalDateValidator } from '../../lib/date/InternalDateValidator'; import { InternalDateComponentType, InternalDateFragment } from '../../lib/date/types'; import { Theme } from '../../lib/theming/Theme'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { cx } from '../../lib/theming/Emotion'; -import { styles } from './DateFragmentsView.styles'; +import { getStyles } from './DateFragmentsView.styles'; interface DateFragmentViewProps { selected: InternalDateComponentType | null; @@ -19,6 +20,8 @@ interface DateFragmentViewProps { export class DateFragmentsView extends React.Component { private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private rootNode: HTMLSpanElement | null = null; public isFragment = (el: HTMLElement | EventTarget): boolean => { @@ -34,20 +37,29 @@ export class DateFragmentsView extends React.Component { public render() { return ( - - {(theme) => { - this.theme = theme; - return this.renderMain(); + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( + + {(theme) => { + this.theme = theme; + return this.renderMain(); + }} + + ); }} - + ); } private renderMain() { + const styles = this.styles; return ( { } private renderSeparator(fragment: InternalDateFragment, index: number): JSX.Element { - const separatorClassName = cx({ + const styles = this.styles; + const separatorClassName = this.emotion.cx({ [styles.mask(this.theme)]: true, [styles.delimiterFilled()]: this.props.fragments[index + 1].value !== null, }); @@ -101,7 +114,7 @@ export class DateFragmentsView extends React.Component { return ( {valueMask} - + {Array(lengthMask) .fill('') .map((_, i) => ( diff --git a/packages/react-ui/components/DateInput/DateInput.styles.ts b/packages/react-ui/components/DateInput/DateInput.styles.ts index a350d0544b3..2f07e9d685e 100644 --- a/packages/react-ui/components/DateInput/DateInput.styles.ts +++ b/packages/react-ui/components/DateInput/DateInput.styles.ts @@ -1,48 +1,49 @@ -import { css } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + import { Theme } from '../../lib/theming/Theme'; -export const styles = { +export const getStyles = (emotion: Emotion) => ({ icon(t: Theme) { - return css` + return emotion.css` cursor: pointer; color: ${t.dateInputIconColor}; `; }, iconSmall(t: Theme) { - return css` + return emotion.css` font-size: ${t.inputFontSizeSmall}; `; }, iconMedium(t: Theme) { - return css` + return emotion.css` font-size: ${t.inputFontSizeMedium}; `; }, iconLarge(t: Theme) { - return css` + return emotion.css` font-size: ${t.inputFontSizeLarge}; `; }, iconDisabled(t: Theme) { - return css` + return emotion.css` cursor: default; color: ${t.textColorDisabled}; `; }, value() { - return css` + return emotion.css` visibility: hidden; `; }, valueVisible() { - return css` + return emotion.css` visibility: visible; `; }, -}; +}); diff --git a/packages/react-ui/components/DateInput/DateInput.tsx b/packages/react-ui/components/DateInput/DateInput.tsx index dbdb3c808bf..6a0119ee892 100644 --- a/packages/react-ui/components/DateInput/DateInput.tsx +++ b/packages/react-ui/components/DateInput/DateInput.tsx @@ -1,6 +1,7 @@ import React, { HTMLAttributes } from 'react'; import ReactDOM from 'react-dom'; import { globalObject } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { ConditionalHandler } from '../../lib/ConditionalHandler'; import { LENGTH_FULLDATE, MAX_FULLDATE, MIN_FULLDATE } from '../../lib/date/constants'; @@ -9,17 +10,17 @@ import { Theme } from '../../lib/theming/Theme'; import { DatePickerLocale, DatePickerLocaleHelper } from '../DatePicker/locale'; import { InputLikeText } from '../../internal/InputLikeText'; import { locale } from '../../lib/locale/decorators'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { CommonWrapper, CommonProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { createPropsGetter } from '../../lib/createPropsGetter'; import { SizeProp } from '../../lib/types/props'; import { FocusControlWrapper } from '../../internal/FocusControlWrapper'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { CalendarIcon } from './CalendarIcon'; import { DateFragmentsView } from './DateFragmentsView'; -import { styles } from './DateInput.styles'; +import { getStyles } from './DateInput.styles'; import { Actions, extractAction } from './helpers/DateInputKeyboardActions'; import { InternalDateMediator } from './helpers/InternalDateMediator'; @@ -107,6 +108,8 @@ export class DateInput extends React.Component { private locale!: DatePickerLocale; private blurEvent: React.FocusEvent | null = null; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private setRootNode!: TSetRootNode; private conditionalHandler = new ConditionalHandler]>() .add(Actions.MoveSelectionLeft, () => this.shiftSelection(-1)) @@ -191,12 +194,20 @@ export class DateInput extends React.Component { public render() { return ( - - {(theme) => { - this.theme = theme; - return this.renderMain(); + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( + + {(theme) => { + this.theme = theme; + return this.renderMain(); + }} + + ); }} - + ); } @@ -230,7 +241,7 @@ export class DateInput extends React.Component { inputMode={'numeric'} takeContentWidth > - + { private renderIcon = () => { const { withIcon, disabled = false } = this.props; const size = this.getProps().size; - + const styles = this.styles; if (withIcon) { const theme = this.theme; const icon = ; - const iconStyles = cx({ + const iconStyles = this.emotion.cx({ [styles.icon(theme)]: true, [styles.iconSmall(theme)]: size === 'small', [styles.iconMedium(theme)]: size === 'medium', diff --git a/packages/react-ui/components/DatePicker/DatePicker.styles.ts b/packages/react-ui/components/DatePicker/DatePicker.styles.ts index 0f20d165ae2..ae578cf14e3 100644 --- a/packages/react-ui/components/DatePicker/DatePicker.styles.ts +++ b/packages/react-ui/components/DatePicker/DatePicker.styles.ts @@ -1,25 +1,28 @@ +import type { Emotion } from '@emotion/css/create-instance'; + import { Theme } from '../../lib/theming/Theme'; -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import { memoizeStyle } from '../../lib/theming/Emotion'; -export const styles = memoizeStyle({ - root() { - return css` - display: inline-block; - position: relative; - touch-action: none; - line-height: normal; - `; - }, - calendarWrapper(t: Theme) { - return css` - background: ${t.calendarBg}; - box-shadow: ${t.pickerShadow}; - display: flex; - flex-flow: column nowrap; - font-size: 0; - z-index: 1000; - touch-action: none; - border-radius: ${t.calendarBorderRadius}; - `; - }, -}); +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root() { + return emotion.css` + display: inline-block; + position: relative; + touch-action: none; + line-height: normal; + `; + }, + calendarWrapper(t: Theme) { + return emotion.css` + background: ${t.calendarBg}; + box-shadow: ${t.pickerShadow}; + display: flex; + flex-flow: column nowrap; + font-size: 0; + z-index: 1000; + touch-action: none; + border-radius: ${t.calendarBorderRadius}; + `; + }, + }); diff --git a/packages/react-ui/components/DatePicker/DatePicker.tsx b/packages/react-ui/components/DatePicker/DatePicker.tsx index cd590ac11ee..2d09316b2d8 100644 --- a/packages/react-ui/components/DatePicker/DatePicker.tsx +++ b/packages/react-ui/components/DatePicker/DatePicker.tsx @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React, { HTMLAttributes } from 'react'; +import type { Emotion } from '@emotion/css/create-instance'; import { Popup } from '../../internal/Popup'; import { LocaleContext } from '../../lib/locale'; @@ -13,14 +14,13 @@ import { InternalDateOrder, InternalDateSeparator, InternalDateValidateCheck } f import { Nullable } from '../../typings/utility-types'; import { DateInput } from '../DateInput'; import { filterProps } from '../../lib/filterProps'; -import { CommonWrapper, CommonProps, CommonWrapperRestProps } from '../../internal/CommonWrapper'; +import { CommonProps, CommonWrapper, CommonWrapperRestProps } from '../../internal/CommonWrapper'; import { isMobile } from '../../lib/client'; import { NativeDateInput } from '../../internal/NativeDateInput'; import { getRootNode, rootNode, TSetRootNode } from '../../lib/rootNode'; import { isNonNullable } from '../../lib/utils'; import { createPropsGetter } from '../../lib/createPropsGetter'; import { Calendar, CalendarDateShape, CalendarProps } from '../Calendar'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme } from '../../lib/theming/Theme'; import { Button } from '../Button'; import { getTodayDate } from '../Calendar/CalendarUtils'; @@ -28,8 +28,10 @@ import { SizeProp } from '../../lib/types/props'; import { responsiveLayout } from '../ResponsiveLayout/decorator'; import { getMenuPositions } from '../../lib/getMenuPositions'; import { ZIndex } from '../../internal/ZIndex'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; -import { styles } from './DatePicker.styles'; +import { getStyles } from './DatePicker.styles'; import { DatePickerLocale, DatePickerLocaleHelper } from './locale'; import { MobilePicker } from './MobilePicker'; @@ -176,6 +178,8 @@ export class DatePicker extends React.PureComponent; private readonly locale!: DatePickerLocale; public static validate = (value: Nullable, range: { minDate?: string; maxDate?: string } = {}) => { @@ -262,19 +266,27 @@ export class DatePicker extends React.PureComponent - {(theme) => { - this.theme = theme; - + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); return ( - - - {this.renderMain} - - + + {(theme) => { + this.theme = theme; + + return ( + + + {this.renderMain} + + + ); + }} + ); }} - + ); } @@ -282,7 +294,6 @@ export class DatePicker extends React.PureComponent
e.preventDefault()} > + memoizeStyle({ + calendarRoot() { + return emotion.css` width: auto; `; - }, -}); + }, + }); diff --git a/packages/react-ui/components/DatePicker/MobilePicker.tsx b/packages/react-ui/components/DatePicker/MobilePicker.tsx index 347c3bd25ae..2796733f3ac 100644 --- a/packages/react-ui/components/DatePicker/MobilePicker.tsx +++ b/packages/react-ui/components/DatePicker/MobilePicker.tsx @@ -8,10 +8,11 @@ import { DateInput } from '../DateInput'; import { Button } from '../Button'; import { useLocaleForControl } from '../../lib/locale/useLocaleForControl'; import { useEffectWithoutInitCall } from '../../hooks/useEffectWithoutInitCall'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { DatePickerProps } from './DatePicker'; import { DatePickerLocaleHelper } from './locale'; -import { styles } from './MobilePicker.styles'; +import { getStyles } from './MobilePicker.styles'; import { getMobilePickerTheme } from './getMobilePickerTheme'; export const MobilePickerDataTids = { @@ -40,6 +41,7 @@ export const MobilePicker: React.FC = (props) => { const locale = useLocaleForControl('DatePicker', DatePickerLocaleHelper); const theme = getMobilePickerTheme(useContext(ThemeContext)); + const emotion = useContext(EmotionContext); const onValueChange = (date: string) => { if (props.onValueChange) { @@ -106,7 +108,7 @@ export const MobilePicker: React.FC = (props) => { { - return css` +export const fileUploaderSizeMixin = + (emotion: Emotion) => (fontSize: string, lineHeight: string, paddingX: string, paddingY: string) => { + return emotion.css` font-size: ${fontSize}; box-sizing: border-box; padding: ${paddingY} ${paddingX}; line-height: ${lineHeight}; `; -}; + }; diff --git a/packages/react-ui/components/FileUploader/FileUploader.styles.ts b/packages/react-ui/components/FileUploader/FileUploader.styles.ts index 5e45e8daa9b..6bf6efcb5d1 100644 --- a/packages/react-ui/components/FileUploader/FileUploader.styles.ts +++ b/packages/react-ui/components/FileUploader/FileUploader.styles.ts @@ -1,4 +1,6 @@ -import { css, keyframes, memoizeStyle, prefix } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle, prefix } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import * as ColorFunctions from '../../lib/styles/ColorFunctions'; @@ -8,23 +10,24 @@ export const globalClasses = prefix('file-uploader')({ afterLinkText: 'after-link-text', }); -const styles = { - calcPulse(t: Theme) { - return keyframes` - 0% { - box-shadow: 0 0 0 1px ${ColorFunctions.fade(t.inputBlinkColor, 0.6)}; - } - 95% { - box-shadow: 0 0 0 10px ${ColorFunctions.fade(t.inputBlinkColor, 0.1)}; - } - 100% { - box-shadow: 0 0 0 1px ${ColorFunctions.fade(t.inputBlinkColor, 0.0)}; - } - `; - }, - - root(t: Theme) { - return css` +export const getStyles = (emotion: Emotion) => { + return memoizeStyle({ + calcPulse(t: Theme) { + return emotion.keyframes` + 0% { + box-shadow: 0 0 0 1px ${ColorFunctions.fade(t.inputBlinkColor, 0.6)}; + } + 95% { + box-shadow: 0 0 0 10px ${ColorFunctions.fade(t.inputBlinkColor, 0.1)}; + } + 100% { + box-shadow: 0 0 0 1px ${ColorFunctions.fade(t.inputBlinkColor, 0.0)}; + } + `; + }, + + root(t: Theme) { + return emotion.css` display: inline-block; position: relative; line-height: ${t.fileUploaderLineHeightSmall}; @@ -32,10 +35,10 @@ const styles = { color: ${t.fileUploaderTextColorDefault}; background-color: ${t.fileUploaderBg}; `; - }, + }, - uploadButton(t: Theme) { - return css` + uploadButton(t: Theme) { + return emotion.css` width: 100%; display: inline-flex; align-items: center; @@ -51,32 +54,32 @@ const styles = { border-color ${t.transitionDuration} ${t.transitionTimingFunction}; background-color: ${t.fileUploaderUploadButtonBg}; `; - }, + }, - uploadButtonFocus(t: Theme) { - return css` + uploadButtonFocus(t: Theme) { + return emotion.css` border: ${t.fileUploaderBorderWidth} solid ${t.fileUploaderBorderColorFocus}; box-shadow: 0px 0px 0px 1px ${t.fileUploaderBorderColorFocus}; `; - }, + }, - dragOver(t: Theme) { - return css` + dragOver(t: Theme) { + return emotion.css` border: 1px solid ${t.fileUploaderDragOverBorderColor}; border-radius: ${t.fileUploaderBorderRadius}; box-shadow: ${t.fileUploaderDragOverShadow}; `; - }, + }, - windowDragOver(t: Theme) { - return css` + windowDragOver(t: Theme) { + return emotion.css` border-radius: ${t.fileUploaderBorderRadius}; - animation: ${styles.calcPulse(t)} 1.5s infinite; + animation: ${this.calcPulse(t)} 1.5s infinite; `; - }, + }, - content() { - return css` + content() { + return emotion.css` display: block; width: 100%; height: 100%; @@ -84,35 +87,35 @@ const styles = { overflow: hidden; text-overflow: ellipsis; `; - }, + }, - contentWithFiles() { - return css` + contentWithFiles() { + return emotion.css` display: flex; align-items: center; `; - }, + }, - contentInnerSmall(t: Theme) { - return css` + contentInnerSmall(t: Theme) { + return emotion.css` width: calc(100% - ${t.inputIconSizeSmall} - ${t.fileUploaderIconGapSmall}); `; - }, + }, - contentInnerMedium(t: Theme) { - return css` + contentInnerMedium(t: Theme) { + return emotion.css` width: calc(100% - ${t.inputIconSizeMedium} - ${t.fileUploaderIconGapMedium}); `; - }, + }, - contentInnerLarge(t: Theme) { - return css` + contentInnerLarge(t: Theme) { + return emotion.css` width: calc(100% - ${t.inputIconSizeLarge} - ${t.fileUploaderIconGapLarge}); `; - }, + }, - visuallyHidden() { - return css` + visuallyHidden() { + return emotion.css` position: absolute; width: 1px; height: 1px; @@ -124,53 +127,53 @@ const styles = { clip: rect(0 0 0 0); overflow: hidden; `; - }, + }, - afterLinkText(t: Theme) { - return css` + afterLinkText(t: Theme) { + return emotion.css` display: inline; color: ${t.fileUploaderAfterLinkColor}; `; - }, + }, - afterLinkText_HasFiles(t: Theme) { - return css` + afterLinkText_HasFiles(t: Theme) { + return emotion.css` display: flex; justify-content: space-between; flex: 1 1 auto; color: ${t.fileUploaderAfterLinkColor}; `; - }, + }, - warning(t: Theme) { - return css` + warning(t: Theme) { + return emotion.css` border: ${t.fileUploaderBorderWidth} solid ${t.fileUploaderBorderColorWarning}; box-shadow: 0px 0px 0px 1px ${t.fileUploaderBorderColorWarning}; `; - }, + }, - error(t: Theme) { - return css` + error(t: Theme) { + return emotion.css` border: ${t.fileUploaderBorderWidth} solid ${t.fileUploaderBorderColorError}; box-shadow: 0px 0px 0px 1px ${t.fileUploaderBorderColorError}; `; - }, + }, - hovered(t: Theme) { - return css` + hovered(t: Theme) { + return emotion.css` background: ${t.fileUploaderHoveredBg}; border-color: ${t.fileUploaderHoveredBorderColor}; `; - }, + }, - linkHovered(t: Theme) { - return css` + linkHovered(t: Theme) { + return emotion.css` text-decoration: ${t.fileUploaderLinkHoverTextDecoration}; `; - }, + }, - disabled(t: Theme) { - return css` + disabled(t: Theme) { + return emotion.css` cursor: default; background: ${t.fileUploaderDisabledBg}; border: ${t.fileUploaderDisabledBorder}; @@ -182,106 +185,105 @@ const styles = { color: ${t.fileUploaderDisabledTextColor}; } `; - }, + }, - icon(t: Theme) { - return css` + icon(t: Theme) { + return emotion.css` display: inline-block; position: absolute; color: ${t.fileUploaderIconColor}; text-align: right; `; - }, + }, - iconDisabled(t: Theme) { - return css` + iconDisabled(t: Theme) { + return emotion.css` color: ${t.fileUploaderDisabledIconColor}; `; - }, + }, - link(t: Theme) { - return css` + link(t: Theme) { + return emotion.css` outline: none; text-decoration: none; color: ${t.fileUploaderLinkColor}; `; - }, + }, - linkDisabled(t: Theme) { - return css` + linkDisabled(t: Theme) { + return emotion.css` color: ${t.fileUploaderDisabledLinkColor}; &:hover { text-decoration: none; } `; - }, + }, - singleFile() { - return css` + singleFile() { + return emotion.css` width: 100%; `; - }, + }, - sizeSmall(t: Theme) { - return css` - ${fileUploaderSizeMixin( + sizeSmall(t: Theme) { + return emotion.css` + ${fileUploaderSizeMixin(emotion)( t.fileUploaderFontSizeSmall, t.fileUploaderLineHeightSmall, t.fileUploaderPaddingXSmall, t.fileUploaderPaddingYSmall, )}; `; - }, + }, - sizeMedium(t: Theme) { - return css` - ${fileUploaderSizeMixin( + sizeMedium(t: Theme) { + return emotion.css` + ${fileUploaderSizeMixin(emotion)( t.fileUploaderFontSizeMedium, t.fileUploaderLineHeightMedium, t.fileUploaderPaddingXMedium, t.fileUploaderPaddingYMedium, )}; `; - }, + }, - sizeLarge(t: Theme) { - return css` - ${fileUploaderSizeMixin( + sizeLarge(t: Theme) { + return emotion.css` + ${fileUploaderSizeMixin(emotion)( t.fileUploaderFontSizeLarge, t.fileUploaderLineHeightLarge, t.fileUploaderPaddingXLarge, t.fileUploaderPaddingYLarge, )}; `; - }, + }, - iconSmall(t: Theme) { - return css` + iconSmall(t: Theme) { + return emotion.css` font-size: ${t.fileUploaderFontSizeSmall}; line-height: ${t.fileUploaderLineHeightSmall}; width: ${t.inputIconSizeSmall}; bottom: ${t.fileUploaderPaddingYSmall}; right: ${t.fileUploaderPaddingXSmall}; `; - }, - iconMedium(t: Theme) { - return css` + }, + iconMedium(t: Theme) { + return emotion.css` font-size: ${t.fileUploaderFontSizeMedium}; line-height: ${t.fileUploaderLineHeightMedium}; width: ${t.inputIconSizeMedium}; bottom: ${t.fileUploaderPaddingYMedium}; right: ${t.fileUploaderPaddingXMedium}; `; - }, - iconLarge(t: Theme) { - return css` + }, + iconLarge(t: Theme) { + return emotion.css` font-size: ${t.fileUploaderFontSizeLarge}; line-height: ${t.fileUploaderLineHeightLarge}; width: ${t.inputIconSizeLarge}; bottom: ${t.fileUploaderPaddingYLarge}; right: ${t.fileUploaderPaddingXLarge}; `; - }, + }, + }); }; - -export const jsStyles = memoizeStyle(styles); diff --git a/packages/react-ui/components/FileUploader/FileUploader.tsx b/packages/react-ui/components/FileUploader/FileUploader.tsx index 060692ecf08..5df020e447f 100644 --- a/packages/react-ui/components/FileUploader/FileUploader.tsx +++ b/packages/react-ui/components/FileUploader/FileUploader.tsx @@ -2,14 +2,13 @@ import React, { useCallback, useContext, useEffect, useImperativeHandle, useRef, import { globalObject, isBrowser } from '@skbkontur/global-object'; import { FileUploaderAttachedFile, getAttachedFile } from '../../internal/FileUploaderControl/fileUtils'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { InstanceWithRootNode } from '../../lib/rootNode'; import { useMemoObject } from '../../hooks/useMemoObject'; import { FileUploaderControlContext } from '../../internal/FileUploaderControl/FileUploaderControlContext'; import { useControlLocale } from '../../internal/FileUploaderControl/hooks/useControlLocale'; import { useUpload } from '../../internal/FileUploaderControl/hooks/useUpload'; import { useDrop } from '../../hooks/useDrop'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { FileUploaderControlProviderProps } from '../../internal/FileUploaderControl/FileUploaderControlProvider'; import { withFileUploaderControlProvider } from '../../internal/FileUploaderControl/withFileUploaderControlProvider'; import { keyListener } from '../../lib/events/keyListener'; @@ -22,9 +21,10 @@ import { useFileUploaderSize } from '../../internal/FileUploaderControl/hooks/us import { SizeProp } from '../../lib/types/props'; import { forwardRefAndName } from '../../lib/forwardRefAndName'; import { FocusControlWrapper } from '../../internal/FocusControlWrapper'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { UploadIcon } from './UploadIcon'; -import { globalClasses, jsStyles } from './FileUploader.styles'; +import { getStyles, globalClasses } from './FileUploader.styles'; const stopPropagation: React.ReactEventHandler = (e) => e.stopPropagation(); @@ -89,6 +89,7 @@ const defaultRenderFile = (file: FileUploaderAttachedFile, fileNode: React.React const _FileUploader = forwardRefAndName('FileUploader', (props, ref) => { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); const { initialFiles, @@ -138,23 +139,23 @@ const _FileUploader = forwardRefAndName('Fi }, [validateBeforeUpload, isAsync, upload, setFileValidationResult], ); - + const styles = getStyles(emotion); const sizeClassName = useFileUploaderSize(size, { - small: jsStyles.sizeSmall(theme), - medium: jsStyles.sizeMedium(theme), - large: jsStyles.sizeLarge(theme), + small: styles.sizeSmall(theme), + medium: styles.sizeMedium(theme), + large: styles.sizeLarge(theme), }); const sizeIconClass = useFileUploaderSize(size, { - small: jsStyles.iconSmall(theme), - medium: jsStyles.iconMedium(theme), - large: jsStyles.iconLarge(theme), + small: styles.iconSmall(theme), + medium: styles.iconMedium(theme), + large: styles.iconLarge(theme), }); const contentInnerClass = useFileUploaderSize(size, { - small: jsStyles.contentInnerSmall(theme), - medium: jsStyles.contentInnerMedium(theme), - large: jsStyles.contentInnerLarge(theme), + small: styles.contentInnerSmall(theme), + medium: styles.contentInnerMedium(theme), + large: styles.contentInnerLarge(theme), }); /** common part **/ @@ -262,31 +263,35 @@ const _FileUploader = forwardRefAndName('Fi const [hovered, setHovered] = useState(false); - const uploadButtonClassNames = cx( - jsStyles.uploadButton(theme), + const uploadButtonClassNames = emotion.cx( + styles.uploadButton(theme), sizeClassName, - focusedByTab && jsStyles.uploadButtonFocus(theme), - disabled && jsStyles.disabled(theme), - !disabled && hovered && jsStyles.hovered(theme), - !!warning && jsStyles.warning(theme), - !!error && jsStyles.error(theme), - isDraggable && !disabled && jsStyles.dragOver(theme), + focusedByTab && styles.uploadButtonFocus(theme), + disabled && styles.disabled(theme), + !disabled && hovered && styles.hovered(theme), + !!warning && styles.warning(theme), + !!error && styles.error(theme), + isDraggable && !disabled && styles.dragOver(theme), ); const canDrop = isWindowDraggable && !disabled; - const uploadButtonWrapperClassNames = cx(canDrop && jsStyles.windowDragOver(theme)); + const uploadButtonWrapperClassNames = emotion.cx(canDrop && styles.windowDragOver(theme)); - const uploadButtonIconClassNames = cx(jsStyles.icon(theme), sizeIconClass, disabled && jsStyles.iconDisabled(theme)); + const uploadButtonIconClassNames = emotion.cx( + styles.icon(theme), + sizeIconClass, + disabled && styles.iconDisabled(theme), + ); const hasOneFile = files.length === 1; const hasOneFileForSingle = isSingleMode && hasOneFile && !hideFiles; - const contentClassNames = cx(jsStyles.content(), hasOneFileForSingle && jsStyles.contentWithFiles()); + const contentClassNames = emotion.cx(styles.content(), hasOneFileForSingle && styles.contentWithFiles()); - const linkClassNames = cx( - jsStyles.link(theme), - !disabled && hovered && jsStyles.linkHovered(theme), - disabled && jsStyles.linkDisabled(theme), + const linkClassNames = emotion.cx( + styles.link(theme), + !disabled && hovered && styles.linkHovered(theme), + disabled && styles.linkDisabled(theme), ); useEffect(() => { @@ -316,7 +321,7 @@ const _FileUploader = forwardRefAndName('Fi
@@ -332,7 +337,7 @@ const _FileUploader = forwardRefAndName('Fi >
{isLinkVisible && ( @@ -341,13 +346,13 @@ const _FileUploader = forwardRefAndName('Fi )} {isLinkVisible && String.fromCharCode(0xa0) /*   */}
{hasOneFileForSingle ? ( -
+
{renderFile(files[0], )}
) : ( @@ -367,7 +372,7 @@ const _FileUploader = forwardRefAndName('Fi type="file" disabled={disabled} multiple={multiple} - className={jsStyles.visuallyHidden()} + className={styles.visuallyHidden()} onClick={stopPropagation} onChange={handleInputChange} onFocus={handleFocus} diff --git a/packages/react-ui/components/FxInput/FxInput.tsx b/packages/react-ui/components/FxInput/FxInput.tsx index 55b161e6dfd..1955f86e1cb 100644 --- a/packages/react-ui/components/FxInput/FxInput.tsx +++ b/packages/react-ui/components/FxInput/FxInput.tsx @@ -6,11 +6,11 @@ import { Input, InputProps } from '../Input'; import { CurrencyInput, CurrencyInputProps } from '../CurrencyInput'; import { createPropsGetter, DefaultizedProps } from '../../lib/createPropsGetter'; import { Override } from '../../typings/utility-types'; -import { CommonWrapper, CommonProps, CommonWrapperRestProps } from '../../internal/CommonWrapper'; +import { CommonProps, CommonWrapper, CommonWrapperRestProps } from '../../internal/CommonWrapper'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme } from '../../lib/theming/Theme'; import { SizeProp } from '../../lib/types/props'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { MathFunctionIcon } from './MathFunctionIcon'; import { FxInputRestoreBtn } from './FxInputRestoreBtn'; diff --git a/packages/react-ui/components/FxInput/FxInputRestoreBtn.tsx b/packages/react-ui/components/FxInput/FxInputRestoreBtn.tsx index 307436d6efe..37026d2dacb 100644 --- a/packages/react-ui/components/FxInput/FxInputRestoreBtn.tsx +++ b/packages/react-ui/components/FxInput/FxInputRestoreBtn.tsx @@ -1,9 +1,9 @@ import React, { AriaAttributes, useContext } from 'react'; import { Input } from '../Input'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Button } from '../Button'; import { SizeProp } from '../../lib/types/props'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { UndoIcon } from './UndoIcon'; import { FxInputProps } from './FxInput'; diff --git a/packages/react-ui/components/GlobalLoader/GlobalLoaderView.styles.ts b/packages/react-ui/components/GlobalLoader/GlobalLoaderView.styles.ts index d805d27291a..12abdb65752 100644 --- a/packages/react-ui/components/GlobalLoader/GlobalLoaderView.styles.ts +++ b/packages/react-ui/components/GlobalLoader/GlobalLoaderView.styles.ts @@ -1,51 +1,55 @@ -import { css, memoizeStyle, keyframes } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; -export const styles = memoizeStyle({ - outer(t: Theme) { - return css` - width: ${t.globalLoaderWidth}; - height: ${t.globalLoaderHeight}; - background-color: ${t.globalLoaderBackgroundColor}; - position: ${t.globalLoaderPosition}; - left: ${t.globalLoaderLeft}; - top: ${t.globalLoaderTop}; - right: ${t.globalLoaderRight}; - bottom: ${t.globalLoaderBottom}; - overflow: hidden; - `; - }, - inner(t: Theme) { - return css` - background-color: ${t.globalLoaderColor}; - width: 0; - height: ${t.globalLoaderHeight}; - position: absolute; - left: 0; - overflow: hidden; - `; - }, - standardWithoutAnimation() { - return css` - left: 0; - width: 90%; - `; - }, - successWithoutAnimation() { - return css` - left: 0; - width: 100%; - `; - }, - errorWithoutAnimation() { - return css` - left: 40%; - width: 20%; - `; - }, -}); +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + outer(t: Theme) { + return emotion.css` + width: ${t.globalLoaderWidth}; + height: ${t.globalLoaderHeight}; + background-color: ${t.globalLoaderBackgroundColor}; + position: ${t.globalLoaderPosition}; + left: ${t.globalLoaderLeft}; + top: ${t.globalLoaderTop}; + right: ${t.globalLoaderRight}; + bottom: ${t.globalLoaderBottom}; + overflow: hidden; + `; + }, + inner(t: Theme) { + return emotion.css` + background-color: ${t.globalLoaderColor}; + width: 0; + height: ${t.globalLoaderHeight}; + position: absolute; + left: 0; + overflow: hidden; + `; + }, + standardWithoutAnimation() { + return emotion.css` + left: 0; + width: 90%; + `; + }, + successWithoutAnimation() { + return emotion.css` + left: 0; + width: 100%; + `; + }, + errorWithoutAnimation() { + return emotion.css` + left: 40%; + width: 20%; + `; + }, + }); -const moveToRightAnimation = keyframes` +export const getAnimations = (emotion: Emotion) => { + const moveToRightAnimation = emotion.keyframes` 0% { left: 0; width: 100%; @@ -59,7 +63,7 @@ const moveToRightAnimation = keyframes` width: 1% } `; -const spinnerAnimation = keyframes` + const spinnerAnimation = emotion.keyframes` 0% { transform: translateX(50%) scaleX(.005); animation-timing-function: cubic-bezier(.895,.03,.685,.22); @@ -72,124 +76,125 @@ const spinnerAnimation = keyframes` transform: translateX(-50%) scaleX(.005); } `; -const linearProgressAnimation = keyframes` + const linearProgressAnimation = emotion.keyframes` from { width: 0; } to { width: 80% } `; -const slowProgressAnimation = keyframes` + const slowProgressAnimation = emotion.keyframes` from { width: 80%; } to { width: 90% } `; -export const animations = { - successAnimation(delayBeforeHide: number, width: number, left: number) { - return css` - animation: successAnimation; - animation-duration: ${delayBeforeHide}ms; - @keyframes successAnimation { - 0% { - width: ${width}px; - left: ${left}px; - opacity: 1; - } - 20% { - width: 100%; - left: 0; - opacity: 1; - } - 80% { - width: 100%; - left: 0; - opacity: 1; - } - 100% { - width: 100%; - opacity: 0; + return { + successAnimation(delayBeforeHide: number, width: number, left: number) { + return emotion.css` + animation: successAnimation; + animation-duration: ${delayBeforeHide}ms; + @keyframes successAnimation { + 0% { + width: ${width}px; + left: ${left}px; + opacity: 1; + } + 20% { + width: 100%; + left: 0; + opacity: 1; + } + 80% { + width: 100%; + left: 0; + opacity: 1; + } + 100% { + width: 100%; + opacity: 0; + } } - } - `; - }, - errorAnimation(t: Theme) { - const transitionDuration = parseInt(t.globalLoaderTransitionToSpinnerDuration); - const spinnerAnimationDuration = parseInt(t.globalLoaderSpinnerAnimationDuration); + `; + }, + errorAnimation(t: Theme) { + const transitionDuration = parseInt(t.globalLoaderTransitionToSpinnerDuration); + const spinnerAnimationDuration = parseInt(t.globalLoaderSpinnerAnimationDuration); - return css` - left: 0; - width: 100%; - animation: - ${moveToRightAnimation} ${transitionDuration}ms linear, - ${spinnerAnimationDuration}ms ${spinnerAnimation} ${transitionDuration}ms infinite alternate; - `; - }, - standardAnimation(t: Theme, expectedTime: number) { - const slowProgressAnimationTime = parseInt(t.globalLoaderSlowAnimationDuration); - return css` - width: 90%; - animation: - ${linearProgressAnimation} ${expectedTime}ms cubic-bezier(0, 0.4, 0.4, 1), - ${slowProgressAnimationTime}ms ${slowProgressAnimation} ${expectedTime}ms linear; - `; - }, - acceptAnimation(t: Theme, startWidth: number, expectedTime: number, width: number, left: number) { - const transitionTime = parseInt(t.globalLoaderTransitionFromSpinnerDuration); - const slowProgressAnimationTime = parseInt(t.globalLoaderSlowAnimationDuration); - return css` - width: 90%; - animation: - transitionAnimation ${transitionTime}ms linear, - ${expectedTime}ms acceptAnimation ${transitionTime}ms cubic-bezier(0, 0.4, 0.4, 1), - ${slowProgressAnimationTime}ms ${slowProgressAnimation} ${expectedTime + transitionTime}ms linear; - @keyframes transitionAnimation { - from { - width: ${width}px; - left: ${left}px; - } - to { - width: ${startWidth}px; - left: 0; - } - } - @keyframes acceptAnimation { - from { - width: ${startWidth}px; - } - to { - width: 80%; - } - } - `; - }, - slowAcceptAnimation(t: Theme, startWidth: number, width: number, left: number) { - const transitionTime = parseInt(t.globalLoaderTransitionFromSpinnerDuration); - const slowProgressAnimationTime = parseInt(t.globalLoaderSlowAnimationDuration); - return css` - width: 90%; - animation: - transitionAnimation ${transitionTime}ms linear, - ${slowProgressAnimationTime}ms acceptAnimation ${transitionTime}ms linear; - @keyframes transitionAnimation { - from { - width: ${width}px; - left: ${left}px; + return emotion.css` + left: 0; + width: 100%; + animation: + ${moveToRightAnimation} ${transitionDuration}ms linear, + ${spinnerAnimationDuration}ms ${spinnerAnimation} ${transitionDuration}ms infinite alternate; + `; + }, + standardAnimation(t: Theme, expectedTime: number) { + const slowProgressAnimationTime = parseInt(t.globalLoaderSlowAnimationDuration); + return emotion.css` + width: 90%; + animation: + ${linearProgressAnimation} ${expectedTime}ms cubic-bezier(0, 0.4, 0.4, 1), + ${slowProgressAnimationTime}ms ${slowProgressAnimation} ${expectedTime}ms linear; + `; + }, + acceptAnimation(t: Theme, startWidth: number, expectedTime: number, width: number, left: number) { + const transitionTime = parseInt(t.globalLoaderTransitionFromSpinnerDuration); + const slowProgressAnimationTime = parseInt(t.globalLoaderSlowAnimationDuration); + return emotion.css` + width: 90%; + animation: + transitionAnimation ${transitionTime}ms linear, + ${expectedTime}ms acceptAnimation ${transitionTime}ms cubic-bezier(0, 0.4, 0.4, 1), + ${slowProgressAnimationTime}ms ${slowProgressAnimation} ${expectedTime + transitionTime}ms linear; + @keyframes transitionAnimation { + from { + width: ${width}px; + left: ${left}px; + } + to { + width: ${startWidth}px; + left: 0; + } } - to { - width: ${startWidth}px; - left: 0; + @keyframes acceptAnimation { + from { + width: ${startWidth}px; + } + to { + width: 80%; + } } - } - @keyframes acceptAnimation { - from { - width: ${startWidth}px; + `; + }, + slowAcceptAnimation(t: Theme, startWidth: number, width: number, left: number) { + const transitionTime = parseInt(t.globalLoaderTransitionFromSpinnerDuration); + const slowProgressAnimationTime = parseInt(t.globalLoaderSlowAnimationDuration); + return emotion.css` + width: 90%; + animation: + transitionAnimation ${transitionTime}ms linear, + ${slowProgressAnimationTime}ms acceptAnimation ${transitionTime}ms linear; + @keyframes transitionAnimation { + from { + width: ${width}px; + left: ${left}px; + } + to { + width: ${startWidth}px; + left: 0; + } } - to { - width: 90%; + @keyframes acceptAnimation { + from { + width: ${startWidth}px; + } + to { + width: 90%; + } } - } - `; - }, - acceptWithoutAnimation(startWidth: number) { - return css` - width: ${startWidth}px; - `; - }, + `; + }, + acceptWithoutAnimation(startWidth: number) { + return emotion.css` + width: ${startWidth}px; + `; + }, + }; }; diff --git a/packages/react-ui/components/GlobalLoader/GlobalLoaderView.tsx b/packages/react-ui/components/GlobalLoader/GlobalLoaderView.tsx index a9a56f03573..e447997e4c9 100644 --- a/packages/react-ui/components/GlobalLoader/GlobalLoaderView.tsx +++ b/packages/react-ui/components/GlobalLoader/GlobalLoaderView.tsx @@ -1,11 +1,11 @@ import React, { useContext, useRef } from 'react'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { ZIndex } from '../../internal/ZIndex'; +import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { CommonWrapper, CommonProps } from '../../internal/CommonWrapper'; -import { animations, styles } from './GlobalLoaderView.styles'; +import { getAnimations, getStyles } from './GlobalLoaderView.styles'; import { useGlobalLoaderPosition, useGlobalLoaderWidth } from './useParams'; export interface GlobalLoaderViewProps extends Pick { @@ -29,8 +29,12 @@ export const GlobalLoaderView = ({ }: GlobalLoaderViewProps) => { const ref = useRef(null); const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const { width, startWidth, fullWidth } = useGlobalLoaderWidth(status, ref); const { left } = useGlobalLoaderPosition(ref); + const animations = getAnimations(emotion); + const styles = getStyles(emotion); const getAnimationClass = (status: GlobalLoaderViewProps['status']) => { if (!disableAnimations) { @@ -66,7 +70,7 @@ export const GlobalLoaderView = ({ return ( -
+
); diff --git a/packages/react-ui/components/Group/Group.styles.ts b/packages/react-ui/components/Group/Group.styles.ts index 530b409b084..07f5dab9400 100644 --- a/packages/react-ui/components/Group/Group.styles.ts +++ b/packages/react-ui/components/Group/Group.styles.ts @@ -1,42 +1,45 @@ -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; -export const styles = memoizeStyle({ - root() { - return css` - display: inline-flex; - line-height: normal; - `; - }, +import { memoizeStyle } from '../../lib/theming/Emotion'; - fixed() { - return css` - flex-shrink: 0; - display: inline-block; - `; - }, +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root() { + return emotion.css` + display: inline-flex; + line-height: normal; + `; + }, - stretch() { - return css` - flex-grow: 1; - flex-shrink: 1; - `; - }, + fixed() { + return emotion.css` + flex-shrink: 0; + display: inline-block; + `; + }, - stretchFallback() { - return css` - flex-basis: 100%; - `; - }, + stretch() { + return emotion.css` + flex-grow: 1; + flex-shrink: 1; + `; + }, - item() { - return css` - margin-left: -1px; - `; - }, + stretchFallback() { + return emotion.css` + flex-basis: 100%; + `; + }, - itemFirst() { - return css` - margin-left: 0; - `; - }, -}); + item() { + return emotion.css` + margin-left: -1px; + `; + }, + + itemFirst() { + return emotion.css` + margin-left: 0; + `; + }, + }); diff --git a/packages/react-ui/components/Group/Group.tsx b/packages/react-ui/components/Group/Group.tsx index 0a415db1312..e0956fe2eac 100644 --- a/packages/react-ui/components/Group/Group.tsx +++ b/packages/react-ui/components/Group/Group.tsx @@ -1,14 +1,15 @@ import React from 'react'; import PropTypes from 'prop-types'; +import type { Emotion } from '@emotion/css/create-instance'; -import { isIE11, isEdge } from '../../lib/client'; +import { isEdge, isIE11 } from '../../lib/client'; import { isButton } from '../Button'; -import { CommonWrapper, CommonProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { isInputLike } from '../../lib/utils'; -import { styles } from './Group.styles'; +import { getStyles } from './Group.styles'; export interface GroupProps extends CommonProps { width?: React.CSSProperties['width']; @@ -83,12 +84,26 @@ export class Group extends React.Component { public static displayName = 'Group'; private setRootNode!: TSetRootNode; + private emotion!: Emotion; + private styles!: ReturnType; public static propTypes = { width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), }; public render() { + return ( + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return this.renderMain(); + }} + + ); + } + + public renderMain() { const style: React.CSSProperties = { width: this.props.width, }; @@ -96,6 +111,7 @@ export class Group extends React.Component { const childrenArray = React.Children.toArray(this.props.children); const firstChild = getFirstChild(childrenArray); const lastChild = getLastChild(childrenArray); + const styles = this.styles; return ( @@ -113,14 +129,14 @@ export class Group extends React.Component { return (
+ memoizeStyle({ + content(t: Theme) { + return emotion.css` + box-sizing: border-box; + color: ${t.hintColor}; + font-size: ${t.hintFontSize}; + line-height: ${t.hintLineHeight}; + max-width: ${t.hintMaxWidth}; + overflow-wrap: break-word; + padding: ${t.hintPaddingY} ${t.hintPaddingX}; + word-break: break-word; + word-wrap: break-word; + `; + }, - contentCenter(t: Theme) { - return css` - text-align: ${t.hintTextAlign}; - `; - }, -}); + contentCenter(t: Theme) { + return emotion.css` + text-align: ${t.hintTextAlign}; + `; + }, + }); diff --git a/packages/react-ui/components/Hint/Hint.tsx b/packages/react-ui/components/Hint/Hint.tsx index 15ac1581b76..5f86cb613e7 100644 --- a/packages/react-ui/components/Hint/Hint.tsx +++ b/packages/react-ui/components/Hint/Hint.tsx @@ -1,20 +1,21 @@ import React from 'react'; import { globalObject, SafeTimer } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { ThemeFactory } from '../../lib/theming/ThemeFactory'; import { Theme } from '../../lib/theming/Theme'; import { DUMMY_LOCATION, Popup, PopupPinnablePositionsType, ShortPopupPositionsType } from '../../internal/Popup'; import { Nullable } from '../../typings/utility-types'; import { MouseEventType } from '../../typings/event-types'; import { isTestEnv } from '../../lib/currentEnvironment'; -import { CommonWrapper, CommonProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { InstanceWithAnchorElement } from '../../lib/InstanceWithAnchorElement'; import { createPropsGetter } from '../../lib/createPropsGetter'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './Hint.styles'; +import { getStyles } from './Hint.styles'; const HINT_BORDER_COLOR = 'transparent'; @@ -103,6 +104,8 @@ export class Hint extends React.PureComponent implements I private timer: SafeTimer; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private setRootNode!: TSetRootNode; private popupRef = React.createRef(); @@ -134,25 +137,34 @@ export class Hint extends React.PureComponent implements I public render() { return ( - - {(theme) => { - this.theme = theme; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( - - {this.renderMain()} - + + {(theme) => { + this.theme = theme; + return ( + + {this.renderMain()} + + ); + }} + ); }} - + ); } @@ -194,9 +206,9 @@ export class Hint extends React.PureComponent implements I const { maxWidth } = this.getProps(); const centerAlignPositions = ['top', 'top center', 'bottom', 'bottom center']; - const className = cx({ - [styles.content(this.theme)]: true, - [styles.contentCenter(this.theme)]: centerAlignPositions.includes(this.state.position), + const className = this.emotion.cx({ + [this.styles.content(this.theme)]: true, + [this.styles.contentCenter(this.theme)]: centerAlignPositions.includes(this.state.position), }); return (
diff --git a/packages/react-ui/components/Input/Input.styles.ts b/packages/react-ui/components/Input/Input.styles.ts index 129897fc8dd..b48b8bb8344 100644 --- a/packages/react-ui/components/Input/Input.styles.ts +++ b/packages/react-ui/components/Input/Input.styles.ts @@ -1,307 +1,310 @@ -import { css, keyframes, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { shift } from '../../lib/styles/DimensionFunctions'; import { resetText } from '../../lib/styles/Mixins'; -export const styles = memoizeStyle({ - wrapper() { - return css` - align-items: center; - display: flex; - margin: 0; - min-width: 0; - overflow: hidden; - position: relative; - text-overflow: clip; - white-space: nowrap; - width: 100%; - - &::before { - content: '\\A0'; - display: inline-block; - width: 0; - } - `; - }, +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + wrapper() { + return emotion.css` + align-items: center; + display: flex; + margin: 0; + min-width: 0; + overflow: hidden; + position: relative; + text-overflow: clip; + white-space: nowrap; + width: 100%; - root(t: Theme) { - return css` - ${resetText()}; + &::before { + content: '\\A0'; + display: inline-block; + width: 0; + } + `; + }, - align-items: center; - background-clip: ${t.inputBackgroundClip}; - background-color: ${t.inputBg}; - border: ${t.inputBorderWidth} solid ${t.inputBorderColor}; - border-top-color: ${t.inputBorderTopColor}; - transition: border-color ${t.transitionDuration} ${t.transitionTimingFunction}; - box-shadow: ${t.inputShadow}; - box-sizing: border-box; - color: ${t.inputColor}; - cursor: text; - display: inline-flex; - outline: none; - position: relative; - width: ${t.inputWidth}; + root(t: Theme) { + return emotion.css` + ${resetText(emotion)}; - & * { + align-items: center; + background-clip: ${t.inputBackgroundClip}; + background-color: ${t.inputBg}; + border: ${t.inputBorderWidth} solid ${t.inputBorderColor}; + border-top-color: ${t.inputBorderTopColor}; + transition: border-color ${t.transitionDuration} ${t.transitionTimingFunction}; + box-shadow: ${t.inputShadow}; box-sizing: border-box; - } - `; - }, + color: ${t.inputColor}; + cursor: text; + display: inline-flex; + outline: none; + position: relative; + width: ${t.inputWidth}; - hovering(t: Theme) { - return css` - &:hover { - border-color: ${t.inputBorderColorHover}; - } - `; - }, + & * { + box-sizing: border-box; + } + `; + }, - borderless() { - return css` - box-shadow: none; - border-color: transparent; - background-clip: padding-box; - `; - }, + hovering(t: Theme) { + return emotion.css` + &:hover { + border-color: ${t.inputBorderColorHover}; + } + `; + }, - focus(t: Theme) { - return css` - background-color: ${t.inputFocusedBg}; - border-color: ${t.inputBorderColorFocus}; - box-shadow: ${t.inputFocusShadow}; - outline: none; - z-index: 2; - `; - }, + borderless() { + return emotion.css` + box-shadow: none; + border-color: transparent; + background-clip: padding-box; + `; + }, - focusFallback(t: Theme) { - return css` - box-shadow: none; - outline: ${t.inputOutlineWidth} solid ${t.inputFocusOutline}; - `; - }, + focus(t: Theme) { + return emotion.css` + background-color: ${t.inputFocusedBg}; + border-color: ${t.inputBorderColorFocus}; + box-shadow: ${t.inputFocusShadow}; + outline: none; + z-index: 2; + `; + }, - placeholder(t: Theme) { - return css` - -ms-user-select: none; - color: ${t.inputPlaceholderColor}; - cursor: text; - font-size: inherit; - height: 100%; - left: 0; - overflow: hidden; - pointer-events: none; - position: absolute; - top: 0; - user-select: none; - white-space: nowrap; - width: 100%; - `; - }, + focusFallback(t: Theme) { + return emotion.css` + box-shadow: none; + outline: ${t.inputOutlineWidth} solid ${t.inputFocusOutline}; + `; + }, - placeholderFocus(t: Theme) { - return css` - color: ${t.inputPlaceholderColorLight}; - `; - }, + placeholder(t: Theme) { + return emotion.css` + -ms-user-select: none; + color: ${t.inputPlaceholderColor}; + cursor: text; + font-size: inherit; + height: 100%; + left: 0; + overflow: hidden; + pointer-events: none; + position: absolute; + top: 0; + user-select: none; + white-space: nowrap; + width: 100%; + `; + }, - placeholderDisabled(t: Theme) { - return css` - color: ${t.inputPlaceholderColorDisabled}; - `; - }, + placeholderFocus(t: Theme) { + return emotion.css` + color: ${t.inputPlaceholderColorLight}; + `; + }, - input(t: Theme) { - return css` - -webkit-appearance: none; - background: transparent; - border: 0 none; - color: ${t.inputTextColor}; - font: inherit; - line-height: inherit; - margin: 0; - outline: none; - padding: 0; - text-overflow: clip; - white-space: nowrap; - width: 100%; - color-scheme: ${t.inputColorScheme}; + placeholderDisabled(t: Theme) { + return emotion.css` + color: ${t.inputPlaceholderColorDisabled}; + `; + }, - &:-moz-placeholder { - opacity: 1; - } - &::-moz-placeholder { - opacity: 1; - } - &::-ms-clear { - display: none; - } - &:-moz-placeholder { - color: ${t.inputPlaceholderColor}; - } - &::-moz-placeholder { - color: ${t.inputPlaceholderColor}; - } - &::placeholder { - color: ${t.inputPlaceholderColor}; - } - `; - }, + input(t: Theme) { + return emotion.css` + -webkit-appearance: none; + background: transparent; + border: 0 none; + color: ${t.inputTextColor}; + font: inherit; + line-height: inherit; + margin: 0; + outline: none; + padding: 0; + text-overflow: clip; + white-space: nowrap; + width: 100%; + color-scheme: ${t.inputColorScheme}; - inputFocus(t: Theme) { - return css` - &:-moz-placeholder { - color: ${t.inputPlaceholderColorLight}; - } - &::-moz-placeholder { - color: ${t.inputPlaceholderColorLight}; - } - &::placeholder { - color: ${t.inputPlaceholderColorLight}; - } - `; - }, + &:-moz-placeholder { + opacity: 1; + } + &::-moz-placeholder { + opacity: 1; + } + &::-ms-clear { + display: none; + } + &:-moz-placeholder { + color: ${t.inputPlaceholderColor}; + } + &::-moz-placeholder { + color: ${t.inputPlaceholderColor}; + } + &::placeholder { + color: ${t.inputPlaceholderColor}; + } + `; + }, + + inputFocus(t: Theme) { + return emotion.css` + &:-moz-placeholder { + color: ${t.inputPlaceholderColorLight}; + } + &::-moz-placeholder { + color: ${t.inputPlaceholderColorLight}; + } + &::placeholder { + color: ${t.inputPlaceholderColorLight}; + } + `; + }, - inputDisabled(t: Theme) { - return css` - color: ${t.inputTextColorDisabled}; - /* fix text color in safari https://bugs.webkit.org/show_bug.cgi?id=115510 */ - -webkit-text-fill-color: ${t.inputTextColorDisabled}; + inputDisabled(t: Theme) { + return emotion.css` + color: ${t.inputTextColorDisabled}; + /* fix text color in safari https://bugs.webkit.org/show_bug.cgi?id=115510 */ + -webkit-text-fill-color: ${t.inputTextColorDisabled}; - &:-moz-placeholder { - -webkit-text-fill-color: ${t.inputPlaceholderColorDisabled}; - } - &::-moz-placeholder { - -webkit-text-fill-color: ${t.inputPlaceholderColorDisabled}; - } - &::placeholder { - -webkit-text-fill-color: ${t.inputPlaceholderColorDisabled}; - } - `; - }, + &:-moz-placeholder { + -webkit-text-fill-color: ${t.inputPlaceholderColorDisabled}; + } + &::-moz-placeholder { + -webkit-text-fill-color: ${t.inputPlaceholderColorDisabled}; + } + &::placeholder { + -webkit-text-fill-color: ${t.inputPlaceholderColorDisabled}; + } + `; + }, - warning(t: Theme) { - return css` - border-color: ${t.inputBorderColorWarning}; - box-shadow: 0 0 0 ${t.inputOutlineWidth} ${t.inputBorderColorWarning}; - z-index: 2; - `; - }, + warning(t: Theme) { + return emotion.css` + border-color: ${t.inputBorderColorWarning}; + box-shadow: 0 0 0 ${t.inputOutlineWidth} ${t.inputBorderColorWarning}; + z-index: 2; + `; + }, - warningFallback(t: Theme) { - return css` - box-shadow: none; - outline: ${t.inputBorderWidth} solid ${t.inputBorderColorWarning}; - `; - }, + warningFallback(t: Theme) { + return emotion.css` + box-shadow: none; + outline: ${t.inputBorderWidth} solid ${t.inputBorderColorWarning}; + `; + }, - error(t: Theme) { - return css` - border-color: ${t.inputBorderColorError}; - box-shadow: 0 0 0 ${t.inputOutlineWidth} ${t.inputBorderColorError}; - z-index: 2; - `; - }, + error(t: Theme) { + return emotion.css` + border-color: ${t.inputBorderColorError}; + box-shadow: 0 0 0 ${t.inputOutlineWidth} ${t.inputBorderColorError}; + z-index: 2; + `; + }, - errorFallback(t: Theme) { - return css` - box-shadow: none; - outline: ${t.inputBorderWidth} solid ${t.inputBorderColorError}; - `; - }, + errorFallback(t: Theme) { + return emotion.css` + box-shadow: none; + outline: ${t.inputBorderWidth} solid ${t.inputBorderColorError}; + `; + }, - disabled(t: Theme) { - return css` - background-color: ${t.inputDisabledBg}; - border-color: ${t.inputDisabledBorderColor}; - box-shadow: none; - `; - }, + disabled(t: Theme) { + return emotion.css` + background-color: ${t.inputDisabledBg}; + border-color: ${t.inputDisabledBorderColor}; + box-shadow: none; + `; + }, - blink(t: Theme) { - const blinkAnimation = keyframes` + blink(t: Theme) { + const blinkAnimation = emotion.keyframes` 0% { background-color: ${t.inputBlinkColor}; } `; - return css` - animation: ${blinkAnimation} 0.15s ease-in; - `; - }, + return emotion.css` + animation: ${blinkAnimation} 0.15s ease-in; + `; + }, - sizeSmall(t: Theme) { - return css` - font-size: ${t.inputFontSizeSmall}; - line-height: ${t.inputLineHeightSmall}; - padding-top: ${t.inputPaddingYSmall}; - padding-bottom: ${t.inputPaddingYSmall}; - padding-left: ${t.inputPaddingXSmall}; - padding-right: ${t.inputPaddingXSmall}; - height: ${t.inputHeightSmall}; - border-radius: ${t.inputBorderRadiusSmall}; - `; - }, + sizeSmall(t: Theme) { + return emotion.css` + font-size: ${t.inputFontSizeSmall}; + line-height: ${t.inputLineHeightSmall}; + padding-top: ${t.inputPaddingYSmall}; + padding-bottom: ${t.inputPaddingYSmall}; + padding-left: ${t.inputPaddingXSmall}; + padding-right: ${t.inputPaddingXSmall}; + height: ${t.inputHeightSmall}; + border-radius: ${t.inputBorderRadiusSmall}; + `; + }, - sizeSmallFallback(t: Theme) { - return css` - padding-top: ${shift(t.inputPaddingYSmall, '0')}; - padding-bottom: ${shift(t.inputPaddingYSmall, '0')}; - padding-left: ${t.inputPaddingXSmall}; - padding-right: ${t.inputPaddingXSmall}; - line-height: normal; - `; - }, + sizeSmallFallback(t: Theme) { + return emotion.css` + padding-top: ${shift(t.inputPaddingYSmall, '0')}; + padding-bottom: ${shift(t.inputPaddingYSmall, '0')}; + padding-left: ${t.inputPaddingXSmall}; + padding-right: ${t.inputPaddingXSmall}; + line-height: normal; + `; + }, - sizeMedium(t: Theme) { - return css` - font-size: ${t.inputFontSizeMedium}; - line-height: ${t.inputLineHeightMedium}; - padding-top: ${t.inputPaddingYMedium}; - padding-bottom: ${t.inputPaddingYMedium}; - padding-left: ${t.inputPaddingXMedium}; - padding-right: ${t.inputPaddingXMedium}; - height: ${t.inputHeightMedium}; - border-radius: ${t.inputBorderRadiusMedium}; - `; - }, + sizeMedium(t: Theme) { + return emotion.css` + font-size: ${t.inputFontSizeMedium}; + line-height: ${t.inputLineHeightMedium}; + padding-top: ${t.inputPaddingYMedium}; + padding-bottom: ${t.inputPaddingYMedium}; + padding-left: ${t.inputPaddingXMedium}; + padding-right: ${t.inputPaddingXMedium}; + height: ${t.inputHeightMedium}; + border-radius: ${t.inputBorderRadiusMedium}; + `; + }, - sizeMediumFallback(t: Theme) { - return css` - padding-top: ${shift(t.inputPaddingYMedium, '0')}; - padding-bottom: ${shift(t.inputPaddingYMedium, '0')}; - padding-left: ${t.inputPaddingXMedium}; - padding-right: ${t.inputPaddingXMedium}; - line-height: normal; - `; - }, + sizeMediumFallback(t: Theme) { + return emotion.css` + padding-top: ${shift(t.inputPaddingYMedium, '0')}; + padding-bottom: ${shift(t.inputPaddingYMedium, '0')}; + padding-left: ${t.inputPaddingXMedium}; + padding-right: ${t.inputPaddingXMedium}; + line-height: normal; + `; + }, - sizeLarge(t: Theme) { - return css` - font-size: ${t.inputFontSizeLarge}; - line-height: ${t.inputLineHeightLarge}; - height: ${t.inputHeightLarge}; - padding-top: ${shift(t.inputPaddingYLarge, '0')}; - padding-bottom: ${shift(t.inputPaddingYLarge, '0')}; - padding-left: ${t.inputPaddingXLarge}; - padding-right: ${t.inputPaddingXLarge}; - border-radius: ${t.inputBorderRadiusLarge}; - `; - }, + sizeLarge(t: Theme) { + return emotion.css` + font-size: ${t.inputFontSizeLarge}; + line-height: ${t.inputLineHeightLarge}; + height: ${t.inputHeightLarge}; + padding-top: ${shift(t.inputPaddingYLarge, '0')}; + padding-bottom: ${shift(t.inputPaddingYLarge, '0')}; + padding-left: ${t.inputPaddingXLarge}; + padding-right: ${t.inputPaddingXLarge}; + border-radius: ${t.inputBorderRadiusLarge}; + `; + }, - sizeLargeFallback(t: Theme) { - return css` - padding-top: ${shift(t.inputPaddingYLarge, '0')}; - padding-bottom: ${shift(t.inputPaddingYLarge, '0')}; - padding-left: ${t.inputPaddingXLarge}; - padding-right: ${t.inputPaddingXLarge}; - line-height: normal; - `; - }, + sizeLargeFallback(t: Theme) { + return emotion.css` + padding-top: ${shift(t.inputPaddingYLarge, '0')}; + padding-bottom: ${shift(t.inputPaddingYLarge, '0')}; + padding-left: ${t.inputPaddingXLarge}; + padding-right: ${t.inputPaddingXLarge}; + line-height: normal; + `; + }, - hideBlinkingCursor() { - return css` - caret-color: transparent; - `; - }, -}); + hideBlinkingCursor() { + return emotion.css` + caret-color: transparent; + `; + }, + }); diff --git a/packages/react-ui/components/Input/Input.tsx b/packages/react-ui/components/Input/Input.tsx index 98f6d5d4dd7..d585eabd865 100644 --- a/packages/react-ui/components/Input/Input.tsx +++ b/packages/react-ui/components/Input/Input.tsx @@ -3,25 +3,26 @@ import invariant from 'invariant'; import React, { AriaAttributes, ClassAttributes, HTMLAttributes, ReactElement } from 'react'; import warning from 'warning'; import { globalObject, SafeTimer } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { isEdge, isIE11 } from '../../lib/client'; import { isKeyBackspace, isKeyDelete, someKeys } from '../../lib/events/keyboard/identifiers'; import { needsPolyfillPlaceholder } from '../../lib/needsPolyfillPlaceholder'; import { Nullable, Override } from '../../typings/utility-types'; import { InternalMaskedInput } from '../../internal/InternalMaskedInput'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme } from '../../lib/theming/Theme'; import { CommonProps, CommonWrapper, CommonWrapperRestProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { createPropsGetter } from '../../lib/createPropsGetter'; import { SizeProp } from '../../lib/types/props'; import { FocusControlWrapper } from '../../internal/FocusControlWrapper'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { InputElement, InputElementProps } from './Input.typings'; -import { styles } from './Input.styles'; import { InputLayout } from './InputLayout/InputLayout'; import { PolyfillPlaceholder } from './InputLayout/PolyfillPlaceholder'; +import { getStyles } from './Input.styles'; export const inputTypes = ['password', 'text', 'number', 'tel', 'search', 'time', 'date', 'url', 'email'] as const; @@ -189,6 +190,8 @@ export class Input extends React.Component { private selectAllId: number | null = null; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private blinkTimeout: SafeTimer; public input: HTMLInputElement | null = null; private setRootNode!: TSetRootNode; @@ -293,16 +296,24 @@ export class Input extends React.Component { public render(): JSX.Element { return ( - - {(theme) => { - this.theme = theme; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); return ( - - {this.renderMain} - + + {(theme) => { + this.theme = theme; + return ( + + {this.renderMain} + + ); + }} + ); }} - + ); } @@ -397,8 +408,9 @@ export class Input extends React.Component { const { blinking, focused } = this.state; + const styles = this.styles; const labelProps = { - className: cx(styles.root(this.theme), this.getSizeClassName(), { + className: this.emotion.cx(styles.root(this.theme), this.getSizeClassName(), { [styles.focus(this.theme)]: focused && !warning && !error, [styles.hovering(this.theme)]: !focused && !disabled && !warning && !error && !borderless, [styles.blink(this.theme)]: blinking, @@ -419,7 +431,7 @@ export class Input extends React.Component { const inputProps: InputElementProps & ClassAttributes = { ...rest, - className: cx(styles.input(this.theme), { + className: this.emotion.cx(styles.input(this.theme), { [styles.inputFocus(this.theme)]: focused, [styles.inputDisabled(this.theme)]: disabled, }), @@ -483,20 +495,21 @@ export class Input extends React.Component { } private getSizeClassName() { + const styles = this.styles; switch (this.getProps().size) { case 'large': - return cx({ + return this.emotion.cx({ [styles.sizeLarge(this.theme)]: true, [styles.sizeLargeFallback(this.theme)]: isIE11 || isEdge, }); case 'medium': - return cx({ + return this.emotion.cx({ [styles.sizeMedium(this.theme)]: true, [styles.sizeMediumFallback(this.theme)]: isIE11 || isEdge, }); case 'small': default: - return cx({ + return this.emotion.cx({ [styles.sizeSmall(this.theme)]: true, [styles.sizeSmallFallback(this.theme)]: isIE11 || isEdge, }); diff --git a/packages/react-ui/components/Input/InputLayout/InputLayout.styles.ts b/packages/react-ui/components/Input/InputLayout/InputLayout.styles.ts index af4dfa2e45b..85ff1f6d2ec 100644 --- a/packages/react-ui/components/Input/InputLayout/InputLayout.styles.ts +++ b/packages/react-ui/components/Input/InputLayout/InputLayout.styles.ts @@ -1,56 +1,59 @@ +import type { Emotion } from '@emotion/css/create-instance'; + import { ZERO_WIDTH_SPACE_CSS } from '../../../lib/chars'; -import { css, memoizeStyle } from '../../../lib/theming/Emotion'; +import { memoizeStyle } from '../../../lib/theming/Emotion'; import { Theme } from '../../../lib/theming/Theme'; -import { styles } from '../Input.styles'; +import { getStyles } from '../Input.styles'; -export const stylesLayout = memoizeStyle({ - root(t: Theme) { - return styles.root(t); - }, - input() { - return css` - min-width: 0; - overflow: hidden; - position: relative; - width: 100%; - display: flex; - `; - }, - aside() { - return css` - display: inline-flex; - align-items: center; - flex-shrink: 0; +export const getStylesLayout = (emotion: Emotion) => + memoizeStyle({ + root(t: Theme) { + return getStyles(emotion).root(t); + }, + input() { + return emotion.css` + min-width: 0; + overflow: hidden; + position: relative; + width: 100%; + display: flex; + `; + }, + aside() { + return emotion.css` + display: inline-flex; + align-items: center; + flex-shrink: 0; - &::before { - content: '${ZERO_WIDTH_SPACE_CSS}'; - } - `; - }, - icon(t: Theme) { - return css` - color: ${t.inputIconColor}; - `; - }, - iconFocus(t: Theme) { - return css` - color: ${t.inputFocusedIconColor}; - `; - }, - iconDisabled(t: Theme) { - return css` - cursor: default; - color: ${t.inputIconColorDisabled}; - `; - }, - text(t: Theme) { - return css` - color: ${t.inputPlaceholderColor}; - `; - }, - textDisabled(t: Theme) { - return css` - color: ${t.inputPlaceholderColorDisabled}; - `; - }, -}); + &::before { + content: '${ZERO_WIDTH_SPACE_CSS}'; + } + `; + }, + icon(t: Theme) { + return emotion.css` + color: ${t.inputIconColor}; + `; + }, + iconFocus(t: Theme) { + return emotion.css` + color: ${t.inputFocusedIconColor}; + `; + }, + iconDisabled(t: Theme) { + return emotion.css` + cursor: default; + color: ${t.inputIconColorDisabled}; + `; + }, + text(t: Theme) { + return emotion.css` + color: ${t.inputPlaceholderColor}; + `; + }, + textDisabled(t: Theme) { + return emotion.css` + color: ${t.inputPlaceholderColorDisabled}; + `; + }, + }); diff --git a/packages/react-ui/components/Input/InputLayout/InputLayout.tsx b/packages/react-ui/components/Input/InputLayout/InputLayout.tsx index d0007ef5ff8..ecb1680874f 100644 --- a/packages/react-ui/components/Input/InputLayout/InputLayout.tsx +++ b/packages/react-ui/components/Input/InputLayout/InputLayout.tsx @@ -1,12 +1,13 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { forwardRefAndName } from '../../../lib/forwardRefAndName'; import { InputDataTids, InputProps } from '../Input'; import { CommonProps, CommonWrapper } from '../../../internal/CommonWrapper'; +import { EmotionContext } from '../../../lib/theming/Emotion'; import { InputLayoutAside } from './InputLayoutAside'; import { InputLayoutContext, InputLayoutContextDefault, InputLayoutContextProps } from './InputLayoutContext'; -import { stylesLayout } from './InputLayout.styles'; +import { getStylesLayout } from './InputLayout.styles'; type InputLayoutRootFromInputProps = Pick; @@ -18,6 +19,8 @@ export interface InputLayoutRootProps extends InputLayoutRootFromInputProps, Com export const InputLayout = forwardRefAndName('InputLayout', (props, ref) => { const { leftIcon, rightIcon, prefix, suffix, labelProps, context, children } = props; const _context: InputLayoutContextProps = { ...InputLayoutContextDefault, ...context }; + const emotion = useContext(EmotionContext); + const stylesLayout = getStylesLayout(emotion); return ( diff --git a/packages/react-ui/components/Input/InputLayout/InputLayoutAside.tsx b/packages/react-ui/components/Input/InputLayout/InputLayoutAside.tsx index 0fe028bed25..5196de5c96b 100644 --- a/packages/react-ui/components/Input/InputLayout/InputLayoutAside.tsx +++ b/packages/react-ui/components/Input/InputLayout/InputLayoutAside.tsx @@ -1,8 +1,9 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { InputProps } from '../Input'; +import { EmotionContext } from '../../../lib/theming/Emotion'; -import { stylesLayout } from './InputLayout.styles'; +import { getStylesLayout } from './InputLayout.styles'; import { InputLayoutAsideIcon } from './InputLayoutAsideIcon'; import { InputLayoutAsideText } from './InputLayoutAsideText'; @@ -13,7 +14,8 @@ export interface InputLayoutAsideProps { } export const InputLayoutAside: React.FunctionComponent = ({ icon, text, side }) => { - const asideClassName = stylesLayout.aside(); + const emotion = useContext(EmotionContext); + const asideClassName = getStylesLayout(emotion).aside(); const _icon = ; const _text = ; diff --git a/packages/react-ui/components/Input/InputLayout/InputLayoutAsideIcon.tsx b/packages/react-ui/components/Input/InputLayout/InputLayoutAsideIcon.tsx index 07314f99d3b..afbfc17e388 100644 --- a/packages/react-ui/components/Input/InputLayout/InputLayoutAsideIcon.tsx +++ b/packages/react-ui/components/Input/InputLayout/InputLayoutAsideIcon.tsx @@ -1,21 +1,23 @@ -import React, { ReactElement } from 'react'; +import React, { ReactElement, useContext } from 'react'; import { isElement } from 'react-is'; import { isKonturIcon } from '../../../lib/utils'; import { InputProps } from '../Input'; -import { cx } from '../../../lib/theming/Emotion'; -import { ThemeContext } from '../../../lib/theming/ThemeContext'; +import { EmotionContext } from '../../../lib/theming/Emotion'; import { SizeProp } from '../../../lib/types/props'; +import { ThemeContext } from '../../../lib/theming/ThemeContext'; import { InputLayoutContext } from './InputLayoutContext'; -import { stylesLayout } from './InputLayout.styles'; +import { getStylesLayout } from './InputLayout.styles'; + export interface InputLayoutAsideIconProps { icon: InputProps['leftIcon'] | InputProps['rightIcon']; side: 'left' | 'right'; } export const InputLayoutAsideIcon: React.FunctionComponent = ({ icon = null, side }) => { - const theme = React.useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const theme = useContext(ThemeContext); const { focused, disabled, size } = React.useContext(InputLayoutContext); const sizes: Record = { @@ -43,10 +45,11 @@ export const InputLayoutAsideIcon: React.FunctionComponent = ({ text = null }) => { - const theme = React.useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const theme = useContext(ThemeContext); const { disabled } = React.useContext(InputLayoutContext); + const stylesLayout = getStylesLayout(emotion); const asideClassName = stylesLayout.aside(); return text ? ( - + {text} ) : null; diff --git a/packages/react-ui/components/Input/InputLayout/PolyfillPlaceholder.tsx b/packages/react-ui/components/Input/InputLayout/PolyfillPlaceholder.tsx index 22e27c7a91f..57d95b7f5f6 100644 --- a/packages/react-ui/components/Input/InputLayout/PolyfillPlaceholder.tsx +++ b/packages/react-ui/components/Input/InputLayout/PolyfillPlaceholder.tsx @@ -1,10 +1,10 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { InputProps } from '../Input'; -import { cx } from '../../../lib/theming/Emotion'; -import { ThemeContext } from '../../../lib/theming/ThemeContext'; -import { styles } from '../Input.styles'; +import { EmotionContext } from '../../../lib/theming/Emotion'; +import { getStyles } from '../Input.styles'; import { needsPolyfillPlaceholder } from '../../../lib/needsPolyfillPlaceholder'; +import { ThemeContext } from '../../../lib/theming/ThemeContext'; import { InputLayoutContext } from './InputLayoutContext'; @@ -23,15 +23,17 @@ export const PolyfillPlaceholder: React.FunctionComponent { - const theme = React.useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const theme = useContext(ThemeContext); const { focused, disabled } = React.useContext(InputLayoutContext); let _placeholder = null; + const styles = getStyles(emotion); if (needsPolyfillPlaceholder && placeholder && !isMaskVisible && !value && !defaultValue) { _placeholder = (
+ memoizeStyle({ + kebab(t: Theme) { + return emotion.css` + display: inline-flex; + align-items: center; + justify-content: center; + text-align: center; + border: ${t.kebabBorder}; + box-sizing: border-box; + border-radius: ${t.kebabBorderRadius}; + user-select: none; + cursor: pointer; + outline: 0; + transition: background 0.12s ease-out; + background: ${t.kebabBackground}; - &:hover { - background: ${t.kebabBackgroundHover}; - } + &:hover { + background: ${t.kebabBackgroundHover}; + } - &:active { - background: ${t.kebabBackgroundActive}; - } - `; - }, + &:active { + background: ${t.kebabBackgroundActive}; + } + `; + }, - kebabSmall(t: Theme) { - return css` - width: ${t.kebabSizeSmall}; - height: ${t.kebabSizeSmall}; - `; - }, + kebabSmall(t: Theme) { + return emotion.css` + width: ${t.kebabSizeSmall}; + height: ${t.kebabSizeSmall}; + `; + }, - kebabMedium(t: Theme) { - return css` - width: ${t.kebabSizeMedium}; - height: ${t.kebabSizeMedium}; - `; - }, + kebabMedium(t: Theme) { + return emotion.css` + width: ${t.kebabSizeMedium}; + height: ${t.kebabSizeMedium}; + `; + }, - kebabLarge(t: Theme) { - return css` - width: ${t.kebabSizeLarge}; - height: ${t.kebabSizeLarge}; - `; - }, + kebabLarge(t: Theme) { + return emotion.css` + width: ${t.kebabSizeLarge}; + height: ${t.kebabSizeLarge}; + `; + }, - focused(t: Theme) { - return css` - background: ${t.kebabBackgroundHover}; - border-color: ${t.borderColorFocus}; - `; - }, + focused(t: Theme) { + return emotion.css` + background: ${t.kebabBackgroundHover}; + border-color: ${t.borderColorFocus}; + `; + }, - opened(t: Theme) { - return css` - background: ${t.kebabBackgroundActive} !important; // override kebab:hover style - cursor: default; - `; - }, + opened(t: Theme) { + return emotion.css` + background: ${t.kebabBackgroundActive} !important; // override kebab:hover style + cursor: default; + `; + }, - disabled() { - return css` - cursor: default; + disabled() { + return emotion.css` + cursor: default; - &:hover { - background: none; - } - `; - }, + &:hover { + background: none; + } + `; + }, - menu(t: Theme) { - return css` - overflow: hidden; - border-radius: ${t.popupBorderRadius}; - `; - }, -}); + menu(t: Theme) { + return emotion.css` + overflow: hidden; + border-radius: ${t.popupBorderRadius}; + `; + }, + }); diff --git a/packages/react-ui/components/Kebab/Kebab.tsx b/packages/react-ui/components/Kebab/Kebab.tsx index 9dfcdb15b30..a5a6179767b 100644 --- a/packages/react-ui/components/Kebab/Kebab.tsx +++ b/packages/react-ui/components/Kebab/Kebab.tsx @@ -2,6 +2,7 @@ import React, { AriaAttributes, ReactElement, HTMLAttributes } from 'react'; import PropTypes from 'prop-types'; import { isElement } from 'react-is'; import { globalObject } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { isKonturIcon } from '../../lib/utils'; import { isKeyArrowVertical, isKeyEnter, isKeySpace, someKeys } from '../../lib/events/keyboard/identifiers'; @@ -10,18 +11,18 @@ import { keyListener } from '../../lib/events/keyListener'; import { PopupMenu, PopupMenuCaptionProps, PopupMenuProps } from '../../internal/PopupMenu'; import { Nullable } from '../../typings/utility-types'; import { PopupPositionsType } from '../../internal/Popup'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme } from '../../lib/theming/Theme'; import { isTestEnv } from '../../lib/currentEnvironment'; import { ThemeFactory } from '../../lib/theming/ThemeFactory'; -import { CommonWrapper, CommonProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { createPropsGetter } from '../../lib/createPropsGetter'; import { SizeProp } from '../../lib/types/props'; import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/utils/getVisualStateDataAttributes'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './Kebab.styles'; +import { getStyles } from './Kebab.styles'; import { KebabIcon } from './KebabIcon'; export interface KebabProps @@ -89,6 +90,8 @@ export class Kebab extends React.Component { }; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private setRootNode!: TSetRootNode; private listener: { @@ -108,23 +111,31 @@ export class Kebab extends React.Component { public render(): JSX.Element { return ( - - {(theme) => { - this.theme = theme; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); return ( - - {this.renderMain()} - + + {(theme) => { + this.theme = theme; + return ( + + {this.renderMain()} + + ); + }} + ); }} - + ); } @@ -166,6 +177,7 @@ export class Kebab extends React.Component { captionProps.toggleMenu(); } }; + const styles = this.styles; return ( { onKeyDown={handleCaptionKeyDown} onFocus={this.handleFocus} onBlur={this.handleBlur} - className={cx( + className={this.emotion.cx( styles.kebab(this.theme), size === 'small' && styles.kebabSmall(this.theme), size === 'medium' && styles.kebabMedium(this.theme), diff --git a/packages/react-ui/components/Link/Link.styles.ts b/packages/react-ui/components/Link/Link.styles.ts index 16d22c330ec..c9c24e2f81e 100644 --- a/packages/react-ui/components/Link/Link.styles.ts +++ b/packages/react-ui/components/Link/Link.styles.ts @@ -1,9 +1,12 @@ -import { css, keyframes, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { linkDisabledMixin, linkUseColorsMixin } from './Link.mixins'; -const line = keyframes` +const oldLineText = (t: Theme, emotion: Emotion) => { + const line = emotion.keyframes` 0% { text-decoration-color: inherit; } @@ -11,10 +14,8 @@ const line = keyframes` text-decoration-color: transparent; } `; - -const oldLineText = function (t: Theme) { const delay = parseFloat(t.linkTextUnderlineOpacity) - 1; - return css` + return emotion.css` animation: ${line} 1s linear !important; // override creevey animation-play-state: paused !important; animation-delay: ${delay}s !important; @@ -22,164 +23,165 @@ const oldLineText = function (t: Theme) { `; }; -export const styles = memoizeStyle({ - root(t: Theme) { - return css` - cursor: pointer; - position: relative; - - border-radius: 1px; - text-decoration: ${t.linkTextDecoration}; - text-decoration-style: ${t.linkTextDecorationStyle}; - text-underline-offset: ${t.linkTextUnderlineOffset}; - text-decoration-thickness: ${t.linkTextDecorationThickness}; - transition: text-decoration-color ${t.transitionDuration} ${t.transitionTimingFunction}; - @supports (text-decoration-color: ${t.linkTextDecorationColor}) { - text-decoration-color: ${t.linkTextDecorationColor}; - &:hover { - text-decoration-color: currentColor; - text-decoration-style: ${t.linkHoverTextDecorationStyle}; +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root(t: Theme) { + return emotion.css` + cursor: pointer; + position: relative; + + border-radius: 1px; + text-decoration: ${t.linkTextDecoration}; + text-decoration-style: ${t.linkTextDecorationStyle}; + text-underline-offset: ${t.linkTextUnderlineOffset}; + text-decoration-thickness: ${t.linkTextDecorationThickness}; + transition: text-decoration-color ${t.transitionDuration} ${t.transitionTimingFunction}; + @supports (text-decoration-color: ${t.linkTextDecorationColor}) { + text-decoration-color: ${t.linkTextDecorationColor}; + &:hover { + text-decoration-color: currentColor; + text-decoration-style: ${t.linkHoverTextDecorationStyle}; + } } - } - @supports not (text-decoration-color: ${t.linkTextDecorationColor}) { - ${oldLineText(t)}; - &:hover { - text-decoration-style: ${t.linkHoverTextDecorationStyle}; - animation: none !important; + @supports not (text-decoration-color: ${t.linkTextDecorationColor}) { + ${oldLineText(t, emotion)}; + &:hover { + text-decoration-style: ${t.linkHoverTextDecorationStyle}; + animation: none !important; + } } - } - `; - }, - - lineFocus(t: Theme) { - return css` - color: ${t.linkHoverColor}; - `; - }, - - lineFocusSuccess(t: Theme) { - return css` - color: ${t.linkSuccessHoverColor} !important; - `; - }, - - lineFocusDanger(t: Theme) { - return css` - color: ${t.linkDangerHoverColor} !important; - `; - }, - - lineFocusGrayed(t: Theme) { - return css` - color: ${t.linkGrayedHoverColor} !important; - `; - }, - - button(t: Theme) { - return css` - display: inline-block; - line-height: ${t.linkButtonLineHeight}; - padding-left: ${t.linkButtonPaddingX}; - padding-right: ${t.linkButtonPaddingX}; - `; - }, - - buttonOpened(t: Theme) { - return css` - background: ${t.btnDefaultActiveBg}; - `; - }, - - arrow() { - return css` - border: 4px solid transparent; - border-bottom-width: 0; - border-top-color: #a0a0a0; - display: inline-block; - margin-bottom: 3px; - margin-left: 3px; - vertical-align: middle; - `; - }, - - useDefault(t: Theme) { - return css` - ${linkUseColorsMixin(t.linkColor, t.linkHoverColor, t.linkActiveColor)}; - `; - }, - - useSuccess(t: Theme) { - return css` - ${linkUseColorsMixin(t.linkSuccessColor, t.linkSuccessHoverColor, t.linkSuccessActiveColor)}; - `; - }, - - useDanger(t: Theme) { - return css` - ${linkUseColorsMixin(t.linkDangerColor, t.linkDangerHoverColor, t.linkDangerActiveColor)}; - `; - }, - - useGrayed(t: Theme) { - return css` - ${linkUseColorsMixin(t.linkGrayedColor, t.linkGrayedHoverColor, t.linkGrayedActiveColor)}; - `; - }, - - useGrayedFocus(t: Theme) { - return css` - color: ${t.linkDisabledColor}; - `; - }, - - focus(t: Theme) { - return css` - text-decoration: ${t.linkHoverTextDecoration}; - outline: ${t.linkFocusOutline}; - `; - }, - - disabled(t: Theme) { - return css` - ${linkDisabledMixin()}; - - color: ${t.linkDisabledColor} !important; // override root color - - &:hover { + `; + }, + + lineFocus(t: Theme) { + return emotion.css` + color: ${t.linkHoverColor}; + `; + }, + + lineFocusSuccess(t: Theme) { + return emotion.css` + color: ${t.linkSuccessHoverColor} !important; + `; + }, + + lineFocusDanger(t: Theme) { + return emotion.css` + color: ${t.linkDangerHoverColor} !important; + `; + }, + + lineFocusGrayed(t: Theme) { + return emotion.css` + color: ${t.linkGrayedHoverColor} !important; + `; + }, + + button(t: Theme) { + return emotion.css` + display: inline-block; + line-height: ${t.linkButtonLineHeight}; + padding-left: ${t.linkButtonPaddingX}; + padding-right: ${t.linkButtonPaddingX}; + `; + }, + + buttonOpened(t: Theme) { + return emotion.css` + background: ${t.btnDefaultActiveBg}; + `; + }, + + arrow() { + return emotion.css` + border: 4px solid transparent; + border-bottom-width: 0; + border-top-color: #a0a0a0; + display: inline-block; + margin-bottom: 3px; + margin-left: 3px; + vertical-align: middle; + `; + }, + + useDefault(t: Theme) { + return emotion.css` + ${linkUseColorsMixin(t.linkColor, t.linkHoverColor, t.linkActiveColor)}; + `; + }, + + useSuccess(t: Theme) { + return emotion.css` + ${linkUseColorsMixin(t.linkSuccessColor, t.linkSuccessHoverColor, t.linkSuccessActiveColor)}; + `; + }, + + useDanger(t: Theme) { + return emotion.css` + ${linkUseColorsMixin(t.linkDangerColor, t.linkDangerHoverColor, t.linkDangerActiveColor)}; + `; + }, + + useGrayed(t: Theme) { + return emotion.css` + ${linkUseColorsMixin(t.linkGrayedColor, t.linkGrayedHoverColor, t.linkGrayedActiveColor)}; + `; + }, + + useGrayedFocus(t: Theme) { + return emotion.css` color: ${t.linkDisabledColor}; - } - `; - }, + `; + }, - icon() { - return css` - display: inline-block; - `; - }, - - iconLeft(t: Theme) { - return css` - margin-right: ${t.linkIconMarginRight}; - `; - }, + focus(t: Theme) { + return emotion.css` + text-decoration: ${t.linkHoverTextDecoration}; + outline: ${t.linkFocusOutline}; + `; + }, - iconRight(t: Theme) { - return css` - margin-left: ${t.linkIconMarginLeft}; - `; - }, + disabled(t: Theme) { + return emotion.css` + ${linkDisabledMixin()}; - warning(t: Theme) { - return css` - background-color: ${t.btnWarningSecondary}; - box-shadow: 0 0 0 2px ${t.btnWarningSecondary}; - `; - }, + color: ${t.linkDisabledColor} !important; // override root color - error(t: Theme) { - return css` - background-color: ${t.btnErrorSecondary}; - box-shadow: 0 0 0 2px ${t.btnErrorSecondary}; - `; - }, -}); + &:hover { + color: ${t.linkDisabledColor}; + } + `; + }, + + icon() { + return emotion.css` + display: inline-block; + `; + }, + + iconLeft(t: Theme) { + return emotion.css` + margin-right: ${t.linkIconMarginRight}; + `; + }, + + iconRight(t: Theme) { + return emotion.css` + margin-left: ${t.linkIconMarginLeft}; + `; + }, + + warning(t: Theme) { + return emotion.css` + background-color: ${t.btnWarningSecondary}; + box-shadow: 0 0 0 2px ${t.btnWarningSecondary}; + `; + }, + + error(t: Theme) { + return emotion.css` + background-color: ${t.btnErrorSecondary}; + box-shadow: 0 0 0 2px ${t.btnErrorSecondary}; + `; + }, + }); diff --git a/packages/react-ui/components/Link/Link.tsx b/packages/react-ui/components/Link/Link.tsx index 67db6c3abc5..878d4e85c73 100644 --- a/packages/react-ui/components/Link/Link.tsx +++ b/packages/react-ui/components/Link/Link.tsx @@ -1,21 +1,22 @@ import React from 'react'; import { globalObject } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { ButtonLinkAllowedValues } from '../../lib/types/button-link'; import { resetButton } from '../../lib/styles/Mixins'; import { PolymorphicPropsWithoutRef } from '../../lib/types/polymorphic-component'; import { keyListener } from '../../lib/events/keyListener'; import { Theme, ThemeIn } from '../../lib/theming/Theme'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { isExternalLink } from '../../lib/utils'; import { CommonProps, CommonWrapper, CommonWrapperRestProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { createPropsGetter, DefaultizedProps } from '../../lib/createPropsGetter'; import { ThemeFactory } from '../../lib/theming/ThemeFactory'; import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/utils/getVisualStateDataAttributes'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './Link.styles'; +import { getStyles } from './Link.styles'; import { LinkIcon } from './LinkIcon'; export interface LinkInnerProps extends CommonProps { @@ -110,20 +111,30 @@ export class Link; private setRootNode!: TSetRootNode; public render(): JSX.Element { return ( - - {(theme) => { - this.theme = this.props.theme ? ThemeFactory.create(this.props.theme as Theme, theme) : theme; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); return ( - - {this.renderMain} - + + {(theme) => { + this.theme = this.props.theme ? ThemeFactory.create(this.props.theme as Theme, theme) : theme; + return ( + + {this.renderMain} + + ); + }} + ); }} - + ); } @@ -166,6 +177,7 @@ export class Link { position?: 'left' | 'right'; @@ -14,10 +14,12 @@ export interface LinkIconProps extends Pick { export const LinkIcon = ({ icon, loading, hasBothIcons, position }: LinkIconProps) => { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); return ( + memoizeStyle({ + active(t: Theme) { + return emotion.css` + border-radius: ${t.loaderBorderRadius}; + position: absolute; + bottom: 0; + left: 0; + right: 0; + top: 0; + + background: ${t.loaderBg}; + `; + }, + + loader() { + return emotion.css` + box-sizing: border-box; + display: inline-block; + position: relative; + width: 100%; + z-index: inherit; + `; + }, + + spinnerContainer() { + return emotion.css` + display: block; + margin: auto; + text-align: center; + + position: absolute; + bottom: 0; + left: 0; + right: 0; + top: 0; + + &::before { + content: ' '; + display: inline-block; + height: 100%; + min-height: 100%; + vertical-align: middle; + } + `; + }, + + spinnerContainerSticky() { + return emotion.css` + position: fixed; + `; + }, + + spinnerComponentWrapper() { + return emotion.css` display: inline-block; - height: 100%; - min-height: 100%; - vertical-align: middle; - } - `; - }, - - spinnerContainerSticky() { - return css` - position: fixed; - `; - }, - - spinnerComponentWrapper() { - return css` - display: inline-block; - `; - }, -}); + `; + }, + }); diff --git a/packages/react-ui/components/Loader/Loader.tsx b/packages/react-ui/components/Loader/Loader.tsx index 1da0a1f6f3b..1dde8ec910d 100644 --- a/packages/react-ui/components/Loader/Loader.tsx +++ b/packages/react-ui/components/Loader/Loader.tsx @@ -2,24 +2,25 @@ import React from 'react'; import PropTypes from 'prop-types'; import debounce from 'lodash.debounce'; import { globalObject, isBrowser } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { AnyObject } from '../../lib/utils'; import * as LayoutEvents from '../../lib/LayoutEvents'; import { Spinner, SpinnerProps } from '../Spinner'; import { Nullable } from '../../typings/utility-types'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme } from '../../lib/theming/Theme'; import { ZIndex } from '../../internal/ZIndex'; -import { CommonWrapper, CommonProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { isTestEnv } from '../../lib/currentEnvironment'; import { TaskWithDelayAndMinimalDuration } from '../../lib/taskWithDelayAndMinimalDuration'; import { getTabbableElements } from '../../lib/dom/tabbableHelpers'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { getDOMRect } from '../../lib/dom/getDOMRect'; import { createPropsGetter } from '../../lib/createPropsGetter'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './Loader.styles'; +import { getStyles } from './Loader.styles'; const types = ['mini', 'normal', 'big'] as const; @@ -132,6 +133,8 @@ export class Loader extends React.Component { }; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private setRootNode!: TSetRootNode; private spinnerContainerNode: Nullable; private childrenContainerNode: Nullable; @@ -230,12 +233,20 @@ export class Loader extends React.Component { public render() { return ( - - {(theme) => { - this.theme = theme; - return this.renderMain(); + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( + + {(theme) => { + this.theme = theme; + return this.renderMain(); + }} + + ); }} - + ); } @@ -246,7 +257,7 @@ export class Loader extends React.Component { return ( -
+
{ {isLoaderActive && ( {this.state.isSpinnerVisible && this.renderSpinner(type, caption, component)} @@ -282,11 +293,13 @@ export class Loader extends React.Component { return (
{ this.spinnerNode = element; }} diff --git a/packages/react-ui/components/Loader/__stories__/Loader.stories.tsx b/packages/react-ui/components/Loader/__stories__/Loader.stories.tsx index 23632e964ee..f993847e8b7 100644 --- a/packages/react-ui/components/Loader/__stories__/Loader.stories.tsx +++ b/packages/react-ui/components/Loader/__stories__/Loader.stories.tsx @@ -3,17 +3,13 @@ import React, { useState } from 'react'; import { AnyObject } from '../../../lib/utils'; import { Story } from '../../../typings/stories'; import { Loader, LoaderProps } from '../Loader'; -import { css } from '../../../lib/theming/Emotion'; import { EyeOpenIcon16Light } from '../../../internal/icons2022/EyeOpenIcon/EyeOpenIcon16Light'; -import { ThemeContext } from '../../../lib/theming/ThemeContext'; import { Toggle } from '../../Toggle'; +import { ThemeContext } from '../../../lib/theming/ThemeContext'; +import { EmotionConsumer } from '../../../lib/theming/Emotion'; import { LoaderAndButton } from './LoaderAndButton'; -const loaderClass = css` - height: 100%; -`; - const wrapperStyle = { width: '800px', background: 'AliceBlue', @@ -224,43 +220,65 @@ ActiveLoader.storyName = 'Active loader'; export const InactiveLoader: Story = () => ; InactiveLoader.storyName = 'Inactive loader'; -export const WrapperWithCustomHeightAndInactiveLoader = () => ( - - {(theme) => { - return ( - -
- -
-
- ); - }} -
-); +export const WrapperWithCustomHeightAndInactiveLoader = () => { + return ( + + {(emotion) => ( + + {(theme) => { + const loaderClass = emotion.css` + height: 100%; + `; + return ( + +
+ +
+
+ ); + }} +
+ )} +
+ ); +}; WrapperWithCustomHeightAndInactiveLoader.storyName = 'Wrapper with custom height and inactive loader'; export const WrapperWithCustomHeightAndActiveLoader = () => ( - - {(theme) => { - return ( - -
- -
-
- ); - }} -
+ + {(emotion) => ( + + {(theme) => { + const loaderClass = emotion.css` + height: 100%; + `; + return ( + +
+ +
+
+ ); + }} +
+ )} +
); WrapperWithCustomHeightAndActiveLoader.storyName = 'Wrapper with custom height and active loader'; diff --git a/packages/react-ui/components/MaskedInput/ColorableInputElement/ColorableInputElement.tsx b/packages/react-ui/components/MaskedInput/ColorableInputElement/ColorableInputElement.tsx index 3f86c3f5a73..6f12b332aac 100644 --- a/packages/react-ui/components/MaskedInput/ColorableInputElement/ColorableInputElement.tsx +++ b/packages/react-ui/components/MaskedInput/ColorableInputElement/ColorableInputElement.tsx @@ -5,7 +5,7 @@ import debounce from 'lodash.debounce'; import { ThemeContext } from '../../../lib/theming/ThemeContext'; import { InputElement, InputElementProps } from '../../Input'; import { forwardRefAndName } from '../../../lib/forwardRefAndName'; -import { cx } from '../../../lib/theming/Emotion'; +import { EmotionContext } from '../../../lib/theming/Emotion'; import { globalClasses } from './ColorableInputElement.styles'; @@ -27,6 +27,7 @@ export const ColorableInputElement = forwardRefAndName( const focused = useRef(false); const inputStyle = React.useRef(); const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); const debouncedPaintText = useCallback(debounce(paintText), []); const [active, setActive] = useState(true); @@ -62,7 +63,7 @@ export const ColorableInputElement = forwardRefAndName( onFocus: handleFocus, onBlur: handleBlur, inputRef, - className: cx(props.className, active && globalClasses.input), + className: emotion.cx(props.className, active && globalClasses.input), })} {active && } diff --git a/packages/react-ui/components/MaskedInput/MaskedInput.tsx b/packages/react-ui/components/MaskedInput/MaskedInput.tsx index b0e8d8d8267..c6f707ed911 100644 --- a/packages/react-ui/components/MaskedInput/MaskedInput.tsx +++ b/packages/react-ui/components/MaskedInput/MaskedInput.tsx @@ -1,11 +1,11 @@ -import React, { Ref, useImperativeHandle, useRef, useState, useEffect } from 'react'; +import React, { Ref, useImperativeHandle, useRef, useState, useEffect, useContext } from 'react'; import { IMaskInputProps } from 'react-imask'; import { forwardRefAndName } from '../../lib/forwardRefAndName'; -import { cx } from '../../lib/theming/Emotion'; import { uiFontGlobalClasses } from '../../lib/styles/UiFont'; import { Input, InputProps, InputType } from '../Input'; import { isKeyBackspace, isKeyDelete } from '../../lib/events/keyboard/identifiers'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { globalClasses } from './MaskedInput.styles'; import { getDefinitions, getMaskChar } from './MaskedInput.helpers'; @@ -95,6 +95,7 @@ export const MaskedInput = forwardRefAndName( ...inputProps } = props; + const emotion = useContext(EmotionContext); const inputRef = useRef(null); const [focused, setFocused] = useState(false); @@ -134,7 +135,7 @@ export const MaskedInput = forwardRefAndName( onBlur={handleBlur} onInput={handleInput} onKeyDown={handleKeyDown} - className={cx(globalClasses.root, uiFontGlobalClasses.root, className)} + className={emotion.cx(globalClasses.root, uiFontGlobalClasses.root, className)} element={ colored ? ( diff --git a/packages/react-ui/components/MenuFooter/MenuFooter.mixins.ts b/packages/react-ui/components/MenuFooter/MenuFooter.mixins.ts index 69bb8c8ad02..512402f7cc8 100644 --- a/packages/react-ui/components/MenuFooter/MenuFooter.mixins.ts +++ b/packages/react-ui/components/MenuFooter/MenuFooter.mixins.ts @@ -1,23 +1,25 @@ -import { css } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; -export const menuFooterSizeMixin = ( - menuFooterPaddingX: string, - menuFooterFontSize: string, - menuFooterLineHeight: string, - menuFooterPaddingTop: string, - menuFooterPaddingBottom: string, -) => { - const paddingRight = menuFooterPaddingX; +export const menuFooterSizeMixin = + (emotion: Emotion) => + ( + menuFooterPaddingX: string, + menuFooterFontSize: string, + menuFooterLineHeight: string, + menuFooterPaddingTop: string, + menuFooterPaddingBottom: string, + ) => { + const paddingRight = menuFooterPaddingX; - return css` + return emotion.css` font-size: ${menuFooterFontSize}; line-height: ${menuFooterLineHeight}; padding: ${menuFooterPaddingTop} ${paddingRight} ${menuFooterPaddingBottom} ${menuFooterPaddingX}; `; -}; + }; -export const withIconSizeMixin = (menuItemPaddingForIcon: string) => { - return css` +export const withIconSizeMixin = (emotion: Emotion) => (menuItemPaddingForIcon: string) => { + return emotion.css` padding-left: ${menuItemPaddingForIcon}; `; }; diff --git a/packages/react-ui/components/MenuFooter/MenuFooter.styles.ts b/packages/react-ui/components/MenuFooter/MenuFooter.styles.ts index 8855c0c419b..305c63331d7 100644 --- a/packages/react-ui/components/MenuFooter/MenuFooter.styles.ts +++ b/packages/react-ui/components/MenuFooter/MenuFooter.styles.ts @@ -1,63 +1,66 @@ -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { menuFooterSizeMixin, withIconSizeMixin } from './MenuFooter.mixins'; -export const styles = memoizeStyle({ - root(t: Theme) { - return css` - color: ${t.menuFooterColor}; - cursor: default; - `; - }, +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root(t: Theme) { + return emotion.css` + color: ${t.menuFooterColor}; + cursor: default; + `; + }, - rootSmall(t: Theme) { - return css` - ${menuFooterSizeMixin( - t.menuFooterPaddingXSmall, - t.menuFooterFontSizeSmall, - t.menuFooterLineHeightSmall, - t.menuFooterPaddingTopSmall, - t.menuFooterPaddingBottomSmall, - )}; - `; - }, - rootMedium(t: Theme) { - return css` - ${menuFooterSizeMixin( - t.menuFooterPaddingXMedium, - t.menuFooterFontSizeMedium, - t.menuFooterLineHeightMedium, - t.menuFooterPaddingTopMedium, - t.menuFooterPaddingBottomMedium, - )}; - `; - }, - rootLarge(t: Theme) { - return css` - ${menuFooterSizeMixin( - t.menuFooterPaddingXLarge, - t.menuFooterFontSizeLarge, - t.menuFooterLineHeightLarge, - t.menuFooterPaddingTopLarge, - t.menuFooterPaddingBottomLarge, - )}; - `; - }, + rootSmall(t: Theme) { + return emotion.css` + ${menuFooterSizeMixin(emotion)( + t.menuFooterPaddingXSmall, + t.menuFooterFontSizeSmall, + t.menuFooterLineHeightSmall, + t.menuFooterPaddingTopSmall, + t.menuFooterPaddingBottomSmall, + )}; + `; + }, + rootMedium(t: Theme) { + return emotion.css` + ${menuFooterSizeMixin(emotion)( + t.menuFooterPaddingXMedium, + t.menuFooterFontSizeMedium, + t.menuFooterLineHeightMedium, + t.menuFooterPaddingTopMedium, + t.menuFooterPaddingBottomMedium, + )}; + `; + }, + rootLarge(t: Theme) { + return emotion.css` + ${menuFooterSizeMixin(emotion)( + t.menuFooterPaddingXLarge, + t.menuFooterFontSizeLarge, + t.menuFooterLineHeightLarge, + t.menuFooterPaddingTopLarge, + t.menuFooterPaddingBottomLarge, + )}; + `; + }, - withLeftPaddingSmall(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconSmall)} - `; - }, - withLeftPaddingMedium(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconMedium)} - `; - }, - withLeftPaddingLarge(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconLarge)} - `; - }, -}); + withLeftPaddingSmall(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconSmall)} + `; + }, + withLeftPaddingMedium(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconMedium)} + `; + }, + withLeftPaddingLarge(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconLarge)} + `; + }, + }); diff --git a/packages/react-ui/components/MenuFooter/MenuFooter.tsx b/packages/react-ui/components/MenuFooter/MenuFooter.tsx index 6db2445ace3..36771fc6d35 100644 --- a/packages/react-ui/components/MenuFooter/MenuFooter.tsx +++ b/packages/react-ui/components/MenuFooter/MenuFooter.tsx @@ -1,11 +1,11 @@ import React, { HTMLAttributes, ReactNode, useContext } from 'react'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { SizeProp } from '../../lib/types/props'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './MenuFooter.styles'; +import { getStyles } from './MenuFooter.styles'; export interface MenuFooterProps extends CommonProps, Pick, 'id'> { _enableIconPadding?: boolean; @@ -27,6 +27,8 @@ export const MenuFooterDataTids = { */ function MenuFooter({ id, _enableIconPadding = false, children, size = 'small', ...rest }: MenuFooterProps) { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); function getRootSizeClassName() { switch (size) { @@ -56,7 +58,7 @@ function MenuFooter({ id, _enableIconPadding = false, children, size = 'small',
{ - const paddingRight = menuHeaderPaddingX; +export const menuHeaderSizeMixin = + (emotion: Emotion) => + ( + menuHeaderPaddingX: string, + menuHeaderFontSize: string, + menuHeaderLineHeight: string, + menuHeaderPaddingTop: string, + menuHeaderPaddingBottom: string, + ) => { + const paddingRight = menuHeaderPaddingX; - return css` + return emotion.css` font-size: ${menuHeaderFontSize}; line-height: ${menuHeaderLineHeight}; padding: ${menuHeaderPaddingTop} ${paddingRight} ${menuHeaderPaddingBottom} ${menuHeaderPaddingX}; `; -}; + }; -export const withIconSizeMixin = (menuItemPaddingForIcon: string) => { - return css` +export const withIconSizeMixin = (emotion: Emotion) => (menuItemPaddingForIcon: string) => { + return emotion.css` padding-left: ${menuItemPaddingForIcon}; `; }; diff --git a/packages/react-ui/components/MenuHeader/MenuHeader.styles.ts b/packages/react-ui/components/MenuHeader/MenuHeader.styles.ts index 778672b8da3..d7642d3389b 100644 --- a/packages/react-ui/components/MenuHeader/MenuHeader.styles.ts +++ b/packages/react-ui/components/MenuHeader/MenuHeader.styles.ts @@ -1,63 +1,66 @@ -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { menuHeaderSizeMixin, withIconSizeMixin } from './MenuHeader.mixins'; -export const styles = memoizeStyle({ - root(t: Theme) { - return css` - color: ${t.menuHeaderColor}; - cursor: default; - `; - }, +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root(t: Theme) { + return emotion.css` + color: ${t.menuHeaderColor}; + cursor: default; + `; + }, - rootSmall(t: Theme) { - return css` - ${menuHeaderSizeMixin( - t.menuHeaderPaddingXSmall, - t.menuHeaderFontSizeSmall, - t.menuHeaderLineHeightSmall, - t.menuHeaderPaddingTopSmall, - t.menuHeaderPaddingBottomSmall, - )}; - `; - }, - rootMedium(t: Theme) { - return css` - ${menuHeaderSizeMixin( - t.menuHeaderPaddingXMedium, - t.menuHeaderFontSizeMedium, - t.menuHeaderLineHeightMedium, - t.menuHeaderPaddingTopMedium, - t.menuHeaderPaddingBottomMedium, - )}; - `; - }, - rootLarge(t: Theme) { - return css` - ${menuHeaderSizeMixin( - t.menuHeaderPaddingXLarge, - t.menuHeaderFontSizeLarge, - t.menuHeaderLineHeightLarge, - t.menuHeaderPaddingTopLarge, - t.menuHeaderPaddingBottomLarge, - )}; - `; - }, + rootSmall(t: Theme) { + return emotion.css` + ${menuHeaderSizeMixin(emotion)( + t.menuHeaderPaddingXSmall, + t.menuHeaderFontSizeSmall, + t.menuHeaderLineHeightSmall, + t.menuHeaderPaddingTopSmall, + t.menuHeaderPaddingBottomSmall, + )}; + `; + }, + rootMedium(t: Theme) { + return emotion.css` + ${menuHeaderSizeMixin(emotion)( + t.menuHeaderPaddingXMedium, + t.menuHeaderFontSizeMedium, + t.menuHeaderLineHeightMedium, + t.menuHeaderPaddingTopMedium, + t.menuHeaderPaddingBottomMedium, + )}; + `; + }, + rootLarge(t: Theme) { + return emotion.css` + ${menuHeaderSizeMixin(emotion)( + t.menuHeaderPaddingXLarge, + t.menuHeaderFontSizeLarge, + t.menuHeaderLineHeightLarge, + t.menuHeaderPaddingTopLarge, + t.menuHeaderPaddingBottomLarge, + )}; + `; + }, - withLeftPaddingSmall(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconSmall)} - `; - }, - withLeftPaddingMedium(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconMedium)} - `; - }, - withLeftPaddingLarge(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconLarge)} - `; - }, -}); + withLeftPaddingSmall(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconSmall)} + `; + }, + withLeftPaddingMedium(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconMedium)} + `; + }, + withLeftPaddingLarge(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconLarge)} + `; + }, + }); diff --git a/packages/react-ui/components/MenuHeader/MenuHeader.tsx b/packages/react-ui/components/MenuHeader/MenuHeader.tsx index f871b264c43..cc25f52ab49 100644 --- a/packages/react-ui/components/MenuHeader/MenuHeader.tsx +++ b/packages/react-ui/components/MenuHeader/MenuHeader.tsx @@ -1,12 +1,12 @@ import React, { HTMLAttributes, ReactNode, useContext } from 'react'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; import { SizeProp } from '../../lib/types/props'; import { MenuContext } from '../../internal/Menu/MenuContext'; +import { EmotionContext } from '../../lib/theming/Emotion'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './MenuHeader.styles'; +import { getStyles } from './MenuHeader.styles'; export interface MenuHeaderProps extends CommonProps, Pick, 'id'> { _enableIconPadding?: boolean; @@ -28,6 +28,8 @@ export const MenuHeaderDataTids = { */ function MenuHeader({ id, _enableIconPadding = false, children, size = 'small', ...rest }: MenuHeaderProps) { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); const menuContext = useContext(MenuContext); function getRootSizeClassName() { @@ -58,7 +60,7 @@ function MenuHeader({ id, _enableIconPadding = false, children, size = 'small',
{ - const { paddingX, paddingY } = getMenuItemPaddings({ - menuItemPaddingX, - menuItemPaddingY, - }); +export const menuItemSizeMixin = + (emotion: Emotion) => + (menuItemPaddingX: string, menuItemPaddingY: string, menuItemLineHeight: string, menuItemFontSize: string) => { + const { paddingX, paddingY } = getMenuItemPaddings({ + menuItemPaddingX, + menuItemPaddingY, + }); - return css` - line-height: ${menuItemLineHeight}; - font-size: ${menuItemFontSize}; - padding: ${menuItemPaddingY} ${paddingX} ${paddingY} ${menuItemPaddingX}; - `; -}; + return emotion.css` + line-height: ${menuItemLineHeight}; + font-size: ${menuItemFontSize}; + padding: ${menuItemPaddingY} ${paddingX} ${paddingY} ${menuItemPaddingX}; + `; + }; -export const iconSizeMixin = (menuItemIconWidth: string, menuItemPaddingX: string) => { - return css` - width: ${menuItemIconWidth}; - left: ${parseInt(menuItemPaddingX)}px; - `; +export const iconSizeMixin = (emotion: Emotion) => (menuItemIconWidth: string, menuItemPaddingX: string) => { + return emotion.css` + width: ${menuItemIconWidth}; + left: ${parseInt(menuItemPaddingX)}px; + `; }; -export const withIconSizeMixin = (menuItemPaddingForIcon: string) => { - return css` +export const withIconSizeMixin = (emotion: Emotion) => (menuItemPaddingForIcon: string) => { + return emotion.css` padding-left: ${menuItemPaddingForIcon}; `; }; diff --git a/packages/react-ui/components/MenuItem/MenuItem.styles.ts b/packages/react-ui/components/MenuItem/MenuItem.styles.ts index e395fef588b..23a6dcd589a 100644 --- a/packages/react-ui/components/MenuItem/MenuItem.styles.ts +++ b/packages/react-ui/components/MenuItem/MenuItem.styles.ts @@ -1,5 +1,7 @@ +import type { Emotion } from '@emotion/css/create-instance'; + import { Theme } from '../../lib/theming/Theme'; -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import { memoizeStyle } from '../../lib/theming/Emotion'; import { resetButton } from '../../lib/styles/Mixins'; import { iconSizeMixin, menuItemSizeMixin, withIconSizeMixin } from './MenuItem.mixins'; @@ -14,150 +16,152 @@ export const getMenuItemPaddings = ({ return { paddingX, paddingY }; }; -export const styles = memoizeStyle({ - root(t: Theme) { - return css` - ${resetButton()}; +export const getStyles = (emotion: Emotion) => { + return memoizeStyle({ + root(t: Theme) { + return emotion.css` + ${resetButton(emotion)}; - cursor: pointer; - display: ${t.menuItemDisplay}; - position: relative; - text-decoration: none; - color: ${t.menuItemTextColor}; - border-radius: ${t.menuItemBorderRadius}; + cursor: pointer; + display: ${t.menuItemDisplay}; + position: relative; + text-decoration: none; + color: ${t.menuItemTextColor}; + border-radius: ${t.menuItemBorderRadius}; - button& { - min-width: 100%; - } + button& { + min-width: 100%; + } - &:nth-last-of-type(n + 2) { - margin-bottom: ${t.menuItemGap}; - } - `; - }, + &:nth-last-of-type(n + 2) { + margin-bottom: ${t.menuItemGap}; + } + `; + }, - rootSmall(t: Theme) { - return css` - ${menuItemSizeMixin( - t.menuItemPaddingXSmall, - t.menuItemPaddingYSmall, - t.menuItemLineHeightSmall, - t.menuItemFontSizeSmall, - )}; - `; - }, - rootMedium(t: Theme) { - return css` - ${menuItemSizeMixin( - t.menuItemPaddingXMedium, - t.menuItemPaddingYMedium, - t.menuItemLineHeightMedium, - t.menuItemFontSizeMedium, - )}; - `; - }, - rootLarge(t: Theme) { - return css` - ${menuItemSizeMixin( - t.menuItemPaddingXLarge, - t.menuItemPaddingYLarge, - t.menuItemLineHeightLarge, - t.menuItemFontSizeLarge, - )}; - `; - }, + rootSmall(t: Theme) { + return emotion.css` + ${menuItemSizeMixin(emotion)( + t.menuItemPaddingXSmall, + t.menuItemPaddingYSmall, + t.menuItemLineHeightSmall, + t.menuItemFontSizeSmall, + )}; + `; + }, + rootMedium(t: Theme) { + return emotion.css` + ${menuItemSizeMixin(emotion)( + t.menuItemPaddingXMedium, + t.menuItemPaddingYMedium, + t.menuItemLineHeightMedium, + t.menuItemFontSizeMedium, + )}; + `; + }, + rootLarge(t: Theme) { + return emotion.css` + ${menuItemSizeMixin(emotion)( + t.menuItemPaddingXLarge, + t.menuItemPaddingYLarge, + t.menuItemLineHeightLarge, + t.menuItemFontSizeLarge, + )}; + `; + }, - rootMobile(t: Theme) { - return css` - font-size: ${t.menuItemFontSizeMobile}; - line-height: ${t.menuItemLineHeightMobile}; - padding: ${t.menuItemPaddingMobile}; - `; - }, + rootMobile(t: Theme) { + return emotion.css` + font-size: ${t.menuItemFontSizeMobile}; + line-height: ${t.menuItemLineHeightMobile}; + padding: ${t.menuItemPaddingMobile}; + `; + }, - hover(t: Theme) { - // Color with !important in purpose to override `a:hover` - return css` - background: ${t.menuItemHoverBg} !important; - color: ${t.menuItemHoverColor} !important; - `; - }, - selected(t: Theme) { - return css` - background: ${t.menuItemSelectedBg} !important; - `; - }, - disabled(t: Theme) { - return css` - background: ${t.menuItemDisabledBg}; - color: ${t.menuItemDisabledColor}; - cursor: default; - `; - }, - link(t: Theme) { - return css` - color: ${t.menuItemLinkColor}; - `; - }, - loose() { - return css` - padding-left: 15px; - `; - }, - withIconSmall(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconSmall)} - `; - }, - withIconMedium(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconMedium)} - `; - }, - withIconLarge(t: Theme) { - return css` - ${withIconSizeMixin(t.menuItemPaddingForIconLarge)} - `; - }, - comment(t: Theme) { - return css` - color: ${t.menuItemCommentColor}; - opacity: ${t.menuItemCommentOpacity}; - white-space: normal; - padding-top: 4px; - `; - }, - commentHover(t: Theme) { - return css` - color: ${t.menuItemCommentColorHover}; - opacity: 0.6; - `; - }, - icon() { - return css` - display: inline-block; - position: absolute; - transform: translateY(0px); // icon shifts one pixel up in firefox on medium size without this property - `; - }, - iconSmall(t: Theme) { - return css` - ${iconSizeMixin(t.menuItemIconWidthSmall, t.menuItemPaddingXSmall)}; - `; - }, - iconMedium(t: Theme) { - return css` - ${iconSizeMixin(t.menuItemIconWidthMedium, t.menuItemPaddingXMedium)}; - `; - }, - iconLarge(t: Theme) { - return css` - ${iconSizeMixin(t.menuItemIconWidthLarge, t.menuItemPaddingXLarge)}; - `; - }, - mobileContentWithIcon() { - return css` - margin-left: 8px; - `; - }, -}); + hover(t: Theme) { + // Color with !important in purpose to override `a:hover` + return emotion.css` + background: ${t.menuItemHoverBg} !important; + color: ${t.menuItemHoverColor} !important; + `; + }, + selected(t: Theme) { + return emotion.css` + background: ${t.menuItemSelectedBg} !important; + `; + }, + disabled(t: Theme) { + return emotion.css` + background: ${t.menuItemDisabledBg}; + color: ${t.menuItemDisabledColor}; + cursor: default; + `; + }, + link(t: Theme) { + return emotion.css` + color: ${t.menuItemLinkColor}; + `; + }, + loose() { + return emotion.css` + padding-left: 15px; + `; + }, + withIconSmall(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconSmall)} + `; + }, + withIconMedium(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconMedium)} + `; + }, + withIconLarge(t: Theme) { + return emotion.css` + ${withIconSizeMixin(emotion)(t.menuItemPaddingForIconLarge)} + `; + }, + comment(t: Theme) { + return emotion.css` + color: ${t.menuItemCommentColor}; + opacity: ${t.menuItemCommentOpacity}; + white-space: normal; + padding-top: 4px; + `; + }, + commentHover(t: Theme) { + return emotion.css` + color: ${t.menuItemCommentColorHover}; + opacity: 0.6; + `; + }, + icon() { + return emotion.css` + display: inline-block; + position: absolute; + transform: translateY(0px); // icon shifts one pixel up in firefox on medium size without this property + `; + }, + iconSmall(t: Theme) { + return emotion.css` + ${iconSizeMixin(emotion)(t.menuItemIconWidthSmall, t.menuItemPaddingXSmall)}; + `; + }, + iconMedium(t: Theme) { + return emotion.css` + ${iconSizeMixin(emotion)(t.menuItemIconWidthMedium, t.menuItemPaddingXMedium)}; + `; + }, + iconLarge(t: Theme) { + return emotion.css` + ${iconSizeMixin(emotion)(t.menuItemIconWidthLarge, t.menuItemPaddingXLarge)}; + `; + }, + mobileContentWithIcon() { + return emotion.css` + margin-left: 8px; + `; + }, + }); +}; diff --git a/packages/react-ui/components/MenuItem/MenuItem.tsx b/packages/react-ui/components/MenuItem/MenuItem.tsx index 6ac580de8cf..e6152583e16 100644 --- a/packages/react-ui/components/MenuItem/MenuItem.tsx +++ b/packages/react-ui/components/MenuItem/MenuItem.tsx @@ -1,19 +1,20 @@ import React, { AriaAttributes, HTMLAttributes } from 'react'; import PropTypes from 'prop-types'; import { globalObject, isBrowser } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { Nullable } from '../../typings/utility-types'; import { isExternalLink, isFunction, isNonNullable, isReactUIComponent } from '../../lib/utils'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme } from '../../lib/theming/Theme'; import { CommonProps, CommonWrapper, CommonWrapperRestProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; import { rootNode, TSetRootNode } from '../../lib/rootNode'; import { SizeProp } from '../../lib/types/props'; import { MenuContext, MenuContextType } from '../../internal/Menu/MenuContext'; import { getVisualStateDataAttributes } from '../../internal/CommonWrapper/utils/getVisualStateDataAttributes'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './MenuItem.styles'; +import { getStyles } from './MenuItem.styles'; export type MenuItemState = null | 'hover' | 'selected' | void; @@ -144,6 +145,8 @@ export class MenuItem extends React.Component { }; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private mouseEntered = false; private setRootNode!: TSetRootNode; private rootRef: Nullable = null; @@ -154,23 +157,31 @@ export class MenuItem extends React.Component { public render() { return ( - - {(theme) => { - this.theme = theme; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); return ( - - {this.renderMain(this.props)} - + + {(theme) => { + this.theme = theme; + return ( + + {this.renderMain(this.props)} + + ); + }} + ); }} - + ); } @@ -240,6 +251,7 @@ export class MenuItem extends React.Component { }; private getRootSizeClassName() { + const styles = this.styles; switch (this.props.size) { case 'large': return styles.rootLarge(this.theme); @@ -252,6 +264,7 @@ export class MenuItem extends React.Component { } private getIconSizeClassName() { + const styles = this.styles; switch (this.props.size) { case 'large': return styles.iconLarge(this.theme); @@ -264,6 +277,7 @@ export class MenuItem extends React.Component { } private getWithIconSizeClassName() { + const styles = this.styles; switch (this.props.size) { case 'large': return styles.withIconLarge(this.theme); @@ -296,12 +310,13 @@ export class MenuItem extends React.Component { ...rest } = props; + const styles = this.styles; let iconElement = null; if (icon) { iconElement = (
{ ); } - const className = cx({ + const className = this.emotion.cx({ [styles.root(this.theme)]: true, [this.getRootSizeClassName()]: true, [styles.rootMobile(this.theme)]: isMobile, @@ -349,7 +364,7 @@ export class MenuItem extends React.Component { > {iconElement} { {this.props.comment && (
+ memoizeStyle({ + root(t: Theme) { + return emotion.css` + margin: ${t.menuSeparatorMarginY} ${t.menuSeparatorMarginX}; + border-top: ${t.menuSeparatorBorderWidth} solid ${t.menuSeparatorBorderColor}; + `; + }, - rootMobile(t: Theme) { - return css` - margin: ${t.mobileMenuSeparatorMarginY} ${t.mobileMenuSeparatorMarginX}; - border-radius: 1px; - `; - }, -}); + rootMobile(t: Theme) { + return emotion.css` + margin: ${t.mobileMenuSeparatorMarginY} ${t.mobileMenuSeparatorMarginX}; + border-radius: 1px; + `; + }, + }); diff --git a/packages/react-ui/components/MenuSeparator/MenuSeparator.tsx b/packages/react-ui/components/MenuSeparator/MenuSeparator.tsx index f44c97202fd..50f97a5582f 100644 --- a/packages/react-ui/components/MenuSeparator/MenuSeparator.tsx +++ b/packages/react-ui/components/MenuSeparator/MenuSeparator.tsx @@ -1,11 +1,11 @@ import React, { useContext } from 'react'; -import { cx } from '../../lib/theming/Emotion'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; import { ResponsiveLayout } from '../ResponsiveLayout'; +import { EmotionContext } from '../../lib/theming/Emotion'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './MenuSeparator.styles'; +import { getStyles } from './MenuSeparator.styles'; export type MenuSeparatorProps = CommonProps; @@ -19,6 +19,8 @@ export const MenuSeparatorDataTids = { */ function MenuSeparator(props: MenuSeparatorProps) { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); return ( @@ -27,7 +29,7 @@ function MenuSeparator(props: MenuSeparatorProps) { return (
); }} diff --git a/packages/react-ui/components/MiniModal/MiniModal.styles.ts b/packages/react-ui/components/MiniModal/MiniModal.styles.ts index 423e234e2a5..a22e0b5d4e1 100644 --- a/packages/react-ui/components/MiniModal/MiniModal.styles.ts +++ b/packages/react-ui/components/MiniModal/MiniModal.styles.ts @@ -1,88 +1,91 @@ -import { css, memoizeStyle } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { globalClasses as buttonGlobalClasses } from '../Button/Button.styles'; -export const styles = memoizeStyle({ - icon() { - return css` - text-align: center; - `; - }, +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + icon() { + return emotion.css` + text-align: center; + `; + }, - description() { - return css` - text-align: center; - `; - }, + description() { + return emotion.css` + text-align: center; + `; + }, - title() { - return css` - text-align: center; - `; - }, + title() { + return emotion.css` + text-align: center; + `; + }, - titleWithIcon(t: Theme) { - return css` - margin-top: ${t.miniModalTitleMarginTop}; - `; - }, + titleWithIcon(t: Theme) { + return emotion.css` + margin-top: ${t.miniModalTitleMarginTop}; + `; + }, - actions(t: Theme) { - return css` - display: flex; - justify-content: stretch; - text-align: center; - gap: ${t.miniModalActionGap}; + actions(t: Theme) { + return emotion.css` + display: flex; + justify-content: stretch; + text-align: center; + gap: ${t.miniModalActionGap}; - .${buttonGlobalClasses.root} { - width: 100%; - } - `; - }, + .${buttonGlobalClasses.root} { + width: 100%; + } + `; + }, - actionsIndent(t: Theme) { - return css` - height: ${t.miniModalCancelIndent}; - `; - }, + actionsIndent(t: Theme) { + return emotion.css` + height: ${t.miniModalCancelIndent}; + `; + }, - actionsIndentIE11Fallback(t: Theme) { - return css` - padding: ${t.miniModalCancelIndent} 0; - `; - }, + actionsIndentIE11Fallback(t: Theme) { + return emotion.css` + padding: ${t.miniModalCancelIndent} 0; + `; + }, - actionsRow() { - return css` - flex-direction: row; - `; - }, + actionsRow() { + return emotion.css` + flex-direction: row; + `; + }, - actionsColumn() { - return css` - flex-direction: column; - `; - }, + actionsColumn() { + return emotion.css` + flex-direction: column; + `; + }, - actionsRowIE11Fallback(t: Theme) { - return css` - > *:nth-of-type(1) { - margin-right: calc(${t.miniModalActionGap} / 2); - } - > *:nth-of-type(2) { - margin-left: calc(${t.miniModalActionGap} / 2); - } - `; - }, + actionsRowIE11Fallback(t: Theme) { + return emotion.css` + > *:nth-of-type(1) { + margin-right: calc(${t.miniModalActionGap} / 2); + } + > *:nth-of-type(2) { + margin-left: calc(${t.miniModalActionGap} / 2); + } + `; + }, - actionsColumnIE11Fallback(t: Theme) { - return css` - > *:nth-of-type(2) { - margin-top: ${t.miniModalActionGap}; - } - > *:nth-of-type(3) { - margin-top: ${t.miniModalActionGap}; - } - `; - }, -}); + actionsColumnIE11Fallback(t: Theme) { + return emotion.css` + > *:nth-of-type(2) { + margin-top: ${t.miniModalActionGap}; + } + > *:nth-of-type(3) { + margin-top: ${t.miniModalActionGap}; + } + `; + }, + }); diff --git a/packages/react-ui/components/MiniModal/MiniModal.tsx b/packages/react-ui/components/MiniModal/MiniModal.tsx index e0132aeab18..4d0096df7f8 100644 --- a/packages/react-ui/components/MiniModal/MiniModal.tsx +++ b/packages/react-ui/components/MiniModal/MiniModal.tsx @@ -1,8 +1,8 @@ import React, { useContext } from 'react'; import { Modal, ModalProps } from '../Modal'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { forwardRefAndName } from '../../lib/forwardRefAndName'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { getMiniModalTheme } from './getMiniModalTheme'; import { MiniModalFooter } from './MiniModalFooter'; diff --git a/packages/react-ui/components/MiniModal/MiniModalBody.tsx b/packages/react-ui/components/MiniModal/MiniModalBody.tsx index d6710653991..721e950ed1a 100644 --- a/packages/react-ui/components/MiniModal/MiniModalBody.tsx +++ b/packages/react-ui/components/MiniModal/MiniModalBody.tsx @@ -2,9 +2,10 @@ import React from 'react'; import { forwardRefAndName } from '../../lib/forwardRefAndName'; import { Modal, ModalBodyProps } from '../Modal'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { MiniModalDataTids } from './MiniModal'; -import { styles } from './MiniModal.styles'; +import { getStyles } from './MiniModal.styles'; /** * Обёртка над Modal.Body @@ -15,11 +16,18 @@ export const MiniModalBody = forwardRefAndName( 'MiniModalBody', ({ children, ...rest }, ref) => { return ( - -
- {children} -
-
+ + {(emotion) => { + const styles = getStyles(emotion); + return ( + +
+ {children} +
+
+ ); + }} +
); }, ); diff --git a/packages/react-ui/components/MiniModal/MiniModalFooter.tsx b/packages/react-ui/components/MiniModal/MiniModalFooter.tsx index 004c4266806..f82dd4c6617 100644 --- a/packages/react-ui/components/MiniModal/MiniModalFooter.tsx +++ b/packages/react-ui/components/MiniModal/MiniModalFooter.tsx @@ -2,11 +2,11 @@ import React, { useContext } from 'react'; import { Modal, ModalFooterProps } from '../Modal'; import { forwardRefAndName } from '../../lib/forwardRefAndName'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { isIE11 } from '../../lib/client'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionContext } from '../../lib/theming/Emotion'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './MiniModal.styles'; +import { getStyles } from './MiniModal.styles'; import { MiniModalDataTids } from './MiniModal'; interface MiniModalFooterProps extends ModalFooterProps { @@ -29,6 +29,8 @@ export const MiniModalFooter = forwardRefAndName { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); const childrenCount = React.Children.count(children); const _direction = childrenCount > 2 || childrenCount === 1 ? 'column' : direction; @@ -36,7 +38,7 @@ export const MiniModalFooter = forwardRefAndName, children, ...rest }, ref) => { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); return ( @@ -39,7 +41,7 @@ export const MiniModalHeader = forwardRefAndName diff --git a/packages/react-ui/components/MiniModal/MiniModalIndent.tsx b/packages/react-ui/components/MiniModal/MiniModalIndent.tsx index 688b0c39c97..a77faa51198 100644 --- a/packages/react-ui/components/MiniModal/MiniModalIndent.tsx +++ b/packages/react-ui/components/MiniModal/MiniModalIndent.tsx @@ -1,11 +1,11 @@ import React, { useContext } from 'react'; import { forwardRefAndName } from '../../lib/forwardRefAndName'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { cx } from '../../lib/theming/Emotion'; +import { EmotionContext } from '../../lib/theming/Emotion'; import { isIE11 } from '../../lib/client'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; -import { styles } from './MiniModal.styles'; +import { getStyles } from './MiniModal.styles'; import { MiniModalDataTids } from './MiniModal'; /** @@ -17,12 +17,14 @@ export const MiniModalIndent = forwardRefAndName { const theme = useContext(ThemeContext); + const emotion = useContext(EmotionContext); + const styles = getStyles(emotion); return (
); diff --git a/packages/react-ui/components/Modal/Modal.styles.ts b/packages/react-ui/components/Modal/Modal.styles.ts index 35a08426b1f..635abed26f8 100644 --- a/packages/react-ui/components/Modal/Modal.styles.ts +++ b/packages/react-ui/components/Modal/Modal.styles.ts @@ -1,4 +1,6 @@ -import { css, memoizeStyle, prefix } from '../../lib/theming/Emotion'; +import type { Emotion } from '@emotion/css/create-instance'; + +import { memoizeStyle, prefix } from '../../lib/theming/Emotion'; import { Theme } from '../../lib/theming/Theme'; import { resetButton } from '../../lib/styles/Mixins'; @@ -7,19 +9,20 @@ export const modalGlobalClasses = prefix('modal')({ container: 'container', }); -export const styles = memoizeStyle({ - root() { - return css` +export const getStyles = (emotion: Emotion) => + memoizeStyle({ + root() { + return emotion.css` height: 100%; left: 0; position: fixed; top: 0; width: 100%; `; - }, + }, - bg(t: Theme) { - return css` + bg(t: Theme) { + return emotion.css` bottom: 0; left: 0; position: absolute; @@ -28,10 +31,10 @@ export const styles = memoizeStyle({ background: ${t.modalBackBg}; opacity: ${t.modalBackOpacity}; `; - }, + }, - container() { - return css` + container() { + return emotion.css` position: relative; white-space: nowrap; text-align: center; @@ -46,20 +49,20 @@ export const styles = memoizeStyle({ height: 80%; /* to vertical align modal 40%/60% of screen height */ } `; - }, + }, - containerMobile(t: Theme) { - return css` + containerMobile(t: Theme) { + return emotion.css` height: ${t.mobileModalContainerHeight}; margin-top: ${t.mobileModalContainerMarginTop}; margin-right: ${t.mobileModalContainerMarginRight}; margin-bottom: ${t.mobileModalContainerMarginBottom}; margin-left: ${t.mobileModalContainerMarginLeft}; `; - }, + }, - window(t: Theme) { - return css` + window(t: Theme) { + return emotion.css` position: relative; white-space: normal; margin: auto; @@ -67,18 +70,18 @@ export const styles = memoizeStyle({ background: ${t.modalBg}; border-radius: ${t.modalBorderRadius}; `; - }, + }, - mobileWindow() { - return css` + mobileWindow() { + return emotion.css` width: 100%; height: 100%; overflow: auto; `; - }, + }, - centerContainer() { - return css` + centerContainer() { + return emotion.css` position: relative; display: inline-block; text-align: left; @@ -87,26 +90,26 @@ export const styles = memoizeStyle({ z-index: 10; margin: 40px 20px; `; - }, + }, - mobileCenterContainer() { - return css` + mobileCenterContainer() { + return emotion.css` margin: 0; width: 100%; height: 100%; `; - }, + }, - alignTop() { - return css` + alignTop() { + return emotion.css` vertical-align: top; `; - }, + }, - close(t: Theme) { - const padding = parseInt(t.modalCloseButtonPadding); - return css` - ${resetButton()}; + close(t: Theme) { + const padding = parseInt(t.modalCloseButtonPadding); + return emotion.css` + ${resetButton(emotion)}; position: absolute; display: flex; right: ${padding}px; @@ -130,10 +133,10 @@ export const styles = memoizeStyle({ box-sizing: content-box; } `; - }, + }, - mobileClose(t: Theme) { - return css` + mobileClose(t: Theme) { + return emotion.css` right: ${t.mobileModalCloseButtonRightPadding}; top: ${parseInt(t.mobileModalCloseButtonTopPadding) + parseInt(t.mobileModalHeaderPadding)}px; padding: ${t.mobileModalCloseButtonClickArea}; @@ -144,32 +147,32 @@ export const styles = memoizeStyle({ height: ${t.mobileModalCloseIconSize}; } `; - }, + }, - mobileCloseWithoutHeader() { - return css` + mobileCloseWithoutHeader() { + return emotion.css` position: static; `; - }, + }, - closeWrapper(t: Theme) { - const padding = parseInt(t.modalCloseButtonPadding); - const paddingBottom = parseInt(t.modalCloseButtonBottomPadding); + closeWrapper(t: Theme) { + const padding = parseInt(t.modalCloseButtonPadding); + const paddingBottom = parseInt(t.modalCloseButtonBottomPadding); - const blockSizeX = parseInt(t.modalCloseIconSize) + 2 * padding; - const blockSizeY = parseInt(t.modalCloseIconSize) + padding + paddingBottom; - return css` + const blockSizeX = parseInt(t.modalCloseIconSize) + 2 * padding; + const blockSizeY = parseInt(t.modalCloseIconSize) + padding + paddingBottom; + return emotion.css` position: relative; float: right; width: ${blockSizeX}px; height: ${blockSizeY}px; `; - }, + }, - mobileCloseWrapper(t: Theme) { - const size = parseInt(t.mobileModalCloseIconSize) + parseInt(t.mobileModalCloseButtonClickArea) * 2; + mobileCloseWrapper(t: Theme) { + const size = parseInt(t.mobileModalCloseIconSize) + parseInt(t.mobileModalCloseButtonClickArea) * 2; - return css` + return emotion.css` position: absolute; right: ${t.mobileModalCloseButtonRightPadding}; top: ${t.mobileModalCloseButtonTopPadding}; @@ -183,25 +186,25 @@ export const styles = memoizeStyle({ background: radial-gradient(50% 50% at 50% 50%, ${t.bgDefault} 60%, rgba(255, 255, 255, 0) 100%); border-radius: ${size}px; `; - }, + }, - disabled(t: Theme) { - return css` + disabled(t: Theme) { + return emotion.css` pointer-events: none; cursor: default; color: ${t.modalCloseButtonDisabledColor}; `; - }, + }, - focus(t: Theme) { - return css` + focus(t: Theme) { + return emotion.css` color: ${t.modalCloseButtonHoverColor}; outline: 2px solid ${t.borderColorFocus}; `; - }, + }, - header(t: Theme) { - return css` + header(t: Theme) { + return emotion.css` font-size: ${t.modalHeaderFontSize}; line-height: ${t.modalHeaderLineHeight}; padding: ${t.modalHeaderPaddingTop} ${t.modalPaddingRight} ${t.modalHeaderPaddingBottom} ${t.modalPaddingLeft}; @@ -210,19 +213,19 @@ export const styles = memoizeStyle({ color: ${t.modalHeaderTextColor}; font-weight: ${t.modalHeaderFontWeight}; `; - }, + }, - mobileHeader(t: Theme) { - return css` + mobileHeader(t: Theme) { + return emotion.css` position: relative; font-size: ${t.mobileModalHeaderFontSize}; line-height: ${t.mobileModalHeaderLineHeight}; padding: ${t.mobileModalHeaderPadding}; `; - }, + }, - body(t: Theme) { - return css` + body(t: Theme) { + return emotion.css` border-radius: ${t.modalBodyBorderRadius}; padding-top: ${t.modalBodyPaddingTop}; padding-right: ${t.modalPaddingRight}; @@ -230,56 +233,56 @@ export const styles = memoizeStyle({ padding-left: ${t.modalPaddingLeft}; color: ${t.modalBodyTextColor}; `; - }, + }, - mobileBody(t: Theme) { - return css` + mobileBody(t: Theme) { + return emotion.css` padding: ${t.mobileModalBodyPadding}; display: flex; flex-flow: column; flex: 1; font-size: ${t.mobileModalBodyFontSize}; `; - }, + }, - headerWithClose(t: Theme) { - const rightPadding = 2 * parseInt(t.modalCloseButtonPadding) + parseInt(t.modalCloseIconSize); + headerWithClose(t: Theme) { + const rightPadding = 2 * parseInt(t.modalCloseButtonPadding) + parseInt(t.modalCloseIconSize); - return css` + return emotion.css` padding-right: ${rightPadding}px; `; - }, + }, - mobileHeaderWithClose(t: Theme) { - return css` + mobileHeaderWithClose(t: Theme) { + return emotion.css` padding-right: ${2 * parseInt(t.mobileModalCloseButtonRightPadding) + parseInt(t.mobileModalCloseIconSize)}px; `; - }, + }, - footer(t: Theme) { - return css` + footer(t: Theme) { + return emotion.css` padding: ${t.modalFooterPaddingTop} ${t.modalPaddingRight} ${t.modalFooterPaddingBottom} ${t.modalPaddingLeft}; color: ${t.modalFooterTextColor}; border-radius: 0 0 ${t.modalBorderRadius} ${t.modalBorderRadius}; `; - }, + }, - mobileFooter(t: Theme) { - return css` + mobileFooter(t: Theme) { + return emotion.css` padding: ${t.mobileModalFooterPadding}; `; - }, + }, - panel(t: Theme) { - return css` + panel(t: Theme) { + return emotion.css` padding-top: ${t.modalFooterPanelPaddingTop}; padding-bottom: ${t.modalFooterPanelPaddingBottom}; background: ${t.modalFooterBg}; `; - }, + }, - fixedHeader(t: Theme) { - return css` + fixedHeader(t: Theme) { + return emotion.css` margin-bottom: ${t.modalFixedHeaderMarginBottom}; padding-bottom: ${t.modalFixedHeaderPaddingBottom}; background: ${t.modalFixedHeaderBg}; @@ -296,16 +299,16 @@ export const styles = memoizeStyle({ box-shadow: ${t.modalFixedHeaderShadow}; } `; - }, + }, - mobileFixedHeader(t: Theme) { - return css` + mobileFixedHeader(t: Theme) { + return emotion.css` padding-bottom: ${t.mobileModalHeaderPadding}; `; - }, + }, - fixedFooter(t: Theme) { - return css` + fixedFooter(t: Theme) { + return emotion.css` padding-top: ${t.modalFixedFooterPaddingTop}; margin-top: ${t.modalFixedFooterMarginTop}; background: ${t.modalFixedHeaderBg}; @@ -322,79 +325,79 @@ export const styles = memoizeStyle({ box-shadow: ${t.modalFixedFooterShadow}; } `; - }, + }, - fixedPanel(t: Theme) { - return css` + fixedPanel(t: Theme) { + return emotion.css` &:before { box-shadow: ${t.modalFixedPanelShadow}; } `; - }, + }, - headerAddPadding(t: Theme) { - return css` + headerAddPadding(t: Theme) { + return emotion.css` padding-bottom: ${t.modalHeaderAdditionalPaddingBottom}; `; - }, + }, - bodyWithoutHeader(t: Theme) { - return css` + bodyWithoutHeader(t: Theme) { + return emotion.css` padding-top: ${t.modalPaddingTop}; `; - }, + }, - mobileBodyWithoutHeader() { - return css` + mobileBodyWithoutHeader() { + return emotion.css` padding-top: 0px; `; - }, + }, - bodyWithoutPadding() { - return css` + bodyWithoutPadding() { + return emotion.css` padding: 0; `; - }, + }, - bodyAddPaddingForPanel(t: Theme) { - return css` + bodyAddPaddingForPanel(t: Theme) { + return emotion.css` padding-bottom: ${t.modalPaddingBottom}; `; - }, + }, - mobileBodyAddPaddingForPanel(t: Theme) { - return css` + mobileBodyAddPaddingForPanel(t: Theme) { + return emotion.css` padding: ${t.mobileModalBodyPadding}; `; - }, + }, - columnFlexContainer() { - return css` + columnFlexContainer() { + return emotion.css` height: 100%; display: flex; flex-flow: column; overflow-y: auto; `; - }, + }, - modalSeparatorWrapper() { - return css` + modalSeparatorWrapper() { + return emotion.css` position: absolute; width: 100%; `; - }, + }, - modalSeparator(t: Theme) { - return css` + modalSeparator(t: Theme) { + return emotion.css` border-bottom: ${t.modalSeparatorBorderBottom}; margin: 0 32px; transition: margin 0.3s; `; - }, + }, - modalSeparatorFixed() { - return css` + modalSeparatorFixed() { + return emotion.css` margin: 0 16px; `; - }, -}); + }, + }); diff --git a/packages/react-ui/components/Modal/Modal.tsx b/packages/react-ui/components/Modal/Modal.tsx index fe3f2755160..f7b1da6f54d 100644 --- a/packages/react-ui/components/Modal/Modal.tsx +++ b/packages/react-ui/components/Modal/Modal.tsx @@ -2,6 +2,7 @@ import React, { AriaAttributes, HTMLAttributes } from 'react'; import FocusLock from 'react-focus-lock'; import throttle from 'lodash.throttle'; import { globalObject } from '@skbkontur/global-object'; +import type { Emotion } from '@emotion/css/create-instance'; import { isNonNullable } from '../../lib/utils'; import { isKeyEscape } from '../../lib/events/keyboard/identifiers'; @@ -12,20 +13,20 @@ import { stopPropagation } from '../../lib/events/stopPropagation'; import { HideBodyVerticalScroll } from '../../internal/HideBodyVerticalScroll'; import { ModalStack, ModalStackSubscription } from '../../lib/ModalStack'; import { ResizeDetector } from '../../internal/ResizeDetector'; -import { ThemeContext } from '../../lib/theming/ThemeContext'; import { Theme, ThemeIn } from '../../lib/theming/Theme'; import { isIE11 } from '../../lib/client'; -import { CommonWrapper, CommonProps } from '../../internal/CommonWrapper'; -import { cx } from '../../lib/theming/Emotion'; +import { CommonProps, CommonWrapper } from '../../internal/CommonWrapper'; +import { EmotionConsumer } from '../../lib/theming/Emotion'; import { createPropsGetter } from '../../lib/createPropsGetter'; import { ResponsiveLayout } from '../ResponsiveLayout'; +import { ThemeContext } from '../../lib/theming/ThemeContext'; import { ModalContext, ModalContextProps } from './ModalContext'; import { ModalFooter } from './ModalFooter'; import { ModalHeader } from './ModalHeader'; import { ModalBody } from './ModalBody'; import { ModalClose } from './ModalClose'; -import { styles } from './Modal.styles'; +import { getStyles } from './Modal.styles'; import { getModalTheme } from './getModalTheme'; let mountedModalsCount = 0; @@ -131,6 +132,8 @@ export class Modal extends React.Component { }; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private stackSubscription: ModalStackSubscription | null = null; private containerNode: HTMLDivElement | null = null; private mouseDownTarget: EventTarget | null = null; @@ -171,12 +174,20 @@ export class Modal extends React.Component { public render(): JSX.Element { return ( - - {(theme) => { - this.theme = getModalTheme(theme, this.props.theme); - return {this.renderMain()}; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( + + {(theme) => { + this.theme = getModalTheme(theme, this.props.theme); + return {this.renderMain()}; + }} + + ); }} - + ); } @@ -222,6 +233,8 @@ export class Modal extends React.Component { containerStyle.width = 'auto'; } + const styles = this.styles; + return ( @@ -240,7 +253,7 @@ export class Modal extends React.Component {
{ aria-modal aria-label={ariaLabel} role={role} - className={cx({ + className={this.emotion.cx({ [styles.centerContainer()]: true, [styles.mobileCenterContainer()]: isMobile, [styles.alignTop()]: Boolean(alignTop), @@ -259,24 +272,30 @@ export class Modal extends React.Component { data-tid={ModalDataTids.content} >
{!hasHeader && !noClose && ( { public static __MODAL_BODY__ = true; private theme!: Theme; + private emotion!: Emotion; + private styles!: ReturnType; private isMobileLayout!: boolean; private setRootNode!: TSetRootNode; public render() { return ( - - {(theme) => { - this.theme = getModalBodyTheme(theme); - return {this.renderMain()}; + + {(emotion) => { + this.emotion = emotion; + this.styles = getStyles(this.emotion); + return ( + + {(theme) => { + this.theme = getModalBodyTheme(theme); + return {this.renderMain()}; + }} + + ); }} - + ); } @@ -54,12 +65,14 @@ export class ModalBody extends React.Component { public renderMain(): JSX.Element { const { noPadding } = this.props; + const styles = this.styles; + return ( {({ additionalPadding, hasHeader }) => ( ; + const styles = getStyles(emotion); return ( @@ -41,7 +43,7 @@ export function ModalClose({ disableClose, requestClose, ...otherProps }: CloseP {({ isMobile }) => (