This commit is contained in:
Nathan Bierema 2020-09-06 09:17:34 -04:00
parent 48dac45b5d
commit b4bd49a1ef
23 changed files with 263 additions and 111 deletions

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { Container } from '../src'; import { Container } from '../src';
import { listSchemes, listThemes } from '../src/utils/theme'; import { listSchemes, listThemes } from '../src/utils/theme';
import '../src/presets.js'; import '../src/presets';
export const parameters = { export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' }, actions: { argTypesRegex: '^on[A-Z].*' },

View File

@ -1,4 +1,4 @@
{ {
"extends": "../../../tsconfig.react.base.json", "extends": "../../../tsconfig.react.base.json",
"include": ["../src", "./"] "include": ["../src", "."]
} }

View File

@ -39,6 +39,7 @@
"base16": "^1.0.0", "base16": "^1.0.0",
"codemirror": "^5.56.0", "codemirror": "^5.56.0",
"color": "^3.1.2", "color": "^3.1.2",
"hoist-non-react-statics": "^3.3.2",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-icons": "^3.10.0", "react-icons": "^3.10.0",
"react-jsonschema-form": "^1.8.1", "react-jsonschema-form": "^1.8.1",

View File

@ -27,6 +27,7 @@ const Template: Story<ButtonProps> = (args) => (
export const Default = Template.bind({}); export const Default = Template.bind({});
Default.args = { Default.args = {
title: 'Hello Tooltip! \\a And from new line hello!', title: 'Hello Tooltip! \\a And from new line hello!',
tooltipPosition: 'top',
primary: true, primary: true,
size: 'normal', size: 'normal',
disabled: false, disabled: false,
@ -43,6 +44,7 @@ export const Mark = Template.bind({});
Mark.args = { Mark.args = {
mark: 'base08', mark: 'base08',
title: 'Hello Tooltip', title: 'Hello Tooltip',
tooltipPosition: 'top',
size: 'normal', size: 'normal',
disabled: false, disabled: false,
children: <MdFiberManualRecord />, children: <MdFiberManualRecord />,

View File

@ -42,7 +42,7 @@ const WithTabsTemplate: Story<WithTabsProps> = (args) => (
export const WithTabs = WithTabsTemplate.bind({}); export const WithTabs = WithTabsTemplate.bind({});
WithTabs.args = { WithTabs.args = {
lineNumbers: true, lineNumbers: true,
align: 'left', position: 'left',
}; };
WithTabs.argTypes = { WithTabs.argTypes = {
value: { control: { disable: true } }, value: { control: { disable: true } },

View File

@ -1,4 +1,4 @@
import React, { Component } from 'react'; import React, { Component, ComponentType } from 'react';
import Editor from './'; import Editor from './';
import Tabs from '../Tabs'; import Tabs from '../Tabs';
@ -11,7 +11,7 @@ const value2 = `
`; `;
export interface WithTabsProps { export interface WithTabsProps {
align: 'left' | 'right' | 'center'; position: 'left' | 'right' | 'center';
lineNumbers: boolean; lineNumbers: boolean;
} }
@ -27,18 +27,18 @@ export default class WithTabs extends Component<WithTabsProps> {
}; };
render() { render() {
const { align, lineNumbers } = this.props; const { position, lineNumbers } = this.props;
return ( return (
<Tabs <Tabs<TabProps>
tabs={[ tabs={[
{ {
name: 'Function 1', name: 'Function 1',
component: Editor, component: (Editor as unknown) as ComponentType<TabProps>,
selector: () => ({ value: value1, lineNumbers }), selector: () => ({ value: value1, lineNumbers }),
}, },
{ {
name: 'Function 2', name: 'Function 2',
component: Editor, component: (Editor as unknown) as ComponentType<TabProps>,
selector: () => ({ value: value2, lineNumbers }), selector: () => ({ value: value2, lineNumbers }),
}, },
]} ]}
@ -46,7 +46,7 @@ export default class WithTabs extends Component<WithTabsProps> {
onClick={(selected) => { onClick={(selected) => {
this.setState({ selected }); this.setState({ selected });
}} }}
align={align} position={position}
/> />
); );
} }

View File

@ -1,32 +1,30 @@
import React from 'react'; import React from 'react';
import { Widget } from 'react-jsonschema-form'; import { FieldProps, Widget, WidgetProps } from 'react-jsonschema-form';
import Select from '../Select'; import Select from '../Select';
import Slider from '../Slider'; import Slider from '../Slider';
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
const SelectWidget: Widget = ({ options, multi, ...rest }) => ( const SelectWidget: Widget = ({ options, ...rest }) => (
<Select options={options.enumOptions} multiple={multi} {...rest} /> <Select options={options.enumOptions} {...rest} />
); );
const RangeWidget: Widget = ({ const RangeWidget: Widget = (({
schema, schema,
readonly, disabled,
autofocus,
label, // eslint-disable-line label, // eslint-disable-line
options, // eslint-disable-line options, // eslint-disable-line
formContext, // eslint-disable-line formContext, // eslint-disable-line
registry, // eslint-disable-line registry, // eslint-disable-line
...rest ...rest
}) => ( }: WidgetProps & { registry: FieldProps['registry'] }) =>
(
<Slider <Slider
{...rest} {...rest}
autoFocus={autofocus} disabled={disabled}
readOnly={readonly}
min={schema.minimum} min={schema.minimum}
max={schema.maximum} max={schema.maximum}
step={schema.multipleOf}
withValue withValue
/> />
); ) as unknown) as Widget;
export default { SelectWidget, RangeWidget }; export default { SelectWidget, RangeWidget };

View File

@ -11,7 +11,7 @@ export interface SelectProps extends Omit<ReactSelectProps, 'theme'> {
/** /**
* Wrapper around [React Select](https://github.com/JedWatson/react-select). * Wrapper around [React Select](https://github.com/JedWatson/react-select).
*/ */
class Select extends (PureComponent || Component)<SelectProps> { export class Select extends (PureComponent || Component)<SelectProps> {
render() { render() {
return ( return (
<ReactSelect <ReactSelect

View File

@ -1,7 +1,9 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { Story } from '@storybook/react';
import Tabs from './'; import Tabs from './';
import { tabs, simple10Tabs } from './data'; import { tabs, simple10Tabs } from './data';
import { TabsProps } from './Tabs';
const Container = styled.div` const Container = styled.div`
display: flex; display: flex;
@ -16,7 +18,7 @@ export default {
component: Tabs, component: Tabs,
}; };
const Template = (args) => ( const Template: Story<TabsProps<unknown>> = (args) => (
<Container> <Container>
<Tabs {...args} /> <Tabs {...args} />
</Container> </Container>
@ -35,7 +37,9 @@ Default.argTypes = {
onClick: { control: { disable: true } }, onClick: { control: { disable: true } },
}; };
export const WithContent = Template.bind({}); export const WithContent = (Template as Story<
TabsProps<{ selected: string }>
>).bind({});
WithContent.args = { WithContent.args = {
tabs, tabs,
selected: 'Tab2', selected: 'Tab2',

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TabsHeader from './TabsHeader'; import TabsHeader, { ReactButtonElement, Tab } from './TabsHeader';
import { TabsContainer } from './styles/common'; import { TabsContainer } from './styles/common';
export type Position = 'left' | 'right' | 'center'; export type Position = 'left' | 'right' | 'center';
@ -96,9 +96,8 @@ export default class Tabs<P> extends Component<TabsProps<P>> {
</TabsContainer> </TabsContainer>
); );
} }
}
Tabs.propTypes = { static propTypes = {
tabs: PropTypes.array.isRequired, tabs: PropTypes.array.isRequired,
selected: PropTypes.string, selected: PropTypes.string,
main: PropTypes.bool, main: PropTypes.bool,
@ -107,4 +106,5 @@ Tabs.propTypes = {
position: PropTypes.oneOf(['left', 'right', 'center']), position: PropTypes.oneOf(['left', 'right', 'center']),
}; };
Tabs.defaultProps = { position: 'left' }; static defaultProps = { position: 'left' };
}

View File

@ -22,7 +22,7 @@ export interface Tab<P> {
interface Props<P> { interface Props<P> {
tabs: ReactButtonElement[]; tabs: ReactButtonElement[];
items: Tab<P>; items: Tab<P>[];
main: boolean | undefined; main: boolean | undefined;
onClick: (value: string) => void; onClick: (value: string) => void;
position: 'left' | 'right' | 'center'; position: 'left' | 'right' | 'center';
@ -38,17 +38,18 @@ interface State {
} }
export default class TabsHeader<P> extends Component<Props<P>, State> { export default class TabsHeader<P> extends Component<Props<P>, State> {
constructor(props: Props<P>) { state: State = {
super(props); visibleTabs: this.props.tabs.slice(),
this.state = {
visibleTabs: props.tabs.slice(),
hiddenTabs: [], hiddenTabs: [],
subMenuOpened: false, subMenuOpened: false,
contextMenu: undefined, contextMenu: undefined,
}; };
this.iconWidth = 0;
this.hiddenTabsWidth = []; iconWidth = 0;
} hiddenTabsWidth: number[] = [];
tabsWrapperRef?: HTMLDivElement | null;
tabsRef?: HTMLDivElement | null;
resizeDetector?: HTMLIFrameElement;
UNSAFE_componentWillReceiveProps(nextProps: Props<P>) { UNSAFE_componentWillReceiveProps(nextProps: Props<P>) {
if ( if (
@ -76,8 +77,11 @@ export default class TabsHeader<P> extends Component<Props<P>, State> {
let shouldCollapse = false; let shouldCollapse = false;
if (this.iconWidth === 0) { if (this.iconWidth === 0) {
const tabButtons = this.tabsRef.children; const tabButtons = this.tabsRef!.children;
if (this.tabsRef.children[tabButtons.length - 1].value === 'expandIcon') { if (
(this.tabsRef!.children[tabButtons.length - 1] as HTMLButtonElement)
.value === 'expandIcon'
) {
this.iconWidth = tabButtons[ this.iconWidth = tabButtons[
tabButtons.length - 1 tabButtons.length - 1
].getBoundingClientRect().width; ].getBoundingClientRect().width;
@ -104,12 +108,12 @@ export default class TabsHeader<P> extends Component<Props<P>, State> {
} }
enableResizeEvents() { enableResizeEvents() {
this.resizeDetector = observeResize(this.tabsWrapperRef, this.collapse); this.resizeDetector = observeResize(this.tabsWrapperRef!, this.collapse);
window.addEventListener('mousedown', this.hideSubmenu); window.addEventListener('mousedown', this.hideSubmenu);
} }
disableResizeEvents() { disableResizeEvents() {
this.resizeDetector.remove(); this.resizeDetector!.remove();
window.removeEventListener('mousedown', this.hideSubmenu); window.removeEventListener('mousedown', this.hideSubmenu);
} }
@ -119,13 +123,13 @@ export default class TabsHeader<P> extends Component<Props<P>, State> {
const { selected, tabs } = this.props; const { selected, tabs } = this.props;
const tabsWrapperRef = this.tabsWrapperRef; const tabsWrapperRef = this.tabsWrapperRef;
const tabsRef = this.tabsRef; const tabsRef = this.tabsRef;
const tabButtons = this.tabsRef.children; const tabButtons = this.tabsRef!.children;
const visibleTabs = this.state.visibleTabs; const visibleTabs = this.state.visibleTabs;
const hiddenTabs = this.state.hiddenTabs; const hiddenTabs = this.state.hiddenTabs;
let tabsWrapperRight = tabsWrapperRef.getBoundingClientRect().right; let tabsWrapperRight = tabsWrapperRef!.getBoundingClientRect().right;
if (!tabsWrapperRight) return; // tabs are hidden if (!tabsWrapperRight) return; // tabs are hidden
const tabsRefRight = tabsRef.getBoundingClientRect().right; const tabsRefRight = tabsRef!.getBoundingClientRect().right;
let i = visibleTabs.length - 1; let i = visibleTabs.length - 1;
let hiddenTab; let hiddenTab;
@ -133,16 +137,16 @@ export default class TabsHeader<P> extends Component<Props<P>, State> {
if ( if (
this.props.position === 'right' && this.props.position === 'right' &&
hiddenTabs.length > 0 && hiddenTabs.length > 0 &&
tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] < tabsRef!.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
tabsWrapperRef.getBoundingClientRect().width tabsWrapperRef!.getBoundingClientRect().width
) { ) {
while ( while (
i < tabs.length - 1 && i < tabs.length - 1 &&
tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] < tabsRef!.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
tabsWrapperRef.getBoundingClientRect().width tabsWrapperRef!.getBoundingClientRect().width
) { ) {
hiddenTab = hiddenTabs.shift(); hiddenTab = hiddenTabs.shift();
visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab); visibleTabs.splice(Number(hiddenTab!.key), 0, hiddenTab!);
i++; i++;
} }
} else { } else {
@ -152,7 +156,7 @@ export default class TabsHeader<P> extends Component<Props<P>, State> {
tabButtons[i].getBoundingClientRect().right >= tabButtons[i].getBoundingClientRect().right >=
tabsWrapperRight - this.iconWidth tabsWrapperRight - this.iconWidth
) { ) {
if (tabButtons[i].value !== selected) { if ((tabButtons[i] as HTMLButtonElement).value !== selected) {
hiddenTabs.unshift(...visibleTabs.splice(i, 1)); hiddenTabs.unshift(...visibleTabs.splice(i, 1));
this.hiddenTabsWidth.unshift( this.hiddenTabsWidth.unshift(
tabButtons[i].getBoundingClientRect().width tabButtons[i].getBoundingClientRect().width
@ -171,7 +175,7 @@ export default class TabsHeader<P> extends Component<Props<P>, State> {
tabsWrapperRight - this.iconWidth tabsWrapperRight - this.iconWidth
) { ) {
hiddenTab = hiddenTabs.shift(); hiddenTab = hiddenTabs.shift();
visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab); visibleTabs.splice(Number(hiddenTab!.key), 0, hiddenTab!);
this.hiddenTabsWidth.shift(); this.hiddenTabsWidth.shift();
i++; i++;
} }
@ -183,15 +187,15 @@ export default class TabsHeader<P> extends Component<Props<P>, State> {
this.setState({ subMenuOpened: false, contextMenu: undefined }); this.setState({ subMenuOpened: false, contextMenu: undefined });
}; };
getTabsWrapperRef = (node) => { getTabsWrapperRef: React.RefCallback<HTMLDivElement> = (node) => {
this.tabsWrapperRef = node; this.tabsWrapperRef = node;
}; };
getTabsRef = (node) => { getTabsRef: React.RefCallback<HTMLDivElement> = (node) => {
this.tabsRef = node; this.tabsRef = node;
}; };
expandMenu = (e) => { expandMenu: React.MouseEventHandler = (e) => {
const rect = e.currentTarget.children[0].getBoundingClientRect(); const rect = e.currentTarget.children[0].getBoundingClientRect();
this.setState({ this.setState({
contextMenu: { contextMenu: {
@ -231,9 +235,8 @@ export default class TabsHeader<P> extends Component<Props<P>, State> {
</TabsWrapper> </TabsWrapper>
); );
} }
}
TabsHeader.propTypes = { static propTypes = {
tabs: PropTypes.array.isRequired, tabs: PropTypes.array.isRequired,
items: PropTypes.array.isRequired, items: PropTypes.array.isRequired,
main: PropTypes.bool, main: PropTypes.bool,
@ -242,3 +245,4 @@ TabsHeader.propTypes = {
collapsible: PropTypes.bool, collapsible: PropTypes.bool,
selected: PropTypes.string, selected: PropTypes.string,
}; };
}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
const Component = ({ selected }) => ( const Component = ({ selected }: { selected: string }) => (
<div <div
style={{ style={{
display: 'flex', display: 'flex',
@ -17,7 +17,9 @@ const Component = ({ selected }) => (
); );
/* eslint-enable react/prop-types */ /* eslint-enable react/prop-types */
const selector = (tab) => ({ selected: tab.name }); const selector = (tab: { name: string; value?: string }) => ({
selected: tab.name,
});
export const tabs = [ 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++) for (let i = 1; i <= 10; i++)
simple10Tabs.push({ name: `Tab${i}`, value: `${i}` }); simple10Tabs.push({ name: `Tab${i}`, value: `${i}` });

View File

@ -1,6 +1,11 @@
import styled from 'styled-components'; import styled from 'styled-components';
import { Position } from '../Tabs';
export const TabsContainer = styled.div` interface StyleProps {
position: Position;
}
export const TabsContainer = styled.div<StyleProps>`
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -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<StyleProps, Theme>) => css`
display: flex; display: flex;
flex: 0 0 1; flex: 0 0 1;
padding-left: 1px; padding-left: 1px;

View File

@ -1,7 +1,12 @@
import { css } from 'styled-components'; import { css, ThemedStyledProps } from 'styled-components';
import { ripple } from '../../utils/animations'; 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<StyleProps, Theme>) => css`
display: flex; display: flex;
flex: 0 0 1; flex: 0 0 1;
padding-left: 1px; padding-left: 1px;

View File

@ -1,5 +1,6 @@
import React from 'react'; import React, { ReactNode } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { Story } from '@storybook/react';
import { MdPlayArrow } from 'react-icons/md'; import { MdPlayArrow } from 'react-icons/md';
import { MdFiberManualRecord } from 'react-icons/md'; import { MdFiberManualRecord } from 'react-icons/md';
import { MdKeyboardArrowLeft } from 'react-icons/md'; import { MdKeyboardArrowLeft } from 'react-icons/md';
@ -16,6 +17,9 @@ import {
} from '../'; } from '../';
import { options } from '../Select/options'; import { options } from '../Select/options';
import { simple10Tabs } from '../Tabs/data'; import { simple10Tabs } from '../Tabs/data';
import { BorderPosition } from './styles/Toolbar';
import { TooltipPosition } from '../Button/Button';
import { Position } from '../Tabs/Tabs';
const Container = styled.div` const Container = styled.div`
display: flex; display: flex;
@ -35,12 +39,27 @@ export default {
component: Toolbar, component: Toolbar,
}; };
const Template = ({ interface TemplateArgs {
borderPosition: BorderPosition;
title?: string;
tooltipPosition: TooltipPosition;
disabled?: boolean;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
label: ReactNode;
}
const Template: Story<TemplateArgs> = ({
// eslint-disable-next-line react/prop-types
borderPosition, borderPosition,
// eslint-disable-next-line react/prop-types
title, title,
// eslint-disable-next-line react/prop-types
tooltipPosition, tooltipPosition,
// eslint-disable-next-line react/prop-types
disabled, disabled,
// eslint-disable-next-line react/prop-types
onClick, onClick,
// eslint-disable-next-line react/prop-types
label, label,
}) => ( }) => (
<Container> <Container>
@ -104,17 +123,41 @@ Default.argTypes = {
}, },
}; };
const TabsTemplate = ({ interface TabsTemplateArgs {
title?: string;
tooltipPosition: TooltipPosition;
disabled?: boolean;
buttonOnClick?: React.MouseEventHandler<HTMLButtonElement>;
label: ReactNode;
selected?: string;
main?: boolean;
tabsOnClick: (value: string) => void;
collapsible?: boolean;
position: Position;
}
const TabsTemplate: Story<TabsTemplateArgs> = ({
// eslint-disable-next-line react/prop-types
title, title,
// eslint-disable-next-line react/prop-types
tooltipPosition, tooltipPosition,
// eslint-disable-next-line react/prop-types
disabled, disabled,
// eslint-disable-next-line react/prop-types
buttonOnClick, buttonOnClick,
// eslint-disable-next-line react/prop-types
label, label,
// eslint-disable-next-line react/prop-types
selected, selected,
// eslint-disable-next-line react/prop-types
main, main,
tabOnClick, // eslint-disable-next-line react/prop-types
tabsOnClick,
// eslint-disable-next-line react/prop-types
collapsible, collapsible,
// eslint-disable-next-line react/prop-types
position, position,
// eslint-disable-next-line react/prop-types
}) => ( }) => (
<Container> <Container>
<Toolbar> <Toolbar>
@ -130,7 +173,7 @@ const TabsTemplate = ({
tabs={simple10Tabs} tabs={simple10Tabs}
selected={selected} selected={selected}
main={main} main={main}
onClick={tabOnClick} onClick={tabsOnClick}
collapsible={collapsible} collapsible={collapsible}
position={position} position={position}
/> />
@ -176,7 +219,7 @@ Tabs.argTypes = {
buttonOnClick: { buttonOnClick: {
action: 'button clicked', action: 'button clicked',
}, },
tabOnClick: { tabsOnClick: {
action: 'tab selected', action: 'tab selected',
}, },
position: { position: {
@ -187,19 +230,48 @@ Tabs.argTypes = {
}, },
}; };
const WithSliderTemplate = ({ interface WithSliderTemplateArgs {
title?: string;
tooltipPosition: TooltipPosition;
playOnClick?: React.MouseEventHandler<HTMLButtonElement>;
value: number;
min: number;
max: number;
label?: string;
withValue?: boolean;
onChange: (value: number) => void;
previousStateOnClick?: React.MouseEventHandler<HTMLButtonElement>;
nextStateOnClick?: React.MouseEventHandler<HTMLButtonElement>;
selected?: string;
segmentedControlOnClick: (value: string) => void;
}
const WithSliderTemplate: Story<WithSliderTemplateArgs> = ({
// eslint-disable-next-line react/prop-types
title, title,
// eslint-disable-next-line react/prop-types
tooltipPosition, tooltipPosition,
// eslint-disable-next-line react/prop-types
playOnClick, playOnClick,
// eslint-disable-next-line react/prop-types
value, value,
// eslint-disable-next-line react/prop-types
min, min,
// eslint-disable-next-line react/prop-types
max, max,
// eslint-disable-next-line react/prop-types
label, label,
// eslint-disable-next-line react/prop-types
withValue, withValue,
// eslint-disable-next-line react/prop-types
onChange, onChange,
// eslint-disable-next-line react/prop-types
previousStateOnClick, previousStateOnClick,
// eslint-disable-next-line react/prop-types
nextStateOnClick, nextStateOnClick,
// eslint-disable-next-line react/prop-types
selected, selected,
// eslint-disable-next-line react/prop-types
segmentedControlOnClick, segmentedControlOnClick,
}) => ( }) => (
<Container> <Container>

View File

@ -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<Theme>).div<Props>`
display: flex; display: flex;
flex-shrink: 0; flex-shrink: 0;
box-sizing: border-box; box-sizing: border-box;

View File

@ -3,6 +3,7 @@
export default { export default {
scheme: 'default', scheme: 'default',
author: 'Mihail Diordiev (https://github.com/zalmoxisus)',
base00: '#ffffff', base00: '#ffffff',
base01: '#f3f3f3', base01: '#f3f3f3',
base02: '#e8e8e8', base02: '#e8e8e8',

View File

@ -0,0 +1,6 @@
declare module 'simple-element-resize-detector' {
export default function (
element: HTMLElement,
handler: () => void
): HTMLIFrameElement;
}

View File

@ -1,9 +1,10 @@
import { css, keyframes } from 'styled-components'; import { css, keyframes } from 'styled-components';
import { Theme } from '../themes/default';
export const spin = keyframes` export const spin = keyframes`
to { transform: rotate(1turn); } to { transform: rotate(1turn); }
`; `;
export const spinner = (theme) => css` export const spinner = (theme: Theme) => css`
animation: ${spin} 400ms infinite linear; animation: ${spin} 400ms infinite linear;
width: ${theme.spinnerSize}px; width: ${theme.spinnerSize}px;
height: ${theme.spinnerSize}px; height: ${theme.spinnerSize}px;
@ -21,7 +22,7 @@ export const fadeIn = keyframes`
`; `;
// Based on https://github.com/mladenplavsic/css-ripple-effect // Based on https://github.com/mladenplavsic/css-ripple-effect
export const ripple = (theme) => ` export const ripple = (theme: Theme) => `
& { & {
position: relative; position: relative;
overflow: hidden; overflow: hidden;

View File

@ -1,2 +1,5 @@
export const prefixSelectors = (tag, selectors, style) => export const prefixSelectors = (
selectors.map((selector) => `${tag}::-${selector} ${style}`); tag: string,
selectors: string[],
style: string
) => selectors.map((selector) => `${tag}::-${selector} ${style}`);

View File

@ -1,13 +1,41 @@
import React from 'react'; import React, { ComponentType } from 'react';
import getDefaultTheme from '../themes/default'; import hoistNonReactStatics from 'hoist-non-react-statics';
import getDefaultTheme, { Theme } from '../themes/default';
import { withTheme } from 'styled-components'; import { withTheme } from 'styled-components';
import { Base16Theme } from 'base16';
export default (UnthemedComponent) => (props) => export default <C extends React.ComponentType<any>>(
props.theme && props.theme.type ? ( UnthemedComponent: React.ComponentProps<C> extends { theme?: Theme }
withTheme(<UnthemedComponent {...props} />) ? C
) : ( : never
// used outside of container (theme provider) ) => {
<UnthemedComponent {...props} theme={getDefaultTheme({})} /> const ThemedComponent = React.forwardRef<C, React.ComponentProps<C>>(
(props, ref) => {
// eslint-disable-next-line react/prop-types
if (props.theme && props.theme.type) {
const ThemedComponent = withTheme(
UnthemedComponent as ComponentType<{ theme?: Theme }>
);
return <ThemedComponent {...props} ref={ref} />;
}
const UnthemedComponentAny = UnthemedComponent as any;
return (
<UnthemedComponentAny
{...props}
ref={ref}
theme={getDefaultTheme({} as Base16Theme)}
/>
);
}
); );
hoistNonReactStatics(ThemedComponent, UnthemedComponent);
ThemedComponent.displayName = `ThemedComponent(${
UnthemedComponent.displayName ?? 'Component'
})`;
return ThemedComponent;
};
// TODO: memoize it? // TODO: memoize it?

View File

@ -1,4 +1,6 @@
function invertColors(theme) { import { Base16Theme } from 'base16';
function invertColors(theme: Base16Theme) {
return { return {
...theme, ...theme,
base00: theme.base07, base00: theme.base07,