diff --git a/packages/devui/.storybook/preview.tsx b/packages/devui/.storybook/preview.tsx index 48e5eabc..47b89177 100644 --- a/packages/devui/.storybook/preview.tsx +++ b/packages/devui/.storybook/preview.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Container } from '../src'; import { listSchemes, listThemes } from '../src/utils/theme'; -import '../src/presets.js'; +import '../src/presets'; export const parameters = { actions: { argTypesRegex: '^on[A-Z].*' }, diff --git a/packages/devui/.storybook/tsconfig.json b/packages/devui/.storybook/tsconfig.json index 2053741a..ca19def4 100644 --- a/packages/devui/.storybook/tsconfig.json +++ b/packages/devui/.storybook/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../../tsconfig.react.base.json", - "include": ["../src", "./"] + "include": ["../src", "."] } diff --git a/packages/devui/package.json b/packages/devui/package.json index 02b59e0b..68b65959 100755 --- a/packages/devui/package.json +++ b/packages/devui/package.json @@ -39,6 +39,7 @@ "base16": "^1.0.0", "codemirror": "^5.56.0", "color": "^3.1.2", + "hoist-non-react-statics": "^3.3.2", "prop-types": "^15.7.2", "react-icons": "^3.10.0", "react-jsonschema-form": "^1.8.1", diff --git a/packages/devui/src/Button/Button.stories.tsx b/packages/devui/src/Button/Button.stories.tsx index bed792b4..3ac47574 100644 --- a/packages/devui/src/Button/Button.stories.tsx +++ b/packages/devui/src/Button/Button.stories.tsx @@ -27,6 +27,7 @@ const Template: Story = (args) => ( export const Default = Template.bind({}); Default.args = { title: 'Hello Tooltip! \\a And from new line hello!', + tooltipPosition: 'top', primary: true, size: 'normal', disabled: false, @@ -43,6 +44,7 @@ export const Mark = Template.bind({}); Mark.args = { mark: 'base08', title: 'Hello Tooltip', + tooltipPosition: 'top', size: 'normal', disabled: false, children: , diff --git a/packages/devui/src/Editor/Editor.stories.tsx b/packages/devui/src/Editor/Editor.stories.tsx index d5c682ba..b4ff1878 100644 --- a/packages/devui/src/Editor/Editor.stories.tsx +++ b/packages/devui/src/Editor/Editor.stories.tsx @@ -42,7 +42,7 @@ const WithTabsTemplate: Story = (args) => ( export const WithTabs = WithTabsTemplate.bind({}); WithTabs.args = { lineNumbers: true, - align: 'left', + position: 'left', }; WithTabs.argTypes = { value: { control: { disable: true } }, diff --git a/packages/devui/src/Editor/WithTabs.tsx b/packages/devui/src/Editor/WithTabs.tsx index 3454e904..60383488 100644 --- a/packages/devui/src/Editor/WithTabs.tsx +++ b/packages/devui/src/Editor/WithTabs.tsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { Component, ComponentType } from 'react'; import Editor from './'; import Tabs from '../Tabs'; @@ -11,7 +11,7 @@ const value2 = ` `; export interface WithTabsProps { - align: 'left' | 'right' | 'center'; + position: 'left' | 'right' | 'center'; lineNumbers: boolean; } @@ -27,18 +27,18 @@ export default class WithTabs extends Component { }; render() { - const { align, lineNumbers } = this.props; + const { position, lineNumbers } = this.props; return ( - tabs={[ { name: 'Function 1', - component: Editor, + component: (Editor as unknown) as ComponentType, selector: () => ({ value: value1, lineNumbers }), }, { name: 'Function 2', - component: Editor, + component: (Editor as unknown) as ComponentType, selector: () => ({ value: value2, lineNumbers }), }, ]} @@ -46,7 +46,7 @@ export default class WithTabs extends Component { onClick={(selected) => { this.setState({ selected }); }} - align={align} + position={position} /> ); } diff --git a/packages/devui/src/Form/widgets.tsx b/packages/devui/src/Form/widgets.tsx index 12accf00..905148af 100644 --- a/packages/devui/src/Form/widgets.tsx +++ b/packages/devui/src/Form/widgets.tsx @@ -1,32 +1,30 @@ import React from 'react'; -import { Widget } from 'react-jsonschema-form'; +import { FieldProps, Widget, WidgetProps } from 'react-jsonschema-form'; import Select from '../Select'; import Slider from '../Slider'; /* eslint-disable react/prop-types */ -const SelectWidget: Widget = ({ options, multi, ...rest }) => ( - ); -const RangeWidget: Widget = ({ +const RangeWidget: Widget = (({ schema, - readonly, - autofocus, + disabled, label, // eslint-disable-line options, // eslint-disable-line formContext, // eslint-disable-line registry, // eslint-disable-line ...rest -}) => ( - -); +}: WidgetProps & { registry: FieldProps['registry'] }) => + ( + + ) as unknown) as Widget; export default { SelectWidget, RangeWidget }; diff --git a/packages/devui/src/Select/Select.tsx b/packages/devui/src/Select/Select.tsx index c483451b..75324b63 100644 --- a/packages/devui/src/Select/Select.tsx +++ b/packages/devui/src/Select/Select.tsx @@ -11,7 +11,7 @@ export interface SelectProps extends Omit { /** * Wrapper around [React Select](https://github.com/JedWatson/react-select). */ -class Select extends (PureComponent || Component) { +export class Select extends (PureComponent || Component) { render() { return ( ( +const Template: Story> = (args) => ( @@ -35,7 +37,9 @@ Default.argTypes = { onClick: { control: { disable: true } }, }; -export const WithContent = Template.bind({}); +export const WithContent = (Template as Story< + TabsProps<{ selected: string }> +>).bind({}); WithContent.args = { tabs, selected: 'Tab2', diff --git a/packages/devui/src/Tabs/Tabs.tsx b/packages/devui/src/Tabs/Tabs.tsx index bd8be97a..38649751 100644 --- a/packages/devui/src/Tabs/Tabs.tsx +++ b/packages/devui/src/Tabs/Tabs.tsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import TabsHeader from './TabsHeader'; +import TabsHeader, { ReactButtonElement, Tab } from './TabsHeader'; import { TabsContainer } from './styles/common'; export type Position = 'left' | 'right' | 'center'; @@ -96,15 +96,15 @@ export default class Tabs

extends Component> { ); } + + static propTypes = { + tabs: PropTypes.array.isRequired, + selected: PropTypes.string, + main: PropTypes.bool, + onClick: PropTypes.func.isRequired, + collapsible: PropTypes.bool, + position: PropTypes.oneOf(['left', 'right', 'center']), + }; + + static defaultProps = { position: 'left' }; } - -Tabs.propTypes = { - tabs: PropTypes.array.isRequired, - selected: PropTypes.string, - main: PropTypes.bool, - onClick: PropTypes.func.isRequired, - collapsible: PropTypes.bool, - position: PropTypes.oneOf(['left', 'right', 'center']), -}; - -Tabs.defaultProps = { position: 'left' }; diff --git a/packages/devui/src/Tabs/TabsHeader.tsx b/packages/devui/src/Tabs/TabsHeader.tsx index 74db89cc..e5d7e4a0 100644 --- a/packages/devui/src/Tabs/TabsHeader.tsx +++ b/packages/devui/src/Tabs/TabsHeader.tsx @@ -22,7 +22,7 @@ export interface Tab

