diff --git a/src/Button/Button.tsx b/src/Button/Button.tsx
index df0a918..6cbf6aa 100644
--- a/src/Button/Button.tsx
+++ b/src/Button/Button.tsx
@@ -6,7 +6,6 @@ import {
createDisabledTextStyles,
createFlatBoxStyles,
createHatchedBackground,
- createWellBorderStyles,
focusOutline
} from '../common';
import { blockSizes } from '../common/system';
@@ -107,12 +106,14 @@ export const StyledButton = styled.button`
border: 2px solid transparent;
&:hover,
&:focus {
- ${!disabled && !active && createWellBorderStyles(false)}
+ ${!disabled &&
+ !active &&
+ createBorderStyles({ style: 'buttonThin' })}
}
&:active {
- ${!disabled && createWellBorderStyles(true)}
+ ${!disabled && createBorderStyles({ style: 'buttonThinPressed' })}
}
- ${active && createWellBorderStyles(true)}
+ ${active && createBorderStyles({ style: 'buttonThinPressed' })}
${disabled && createDisabledTextStyles()}
`
: css`
diff --git a/src/Counter/Counter.stories.tsx b/src/Counter/Counter.stories.tsx
index 7890db9..a755324 100644
--- a/src/Counter/Counter.stories.tsx
+++ b/src/Counter/Counter.stories.tsx
@@ -1,6 +1,6 @@
import { ComponentMeta } from '@storybook/react';
import React, { useState } from 'react';
-import { Button, Counter, Panel } from 'react95';
+import { Button, Counter, Frame } from 'react95';
import styled from 'styled-components';
const Wrapper = styled.div`
@@ -29,14 +29,14 @@ export function Default() {
const [count, setCount] = useState(13);
const handleClick = () => setCount(count + 1);
return (
-
+
Click!
-
+
);
}
diff --git a/src/Counter/Counter.tsx b/src/Counter/Counter.tsx
index 55dfec8..7c2b5c2 100644
--- a/src/Counter/Counter.tsx
+++ b/src/Counter/Counter.tsx
@@ -1,7 +1,7 @@
import React, { forwardRef, useMemo } from 'react';
import styled from 'styled-components';
-import { createWellBorderStyles } from '../common';
+import { createBorderStyles } from '../common';
import { CommonStyledProps, Sizes } from '../types';
import { Digit } from './Digit';
@@ -13,7 +13,7 @@ type CounterProps = {
CommonStyledProps;
const CounterWrapper = styled.div`
- ${createWellBorderStyles(true)}
+ ${createBorderStyles({ style: 'status' })}
display: inline-flex;
background: #000000;
`;
diff --git a/src/Frame/Frame.spec.tsx b/src/Frame/Frame.spec.tsx
new file mode 100644
index 0000000..2cabe12
--- /dev/null
+++ b/src/Frame/Frame.spec.tsx
@@ -0,0 +1,41 @@
+import { render } from '@testing-library/react';
+import React from 'react';
+
+import { Frame } from './Frame';
+
+describe(' ', () => {
+ it('should render frame', () => {
+ const { container } = render( );
+ const frame = container.firstElementChild;
+
+ expect(frame).toBeInTheDocument();
+ });
+
+ it('should render custom styles', () => {
+ const { container } = render(
+
+ );
+ const frame = container.firstElementChild;
+
+ expect(frame).toHaveAttribute('style', 'background-color: papayawhip;');
+ });
+
+ it('should render children', async () => {
+ const { findByText } = render(
+
+ Cool frame
+
+ );
+ const content = await findByText(/cool frame/i);
+
+ expect(content).toBeInTheDocument();
+ });
+
+ it('should render custom props', () => {
+ const customProps = { title: 'frame' };
+ const { container } = render( );
+ const frame = container.firstElementChild;
+
+ expect(frame).toHaveAttribute('title', 'frame');
+ });
+});
diff --git a/src/Panel/Panel.stories.tsx b/src/Frame/Frame.stories.tsx
similarity index 54%
rename from src/Panel/Panel.stories.tsx
rename to src/Frame/Frame.stories.tsx
index 930fb96..dd19c23 100644
--- a/src/Panel/Panel.stories.tsx
+++ b/src/Frame/Frame.stories.tsx
@@ -1,6 +1,6 @@
import { ComponentMeta } from '@storybook/react';
import React from 'react';
-import { Panel } from 'react95';
+import { Frame } from 'react95';
import styled from 'styled-components';
const Wrapper = styled.div`
@@ -19,28 +19,30 @@ const Wrapper = styled.div`
`;
export default {
- title: 'Panel',
- component: Panel,
+ title: 'Layout/Frame',
+ component: Frame,
decorators: [story => {story()} ]
-} as ComponentMeta;
+} as ComponentMeta;
export function Default() {
return (
-
- Notice the subtle difference in borders. The lightest border is not on
- the edge of this panel.
+ This is a frame of the 'window' variant, the default. Notice
+ the subtle difference in borders. The lightest border is not on the edge
+ of this frame.
-
- This panel on the other hand has the lightest border on the edge. Use
- this panel inside 'outside' panels.
+
+ This frame of the 'button' variant on the other hand has the
+ lightest border on the edge. Use this frame inside 'window'
+ frames.
-
- Put some content here
-
-
-
+
+
- The 'well' variant of a panel is often used as a window
- footer.
-
-
+ The 'status' variant of a frame is often used as a status bar
+ at the end of the window.
+
+
);
}
diff --git a/src/Frame/Frame.tsx b/src/Frame/Frame.tsx
new file mode 100644
index 0000000..440eddd
--- /dev/null
+++ b/src/Frame/Frame.tsx
@@ -0,0 +1,58 @@
+import React, { forwardRef } from 'react';
+import styled, { css } from 'styled-components';
+import { createBorderStyles, createBoxStyles } from '../common';
+import { CommonStyledProps } from '../types';
+
+type FrameProps = {
+ children?: React.ReactNode;
+ shadow?: boolean;
+ variant?: 'outside' | 'field' | 'inside' | 'well';
+} & React.HTMLAttributes &
+ CommonStyledProps;
+
+const createFrameStyles = (variant: FrameProps['variant']) => {
+ switch (variant) {
+ case 'well':
+ return css`
+ ${createBorderStyles({ style: 'status' })}
+ `;
+ case 'outside':
+ return css`
+ ${createBorderStyles({ style: 'window' })}
+ `;
+ case 'field':
+ return css`
+ ${createBorderStyles({ style: 'field' })}
+ `;
+ default:
+ return css`
+ ${createBorderStyles()}
+ `;
+ }
+};
+
+const StyledFrame = styled.div>>`
+ position: relative;
+ font-size: 1rem;
+ ${({ variant }) => createFrameStyles(variant)}
+ ${({ variant }) =>
+ createBoxStyles(
+ variant === 'field'
+ ? { background: 'canvas', color: 'canvasText' }
+ : undefined
+ )}
+`;
+
+const Frame = forwardRef(
+ ({ children, shadow = false, variant = 'window', ...otherProps }, ref) => {
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+Frame.displayName = 'Frame';
+
+export { Frame, FrameProps };
diff --git a/src/MenuList/MenuList.tsx b/src/MenuList/MenuList.tsx
index 9304c6e..57b5784 100644
--- a/src/MenuList/MenuList.tsx
+++ b/src/MenuList/MenuList.tsx
@@ -17,7 +17,7 @@ const MenuList = styled.ul.attrs(() => ({
box-sizing: border-box;
width: ${props => (props.fullWidth ? '100%' : 'auto')};
padding: 4px;
- ${createBorderStyles({ windowBorders: true })}
+ ${createBorderStyles({ style: 'window' })}
${createBoxStyles()}
${props =>
props.inline &&
diff --git a/src/Panel/Panel.spec.tsx b/src/Panel/Panel.spec.tsx
deleted file mode 100644
index 90d7e4a..0000000
--- a/src/Panel/Panel.spec.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { render } from '@testing-library/react';
-import React from 'react';
-
-import { Panel } from './Panel';
-
-describe(' ', () => {
- it('should render panel', () => {
- const { container } = render( );
- const panel = container.firstElementChild;
-
- expect(panel).toBeInTheDocument();
- });
-
- it('should render custom styles', () => {
- const { container } = render(
-
- );
- const panel = container.firstElementChild;
-
- expect(panel).toHaveAttribute('style', 'background-color: papayawhip;');
- });
-
- it('should render children', async () => {
- const { findByText } = render(
-
- Cool panel
-
- );
- const content = await findByText(/cool panel/i);
-
- expect(content).toBeInTheDocument();
- });
-
- it('should render custom props', () => {
- const customProps = { title: 'panel' };
- const { container } = render( );
- const panel = container.firstElementChild;
-
- expect(panel).toHaveAttribute('title', 'panel');
- });
-});
diff --git a/src/Panel/Panel.tsx b/src/Panel/Panel.tsx
deleted file mode 100644
index 58af24d..0000000
--- a/src/Panel/Panel.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import React, { forwardRef } from 'react';
-import styled, { css } from 'styled-components';
-import {
- createBorderStyles,
- createBoxStyles,
- createWellBorderStyles
-} from '../common';
-import { CommonStyledProps } from '../types';
-
-type PanelProps = {
- children?: React.ReactNode;
- shadow?: boolean;
- variant?: 'outside' | 'inside' | 'well';
-} & React.HTMLAttributes &
- CommonStyledProps;
-
-const createPanelStyles = (variant: PanelProps['variant']) => {
- switch (variant) {
- case 'well':
- return css`
- ${createWellBorderStyles(true)}
- `;
- case 'outside':
- return css`
- ${createBorderStyles({ windowBorders: true })}
- `;
- default:
- return css`
- ${createBorderStyles()}
- `;
- }
-};
-
-const StyledPanel = styled.div>>`
- position: relative;
- font-size: 1rem;
- ${({ variant }) => createPanelStyles(variant)}
- ${createBoxStyles()}
-`;
-
-const Panel = forwardRef(function Panel(
- { children, shadow = false, variant = 'outside', ...otherProps },
- ref
-) {
- return (
-
- {children}
-
- );
-});
-
-export { Panel, PanelProps };
diff --git a/src/TableHeadCell/TableHeadCell.tsx b/src/TableHeadCell/TableHeadCell.tsx
index dd4c359..81daf51 100644
--- a/src/TableHeadCell/TableHeadCell.tsx
+++ b/src/TableHeadCell/TableHeadCell.tsx
@@ -37,7 +37,7 @@ const StyledHeadCell = styled.th<{ $disabled: boolean }>`
css`
&:active {
&:before {
- ${createBorderStyles({ invert: true, windowBorders: true })}
+ ${createBorderStyles({ invert: true, style: 'window' })}
border-left: none;
border-top: none;
padding-top: 2px;
diff --git a/src/Window/Window.stories.tsx b/src/Window/Window.stories.tsx
index 050a044..e27b4c6 100644
--- a/src/Window/Window.stories.tsx
+++ b/src/Window/Window.stories.tsx
@@ -2,7 +2,7 @@ import { ComponentMeta } from '@storybook/react';
import React from 'react';
import {
Button,
- Panel,
+ Frame,
Toolbar,
Window,
WindowContent,
@@ -97,9 +97,9 @@ export function Default() {
you tho!)
-
+
Put some useful information here
-
+
diff --git a/src/Window/Window.tsx b/src/Window/Window.tsx
index a8b10ea..a1c2d6b 100644
--- a/src/Window/Window.tsx
+++ b/src/Window/Window.tsx
@@ -15,7 +15,7 @@ const StyledWindow = styled.div`
position: relative;
padding: 4px;
font-size: 1rem;
- ${createBorderStyles({ windowBorders: true })}
+ ${createBorderStyles({ style: 'window' })}
${createBoxStyles()}
`;
diff --git a/src/common/index.ts b/src/common/index.ts
index 9f90ae7..6789ed1 100644
--- a/src/common/index.ts
+++ b/src/common/index.ts
@@ -1,5 +1,5 @@
import { css } from 'styled-components';
-import { Color, CommonThemeProps } from '../types';
+import { Color, CommonThemeProps, Theme } from '../types';
export const shadow = '4px 4px 10px 0 rgba(0, 0, 0, 0.35)';
export const insetShadow = 'inset 2px 2px 3px rgba(0,0,0,0.2)';
@@ -11,11 +11,17 @@ export const createDisabledTextStyles = () => css`
/* filter: grayscale(100%); */
`;
-export const createBoxStyles = () => css`
+export const createBoxStyles = ({
+ background = 'material',
+ color = 'materialText'
+}: {
+ background?: keyof Theme;
+ color?: keyof Theme;
+} = {}) => css`
box-sizing: border-box;
display: inline-block;
- background: ${({ theme }) => theme.material};
- color: ${({ theme }) => theme.materialText};
+ background: ${({ theme }) => theme[background]};
+ color: ${({ theme }) => theme[color]};
`;
// TODO for flat box styles add checkered background when disabled (not solid color)
@@ -57,56 +63,137 @@ export const createFlatBoxStyles = () => css`
outline-offset: -4px;
`;
+export type BorderStyles =
+ | 'button'
+ | 'buttonPressed'
+ | 'buttonThin'
+ | 'buttonThinPressed'
+ | 'field'
+ | 'grouping'
+ | 'status'
+ | 'window';
+
+type BorderStyle = {
+ topLeftOuter: keyof Theme;
+ topLeftInner: keyof Theme | null;
+ bottomRightInner: keyof Theme | null;
+ bottomRightOuter: keyof Theme;
+};
+
+const borderStyles: Record = {
+ button: {
+ topLeftOuter: 'borderLightest',
+ topLeftInner: 'borderLight',
+ bottomRightInner: 'borderDark',
+ bottomRightOuter: 'borderDarkest'
+ },
+ buttonPressed: {
+ topLeftOuter: 'borderDarkest',
+ topLeftInner: 'borderDark',
+ bottomRightInner: 'borderLight',
+ bottomRightOuter: 'borderLightest'
+ },
+ buttonThin: {
+ topLeftOuter: 'borderLightest',
+ topLeftInner: null,
+ bottomRightInner: null,
+ bottomRightOuter: 'borderDark'
+ },
+ buttonThinPressed: {
+ topLeftOuter: 'borderDark',
+ topLeftInner: null,
+ bottomRightInner: null,
+ bottomRightOuter: 'borderLightest'
+ },
+ field: {
+ topLeftOuter: 'borderDark',
+ topLeftInner: 'borderDarkest',
+ bottomRightInner: 'borderLight',
+ bottomRightOuter: 'borderLightest'
+ },
+ grouping: {
+ topLeftOuter: 'borderDark',
+ topLeftInner: 'borderLightest',
+ bottomRightInner: 'borderDark',
+ bottomRightOuter: 'borderLightest'
+ },
+ status: {
+ topLeftOuter: 'borderDark',
+ topLeftInner: null,
+ bottomRightInner: null,
+ bottomRightOuter: 'borderLightest'
+ },
+ window: {
+ topLeftOuter: 'borderLight',
+ topLeftInner: 'borderLightest',
+ bottomRightInner: 'borderDark',
+ bottomRightOuter: 'borderDarkest'
+ }
+};
+
+export const createInnerBorderWithShadow = ({
+ theme,
+ topLeftInner,
+ bottomRightInner,
+ hasShadow = false,
+ hasInsetShadow = false
+}: {
+ theme: Theme;
+ topLeftInner: keyof Theme | null;
+ bottomRightInner: keyof Theme | null;
+ hasShadow?: boolean;
+ hasInsetShadow?: boolean;
+}) =>
+ [
+ hasShadow ? shadow : false,
+ hasInsetShadow ? insetShadow : false,
+ topLeftInner !== null
+ ? `inset 1px 1px 0px 1px ${theme[topLeftInner]}`
+ : false,
+ bottomRightInner !== null
+ ? `inset -1px -1px 0 1px ${theme[bottomRightInner]}`
+ : false
+ ]
+ .filter(Boolean)
+ .join(', ');
+
export const createBorderStyles = ({
invert = false,
- windowBorders = false
-} = {}) =>
- invert
- ? css`
- border-style: solid;
- border-width: 2px;
- border-left-color: ${({ theme }) => theme.borderDarkest};
- border-top-color: ${({ theme }) => theme.borderDarkest};
- border-right-color: ${({ theme }) => theme.borderLightest};
- border-bottom-color: ${({ theme }) => theme.borderLightest};
- box-shadow: ${props => props.shadow && `${shadow}, `} inset 1px 1px 0px
- 1px ${({ theme }) => theme.borderDark},
- inset -1px -1px 0 1px ${({ theme }) => theme.borderLight};
- `
- : css`
- border-style: solid;
- border-width: 2px;
- border-left-color: ${({ theme }) =>
- windowBorders ? theme.borderLight : theme.borderLightest};
- border-top-color: ${({ theme }) =>
- windowBorders ? theme.borderLight : theme.borderLightest};
- border-right-color: ${({ theme }) => theme.borderDarkest};
- border-bottom-color: ${({ theme }) => theme.borderDarkest};
- box-shadow: ${props => props.shadow && `${shadow}, `} inset 1px 1px 0px
- 1px
- ${({ theme }) =>
- windowBorders ? theme.borderLightest : theme.borderLight},
- inset -1px -1px 0 1px ${({ theme }) => theme.borderDark};
- `;
+ style = 'button'
+}: { invert?: boolean; style?: BorderStyles } = {}) => {
+ const borders = {
+ topLeftOuter: invert ? 'bottomRightOuter' : 'topLeftOuter',
+ topLeftInner: invert ? 'bottomRightInner' : 'topLeftInner',
+ bottomRightInner: invert ? 'topLeftInner' : 'bottomRightInner',
+ bottomRightOuter: invert ? 'topLeftOuter' : 'bottomRightOuter'
+ } as const;
+ return css`
+ border-style: solid;
+ border-width: 2px;
+ border-left-color: ${({ theme }) =>
+ theme[borderStyles[style][borders.topLeftOuter]]};
+ border-top-color: ${({ theme }) =>
+ theme[borderStyles[style][borders.topLeftOuter]]};
+ border-right-color: ${({ theme }) =>
+ theme[borderStyles[style][borders.bottomRightOuter]]};
+ border-bottom-color: ${({ theme }) =>
+ theme[borderStyles[style][borders.bottomRightOuter]]};
+ box-shadow: ${({ theme, shadow: hasShadow }) =>
+ createInnerBorderWithShadow({
+ theme,
+ topLeftInner: borderStyles[style][borders.topLeftInner],
+ bottomRightInner: borderStyles[style][borders.bottomRightInner],
+ hasShadow
+ })};
+ `;
+};
+/** @deprecated Use `createBorderStyles` instead */
export const createWellBorderStyles = (invert = false) =>
- invert
- ? css`
- border-style: solid;
- border-width: 2px;
- border-left-color: ${({ theme }) => theme.borderDark};
- border-top-color: ${({ theme }) => theme.borderDark};
- border-right-color: ${({ theme }) => theme.borderLightest};
- border-bottom-color: ${({ theme }) => theme.borderLightest};
- `
- : css`
- border-style: solid;
- border-width: 2px;
- border-left-color: ${({ theme }) => theme.borderLightest};
- border-top-color: ${({ theme }) => theme.borderLightest};
- border-right-color: ${({ theme }) => theme.borderDark};
- border-bottom-color: ${({ theme }) => theme.borderDark};
- `;
+ createBorderStyles({
+ invert: !invert,
+ style: 'status'
+ });
export const focusOutline = () => css`
outline: 2px dotted ${({ theme }) => theme.materialText};
@@ -141,7 +228,7 @@ export const createScrollbars = (variant = 'default') => css`
${createBoxStyles()}
${variant === 'flat'
? createFlatBoxStyles()
- : createBorderStyles({ windowBorders: true })}
+ : createBorderStyles({ style: 'window' })}
outline-offset: -2px;
}
@@ -152,7 +239,7 @@ export const createScrollbars = (variant = 'default') => css`
${createBoxStyles()}
${variant === 'flat'
? createFlatBoxStyles()
- : createBorderStyles({ windowBorders: true })}
+ : createBorderStyles({ style: 'window' })}
display: block;
outline-offset: -2px;
height: 26px;
@@ -165,7 +252,7 @@ export const createScrollbars = (variant = 'default') => css`
::-webkit-scrollbar-button:active {
background-position: 0 1px;
${variant === 'default'
- ? createBorderStyles({ windowBorders: true, invert: true })
+ ? createBorderStyles({ style: 'window', invert: true })
: ''}
}
diff --git a/src/index.ts b/src/index.ts
index 93e0ea6..04e603e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -11,13 +11,13 @@ export * from './Checkbox/Checkbox';
export * from './ColorInput/ColorInput';
export * from './Counter/Counter';
export * from './DatePicker/DatePicker';
+export * from './Frame/Frame';
export * from './GroupBox/GroupBox';
export * from './Handle/Handle';
export * from './Hourglass/Hourglass';
export * from './MenuList/MenuList';
export * from './Monitor/Monitor';
export * from './NumberInput/NumberInput';
-export * from './Panel/Panel';
export * from './ProgressBar/ProgressBar';
export * from './Radio/Radio';
export * from './ScrollView/ScrollView';
@@ -48,4 +48,5 @@ export * from './legacy/Fieldset';
export * from './legacy/List';
export * from './legacy/ListItem';
export * from './legacy/NumberField';
+export * from './legacy/Panel';
export * from './legacy/Progress';
diff --git a/src/legacy/Panel.tsx b/src/legacy/Panel.tsx
new file mode 100644
index 0000000..b317f56
--- /dev/null
+++ b/src/legacy/Panel.tsx
@@ -0,0 +1,7 @@
+import { Frame, FrameProps } from '../Frame/Frame';
+
+/** @deprecated Use `FrameProps` */
+export type PanelProps = FrameProps;
+
+/** @deprecated Use `Frame` */
+export const Panel = Frame;