{ interface Props

{ tabs: ReactButtonElement[]; - items: Tab

; + items: Tab

[]; main: boolean | undefined; onClick: (value: string) => void; position: 'left' | 'right' | 'center'; @@ -38,17 +38,18 @@ interface State { } export default class TabsHeader

extends Component, State> { - constructor(props: Props

) { - super(props); - this.state = { - visibleTabs: props.tabs.slice(), - hiddenTabs: [], - subMenuOpened: false, - contextMenu: undefined, - }; - this.iconWidth = 0; - this.hiddenTabsWidth = []; - } + state: State = { + visibleTabs: this.props.tabs.slice(), + hiddenTabs: [], + subMenuOpened: false, + contextMenu: undefined, + }; + + iconWidth = 0; + hiddenTabsWidth: number[] = []; + tabsWrapperRef?: HTMLDivElement | null; + tabsRef?: HTMLDivElement | null; + resizeDetector?: HTMLIFrameElement; UNSAFE_componentWillReceiveProps(nextProps: Props

) { if ( @@ -76,8 +77,11 @@ export default class TabsHeader

extends Component, State> { let shouldCollapse = false; if (this.iconWidth === 0) { - const tabButtons = this.tabsRef.children; - if (this.tabsRef.children[tabButtons.length - 1].value === 'expandIcon') { + const tabButtons = this.tabsRef!.children; + if ( + (this.tabsRef!.children[tabButtons.length - 1] as HTMLButtonElement) + .value === 'expandIcon' + ) { this.iconWidth = tabButtons[ tabButtons.length - 1 ].getBoundingClientRect().width; @@ -104,12 +108,12 @@ export default class TabsHeader

extends Component, State> { } enableResizeEvents() { - this.resizeDetector = observeResize(this.tabsWrapperRef, this.collapse); + this.resizeDetector = observeResize(this.tabsWrapperRef!, this.collapse); window.addEventListener('mousedown', this.hideSubmenu); } disableResizeEvents() { - this.resizeDetector.remove(); + this.resizeDetector!.remove(); window.removeEventListener('mousedown', this.hideSubmenu); } @@ -119,13 +123,13 @@ export default class TabsHeader

extends Component, State> { const { selected, tabs } = this.props; const tabsWrapperRef = this.tabsWrapperRef; const tabsRef = this.tabsRef; - const tabButtons = this.tabsRef.children; + const tabButtons = this.tabsRef!.children; const visibleTabs = this.state.visibleTabs; const hiddenTabs = this.state.hiddenTabs; - let tabsWrapperRight = tabsWrapperRef.getBoundingClientRect().right; + let tabsWrapperRight = tabsWrapperRef!.getBoundingClientRect().right; if (!tabsWrapperRight) return; // tabs are hidden - const tabsRefRight = tabsRef.getBoundingClientRect().right; + const tabsRefRight = tabsRef!.getBoundingClientRect().right; let i = visibleTabs.length - 1; let hiddenTab; @@ -133,16 +137,16 @@ export default class TabsHeader

extends Component, State> { if ( this.props.position === 'right' && hiddenTabs.length > 0 && - tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] < - tabsWrapperRef.getBoundingClientRect().width + tabsRef!.getBoundingClientRect().width + this.hiddenTabsWidth[0] < + tabsWrapperRef!.getBoundingClientRect().width ) { while ( i < tabs.length - 1 && - tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] < - tabsWrapperRef.getBoundingClientRect().width + tabsRef!.getBoundingClientRect().width + this.hiddenTabsWidth[0] < + tabsWrapperRef!.getBoundingClientRect().width ) { hiddenTab = hiddenTabs.shift(); - visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab); + visibleTabs.splice(Number(hiddenTab!.key), 0, hiddenTab!); i++; } } else { @@ -152,7 +156,7 @@ export default class TabsHeader

extends Component, State> { tabButtons[i].getBoundingClientRect().right >= tabsWrapperRight - this.iconWidth ) { - if (tabButtons[i].value !== selected) { + if ((tabButtons[i] as HTMLButtonElement).value !== selected) { hiddenTabs.unshift(...visibleTabs.splice(i, 1)); this.hiddenTabsWidth.unshift( tabButtons[i].getBoundingClientRect().width @@ -171,7 +175,7 @@ export default class TabsHeader

extends Component, State> { tabsWrapperRight - this.iconWidth ) { hiddenTab = hiddenTabs.shift(); - visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab); + visibleTabs.splice(Number(hiddenTab!.key), 0, hiddenTab!); this.hiddenTabsWidth.shift(); i++; } @@ -183,15 +187,15 @@ export default class TabsHeader

extends Component, State> { this.setState({ subMenuOpened: false, contextMenu: undefined }); }; - getTabsWrapperRef = (node) => { + getTabsWrapperRef: React.RefCallback = (node) => { this.tabsWrapperRef = node; }; - getTabsRef = (node) => { + getTabsRef: React.RefCallback = (node) => { this.tabsRef = node; }; - expandMenu = (e) => { + expandMenu: React.MouseEventHandler = (e) => { const rect = e.currentTarget.children[0].getBoundingClientRect(); this.setState({ contextMenu: { @@ -231,14 +235,14 @@ export default class TabsHeader

extends Component, State> { ); } -} -TabsHeader.propTypes = { - tabs: PropTypes.array.isRequired, - items: PropTypes.array.isRequired, - main: PropTypes.bool, - onClick: PropTypes.func, - position: PropTypes.string, - collapsible: PropTypes.bool, - selected: PropTypes.string, -}; + static propTypes = { + tabs: PropTypes.array.isRequired, + items: PropTypes.array.isRequired, + main: PropTypes.bool, + onClick: PropTypes.func, + position: PropTypes.string, + collapsible: PropTypes.bool, + selected: PropTypes.string, + }; +} diff --git a/packages/devui/src/Tabs/data.tsx b/packages/devui/src/Tabs/data.tsx index 0d27887e..b3e27238 100644 --- a/packages/devui/src/Tabs/data.tsx +++ b/packages/devui/src/Tabs/data.tsx @@ -1,7 +1,7 @@ import React from 'react'; /* eslint-disable react/prop-types */ -const Component = ({ selected }) => ( +const Component = ({ selected }: { selected: string }) => (

( ); /* eslint-enable react/prop-types */ -const selector = (tab) => ({ selected: tab.name }); +const selector = (tab: { name: string; value?: string }) => ({ + selected: tab.name, +}); export const tabs = [ { @@ -37,6 +39,6 @@ export const tabs = [ }, ]; -export const simple10Tabs = []; +export const simple10Tabs: { name: string; value: string }[] = []; for (let i = 1; i <= 10; i++) simple10Tabs.push({ name: `Tab${i}`, value: `${i}` }); diff --git a/packages/devui/src/Tabs/styles/common.ts b/packages/devui/src/Tabs/styles/common.ts index 9ecd8dd8..d85259b7 100644 --- a/packages/devui/src/Tabs/styles/common.ts +++ b/packages/devui/src/Tabs/styles/common.ts @@ -1,6 +1,11 @@ import styled from 'styled-components'; +import { Position } from '../Tabs'; -export const TabsContainer = styled.div` +interface StyleProps { + position: Position; +} + +export const TabsContainer = styled.div` position: relative; display: flex; flex-direction: column; diff --git a/packages/devui/src/Tabs/styles/default.ts b/packages/devui/src/Tabs/styles/default.ts index bf19218a..e79ae5da 100644 --- a/packages/devui/src/Tabs/styles/default.ts +++ b/packages/devui/src/Tabs/styles/default.ts @@ -1,6 +1,14 @@ -import { css } from 'styled-components'; +import { css, ThemedStyledProps } from 'styled-components'; +import { Theme } from '../../themes/default'; -export const style = ({ theme, main }) => css` +export interface StyleProps { + main: boolean | undefined; +} + +export const style = ({ + theme, + main, +}: ThemedStyledProps) => css` display: flex; flex: 0 0 1; padding-left: 1px; diff --git a/packages/devui/src/Tabs/styles/material.ts b/packages/devui/src/Tabs/styles/material.ts index 787ae50f..a55bda86 100644 --- a/packages/devui/src/Tabs/styles/material.ts +++ b/packages/devui/src/Tabs/styles/material.ts @@ -1,7 +1,12 @@ -import { css } from 'styled-components'; +import { css, ThemedStyledProps } from 'styled-components'; import { ripple } from '../../utils/animations'; +import { Theme } from '../../themes/default'; +import { StyleProps } from './default'; -export const style = ({ theme, main }) => css` +export const style = ({ + theme, + main, +}: ThemedStyledProps) => css` display: flex; flex: 0 0 1; padding-left: 1px; diff --git a/packages/devui/src/Toolbar/Toolbar.stories.tsx b/packages/devui/src/Toolbar/Toolbar.stories.tsx index 925e8658..fe16d3cf 100644 --- a/packages/devui/src/Toolbar/Toolbar.stories.tsx +++ b/packages/devui/src/Toolbar/Toolbar.stories.tsx @@ -1,5 +1,6 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; import styled from 'styled-components'; +import { Story } from '@storybook/react'; import { MdPlayArrow } from 'react-icons/md'; import { MdFiberManualRecord } from 'react-icons/md'; import { MdKeyboardArrowLeft } from 'react-icons/md'; @@ -16,6 +17,9 @@ import { } from '../'; import { options } from '../Select/options'; import { simple10Tabs } from '../Tabs/data'; +import { BorderPosition } from './styles/Toolbar'; +import { TooltipPosition } from '../Button/Button'; +import { Position } from '../Tabs/Tabs'; const Container = styled.div` display: flex; @@ -35,12 +39,27 @@ export default { component: Toolbar, }; -const Template = ({ +interface TemplateArgs { + borderPosition: BorderPosition; + title?: string; + tooltipPosition: TooltipPosition; + disabled?: boolean; + onClick?: React.MouseEventHandler; + label: ReactNode; +} + +const Template: Story = ({ + // eslint-disable-next-line react/prop-types borderPosition, + // eslint-disable-next-line react/prop-types title, + // eslint-disable-next-line react/prop-types tooltipPosition, + // eslint-disable-next-line react/prop-types disabled, + // eslint-disable-next-line react/prop-types onClick, + // eslint-disable-next-line react/prop-types label, }) => ( @@ -104,17 +123,41 @@ Default.argTypes = { }, }; -const TabsTemplate = ({ +interface TabsTemplateArgs { + title?: string; + tooltipPosition: TooltipPosition; + disabled?: boolean; + buttonOnClick?: React.MouseEventHandler; + label: ReactNode; + selected?: string; + main?: boolean; + tabsOnClick: (value: string) => void; + collapsible?: boolean; + position: Position; +} + +const TabsTemplate: Story = ({ + // eslint-disable-next-line react/prop-types title, + // eslint-disable-next-line react/prop-types tooltipPosition, + // eslint-disable-next-line react/prop-types disabled, + // eslint-disable-next-line react/prop-types buttonOnClick, + // eslint-disable-next-line react/prop-types label, + // eslint-disable-next-line react/prop-types selected, + // eslint-disable-next-line react/prop-types main, - tabOnClick, + // eslint-disable-next-line react/prop-types + tabsOnClick, + // eslint-disable-next-line react/prop-types collapsible, + // eslint-disable-next-line react/prop-types position, + // eslint-disable-next-line react/prop-types }) => ( @@ -130,7 +173,7 @@ const TabsTemplate = ({ tabs={simple10Tabs} selected={selected} main={main} - onClick={tabOnClick} + onClick={tabsOnClick} collapsible={collapsible} position={position} /> @@ -176,7 +219,7 @@ Tabs.argTypes = { buttonOnClick: { action: 'button clicked', }, - tabOnClick: { + tabsOnClick: { action: 'tab selected', }, position: { @@ -187,19 +230,48 @@ Tabs.argTypes = { }, }; -const WithSliderTemplate = ({ +interface WithSliderTemplateArgs { + title?: string; + tooltipPosition: TooltipPosition; + playOnClick?: React.MouseEventHandler; + value: number; + min: number; + max: number; + label?: string; + withValue?: boolean; + onChange: (value: number) => void; + previousStateOnClick?: React.MouseEventHandler; + nextStateOnClick?: React.MouseEventHandler; + selected?: string; + segmentedControlOnClick: (value: string) => void; +} + +const WithSliderTemplate: Story = ({ + // eslint-disable-next-line react/prop-types title, + // eslint-disable-next-line react/prop-types tooltipPosition, + // eslint-disable-next-line react/prop-types playOnClick, + // eslint-disable-next-line react/prop-types value, + // eslint-disable-next-line react/prop-types min, + // eslint-disable-next-line react/prop-types max, + // eslint-disable-next-line react/prop-types label, + // eslint-disable-next-line react/prop-types withValue, + // eslint-disable-next-line react/prop-types onChange, + // eslint-disable-next-line react/prop-types previousStateOnClick, + // eslint-disable-next-line react/prop-types nextStateOnClick, + // eslint-disable-next-line react/prop-types selected, + // eslint-disable-next-line react/prop-types segmentedControlOnClick, }) => ( diff --git a/packages/devui/src/Toolbar/styles/Toolbar.ts b/packages/devui/src/Toolbar/styles/Toolbar.ts index addbafb2..84ab798a 100644 --- a/packages/devui/src/Toolbar/styles/Toolbar.ts +++ b/packages/devui/src/Toolbar/styles/Toolbar.ts @@ -1,6 +1,16 @@ -import styled from 'styled-components'; +import styled, { ThemedStyledInterface } from 'styled-components'; +import { Theme } from '../../themes/default'; -const Toolbar = styled.div` +export type BorderPosition = 'top' | 'bottom'; + +export interface Props { + fullHeight?: boolean; + compact?: boolean; + borderPosition?: BorderPosition; + noBorder?: boolean; +} + +const Toolbar = (styled as ThemedStyledInterface).div` display: flex; flex-shrink: 0; box-sizing: border-box; diff --git a/packages/devui/src/colorSchemes/default.ts b/packages/devui/src/colorSchemes/default.ts index cb9691c7..d051dcd8 100644 --- a/packages/devui/src/colorSchemes/default.ts +++ b/packages/devui/src/colorSchemes/default.ts @@ -3,6 +3,7 @@ export default { scheme: 'default', + author: 'Mihail Diordiev (https://github.com/zalmoxisus)', base00: '#ffffff', base01: '#f3f3f3', base02: '#e8e8e8', diff --git a/packages/devui/src/simple-element-resize-detector.ts b/packages/devui/src/simple-element-resize-detector.ts new file mode 100644 index 00000000..66c7c0c0 --- /dev/null +++ b/packages/devui/src/simple-element-resize-detector.ts @@ -0,0 +1,6 @@ +declare module 'simple-element-resize-detector' { + export default function ( + element: HTMLElement, + handler: () => void + ): HTMLIFrameElement; +} diff --git a/packages/devui/src/utils/animations.ts b/packages/devui/src/utils/animations.ts index c9cba056..da6db22b 100644 --- a/packages/devui/src/utils/animations.ts +++ b/packages/devui/src/utils/animations.ts @@ -1,9 +1,10 @@ import { css, keyframes } from 'styled-components'; +import { Theme } from '../themes/default'; export const spin = keyframes` to { transform: rotate(1turn); } `; -export const spinner = (theme) => css` +export const spinner = (theme: Theme) => css` animation: ${spin} 400ms infinite linear; width: ${theme.spinnerSize}px; height: ${theme.spinnerSize}px; @@ -21,7 +22,7 @@ export const fadeIn = keyframes` `; // Based on https://github.com/mladenplavsic/css-ripple-effect -export const ripple = (theme) => ` +export const ripple = (theme: Theme) => ` & { position: relative; overflow: hidden; diff --git a/packages/devui/src/utils/autoPrefix.ts b/packages/devui/src/utils/autoPrefix.ts index adea5eb9..36e3a604 100644 --- a/packages/devui/src/utils/autoPrefix.ts +++ b/packages/devui/src/utils/autoPrefix.ts @@ -1,2 +1,5 @@ -export const prefixSelectors = (tag, selectors, style) => - selectors.map((selector) => `${tag}::-${selector} ${style}`); +export const prefixSelectors = ( + tag: string, + selectors: string[], + style: string +) => selectors.map((selector) => `${tag}::-${selector} ${style}`); diff --git a/packages/devui/src/utils/createThemedComponent.tsx b/packages/devui/src/utils/createThemedComponent.tsx index 019c4035..82205f62 100644 --- a/packages/devui/src/utils/createThemedComponent.tsx +++ b/packages/devui/src/utils/createThemedComponent.tsx @@ -1,13 +1,41 @@ -import React from 'react'; -import getDefaultTheme from '../themes/default'; +import React, { ComponentType } from 'react'; +import hoistNonReactStatics from 'hoist-non-react-statics'; +import getDefaultTheme, { Theme } from '../themes/default'; import { withTheme } from 'styled-components'; +import { Base16Theme } from 'base16'; -export default (UnthemedComponent) => (props) => - props.theme && props.theme.type ? ( - withTheme() - ) : ( - // used outside of container (theme provider) - +export default >( + UnthemedComponent: React.ComponentProps extends { theme?: Theme } + ? C + : never +) => { + const ThemedComponent = React.forwardRef>( + (props, ref) => { + // eslint-disable-next-line react/prop-types + if (props.theme && props.theme.type) { + const ThemedComponent = withTheme( + UnthemedComponent as ComponentType<{ theme?: Theme }> + ); + return ; + } + const UnthemedComponentAny = UnthemedComponent as any; + return ( + + ); + } ); + hoistNonReactStatics(ThemedComponent, UnthemedComponent); + + ThemedComponent.displayName = `ThemedComponent(${ + UnthemedComponent.displayName ?? 'Component' + })`; + + return ThemedComponent; +}; + // TODO: memoize it? diff --git a/packages/devui/src/utils/invertColors.ts b/packages/devui/src/utils/invertColors.ts index 2e0cdf79..2c70b0b6 100644 --- a/packages/devui/src/utils/invertColors.ts +++ b/packages/devui/src/utils/invertColors.ts @@ -1,4 +1,6 @@ -function invertColors(theme) { +import { Base16Theme } from 'base16'; + +function invertColors(theme: Base16Theme) { return { ...theme, base00: theme.base07,