Skip to content

Commit

Permalink
fix: improve menu, themeprovider
Browse files Browse the repository at this point in the history
  • Loading branch information
LexSwed committed Oct 3, 2020
1 parent 1975991 commit 9e01147
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 211 deletions.
9 changes: 9 additions & 0 deletions link.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
yarn install
yarn link
cd node_modules/react
yarn link
cd ../../node_modules/react-dom
yarn link
cd $1
yarn link react
yarn link react-dom
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"access": "public"
},
"scripts": {
"build": "microbundle build --jsx React.createElement --no-compress --entry src/lib/index.ts --output build/index.js --no-pkg-main --tsconfig tsconfig.lib.json",
"build": "microbundle build --jsx React.createElement --compress --entry src/lib/index.ts --output build/index.js --no-pkg-main --tsconfig tsconfig.lib.json",
"start": "microbundle watch --jsx React.createElement --no-compress --entry src/lib/index.ts --output build/index.js --no-pkg-main --tsconfig tsconfig.lib.json --format modern",
"dev": "yarn run dokz:dev",
"lint": "eslint src",
Expand All @@ -35,9 +35,9 @@
},
"dependencies": {
"@popperjs/core": "^2.5.3",
"@react-aria/button": "^3.2.1",
"@react-aria/focus": "^3.2.1",
"@stitches/react": "^0.0.3-canary.1",
"@react-aria/button": "^3.2.2",
"@react-aria/focus": "^3.2.2",
"@stitches/react": "^0.0.3-canary.3",
"react-uid": "^2.3.0"
},
"devDependencies": {
Expand All @@ -46,17 +46,17 @@
"@emotion/styled": "^10.0.27",
"@types/jest": "^26.0.14",
"@types/node": "^14.11.2",
"@types/react": "^16.9.49",
"@types/react": "^16.9.50",
"@typescript-eslint/eslint-plugin": "^4.3.0",
"@typescript-eslint/parser": "^4.3.0",
"babel-eslint": "^10.1.0",
"dokz": "^1.0.77",
"dokz": "^1.0.79",
"eslint": "^7.10.0",
"eslint-config-react-app": "^5.2.1",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.21.2",
"eslint-plugin-react": "^7.21.3",
"eslint-plugin-react-hooks": "^4.1.2",
"husky": "^4.3.0",
"jest": "^26.4.2",
Expand Down
3 changes: 2 additions & 1 deletion src/lib/Menu/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Item = styled(Inline, {
'fontSize': '$sm',
'lineHeight': 1,
'display': 'flex',
'alignItems': 'center',
'height': '$8',
'cursor': 'pointer',
'br': '$sm',
Expand Down Expand Up @@ -61,7 +62,7 @@ const MenuItem = React.forwardRef<HTMLLIElement, Props>(({ action, ...props }, r
);

return (
<Item {...props} onClick={onClick} as="li" tabIndex={props.disabled ? undefined : -1} role="menuitem" ref={refs} />
<Item as="li" {...props} onClick={onClick} tabIndex={props.disabled ? undefined : -1} role="menuitem" ref={refs} />
);
});

Expand Down
5 changes: 5 additions & 0 deletions src/lib/Menu/MenuList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const List = styled('ul', {
br: '$md',
border: '1px solid $gray200',
outline: 'none',
pl: 0,
paddingInlineStart: 0,
});

const UlList: React.FC<UlListProps> = (ulProps) => {
Expand Down Expand Up @@ -40,6 +42,7 @@ const MenuList: React.FC<UlListProps> = (props) => {
export default MenuList;

const focusOptions: FocusManagerOptions = { wrap: true };

function useMenuProps(props: UlListProps): UlListProps {
const { popoverRef, seed, onAction } = useMenu();
const { close, stateRef } = useMenuControlState();
Expand Down Expand Up @@ -82,6 +85,8 @@ function useMenuProps(props: UlListProps): UlListProps {
},
'Escape': (event) => {
if (event.defaultPrevented) return;
event.preventDefault();
event.stopPropagation();
close();
},
'Tab': (event) => {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/Menu/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default as Menu } from './Menu';
export { default } from './Menu';
46 changes: 21 additions & 25 deletions src/lib/Menu/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import React, { useContext, createContext, useEffect, useMemo, useRef, useCallba
import { Options, createPopper, Instance, VirtualElement } from '@popperjs/core';
import { useUIDSeed } from 'react-uid';

type MenuStaticContextValue = ReturnType<typeof usePopper> & {
type MenuStaticContextValue = {
triggerRef: React.RefObject<HTMLElement>;
popoverRef: React.RefObject<HTMLElement>;
seed: ReturnType<typeof useUIDSeed>;
onAction?: (key: string) => void;
};
Expand Down Expand Up @@ -30,24 +32,29 @@ const options: Options = {
},
],
};

export const MenuProvider: React.FC<{
onAction?: (key: string) => void;
}> = ({ children, onAction }) => {
const seed = useUIDSeed();
const refs = usePopper(options);
const triggerRef = useRef<HTMLElement>(null);
const popoverRef = usePopper(triggerRef, options);

const menuContextValue = useMemo(
() => ({
...refs,
triggerRef,
popoverRef,
seed,
onAction,
}),
[refs, onAction, seed]
[triggerRef, popoverRef, seed, onAction]
);

return (
<menuContext.Provider value={menuContextValue}>
<MenuStateProvider>{children}</MenuStateProvider>
<MenuStateProvider>
<>{children}</>
</MenuStateProvider>
</menuContext.Provider>
);
};
Expand Down Expand Up @@ -78,9 +85,9 @@ export function useMenuControlState() {
}

function usePopper(
triggerRef: React.RefObject<Element | VirtualElement>,
options?: Partial<Options>
): { triggerRef: React.RefObject<Element | VirtualElement>; popoverRef: React.RefObject<HTMLElement> } {
const triggerRef = useRef<Element | VirtualElement>(null);
): React.RefObject<HTMLElement> {
const popoverRef = useRef<HTMLElement>(null);
const popperInstanceRef = useRef<Instance>();

Expand All @@ -89,7 +96,7 @@ function usePopper(

popperInstanceRef.current?.destroy();
popperInstanceRef.current = createPopper(triggerRef.current, popoverRef.current, options);
}, [options]);
}, [triggerRef, options]);

useEffect(() => {
return () => {
Expand All @@ -99,26 +106,15 @@ function usePopper(

return useMemo(() => {
return {
triggerRef: {
get current() {
return triggerRef.current;
},
set current(node) {
(triggerRef as React.MutableRefObject<any>).current = node;
instantiatePopper();
},
get current() {
return popoverRef.current;
},
popoverRef: {
get current() {
return popoverRef.current;
},
set current(node) {
(popoverRef as React.MutableRefObject<any>).current = node;
instantiatePopper();
},
set current(node) {
(popoverRef as React.MutableRefObject<any>).current = node;
instantiatePopper();
},
};
}, [triggerRef, popoverRef, instantiatePopper]);
}, [popoverRef, instantiatePopper]);
}

type InternalState = { lastKey: string | null; items: Map<HTMLLIElement, { action: string }> };
Expand Down
3 changes: 2 additions & 1 deletion src/lib/Portal/Portal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import ThemeProvider from '../ThemeProvider';

const Portal: React.FC = ({ children }) => {
const [mounted, setMounted] = useState(false);
Expand All @@ -8,7 +9,7 @@ const Portal: React.FC = ({ children }) => {

if (!mounted) return null;

return ReactDOM.createPortal(children, document.body);
return ReactDOM.createPortal(<ThemeProvider>{children}</ThemeProvider>, document.body);
};

export default Portal;
33 changes: 18 additions & 15 deletions src/lib/ThemeProvider/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import React from 'react';
import React, { createContext, useContext } from 'react';
import Box from '../Box';
import { themes } from '../theme/themes';
import { joinNonEmpty } from '../utils';

type Theme = keyof typeof themes;

type Props = {
theme: keyof typeof themes;
theme?: Theme;
};

const themeContext = createContext<Props['theme']>('default');

const ThemeProvider: React.FC<Props> = ({ theme, children }) => {
const themeClass = theme in themes ? themes[theme] : theme;
const contextTheme = useContext(themeContext);
const themeClass = themes[(theme as Theme) || (contextTheme as Theme)];

if (!themeClass) {
return <>{children}</>;
}

return (
<>
{React.Children.map(children, (child) =>
React.isValidElement(child) ? (
React.cloneElement(child, { className: joinNonEmpty(child.props.className, themeClass) })
) : (
<Box className={themeClass} as="span">
{child}
</Box>
)
)}
</>
<themeContext.Provider value={theme}>
<Box className={themeClass} as="span">
{children}
</Box>
</themeContext.Provider>
);
};

Expand Down
2 changes: 1 addition & 1 deletion src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export { default as Stack } from './Stack';
export { default as Text } from './Text';
export { default as TextLink } from './TextLink';

export * from './Menu';
export { default as Menu } from './Menu';

export { default as ThemeProvider } from './ThemeProvider';
export { theme, styled, css } from './stitches.config';
Expand Down
28 changes: 14 additions & 14 deletions src/lib/stitches.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,55 +79,55 @@ export const { styled, css } = createStyled({
screen: (rule) => `@media (min-width: 1280px) { ${rule} }`,
},
utils: {
p: (value: keyof Theme['space'] | (string & {})) => ({
p: (value: keyof Theme['space'] | (number | (string & {}))) => ({
paddingTop: value,
paddingBottom: value,
paddingLeft: value,
paddingRight: value,
}),
pt: (value: keyof Theme['space'] | (string & {})) => ({
pt: (value: keyof Theme['space'] | (number | (string & {}))) => ({
paddingTop: value,
}),
pr: (value: keyof Theme['space'] | (string & {})) => ({
pr: (value: keyof Theme['space'] | (number | (string & {}))) => ({
paddingRight: value,
}),
pb: (value: keyof Theme['space'] | (string & {})) => ({
pb: (value: keyof Theme['space'] | (number | (string & {}))) => ({
paddingBottom: value,
}),
pl: (value: keyof Theme['space'] | (string & {})) => ({
pl: (value: keyof Theme['space'] | (number | (string & {}))) => ({
paddingLeft: value,
}),
px: (value: keyof Theme['space'] | (string & {})) => ({
px: (value: keyof Theme['space'] | (number | (string & {}))) => ({
paddingLeft: value,
paddingRight: value,
}),
py: (value: keyof Theme['space'] | (string & {})) => ({
py: (value: keyof Theme['space'] | (number | (string & {}))) => ({
paddingTop: value,
paddingBottom: value,
}),
m: (value: keyof Theme['space'] | (string & {})) => ({
m: (value: keyof Theme['space'] | (number | (string & {}))) => ({
marginTop: value,
marginBottom: value,
marginLeft: value,
marginRight: value,
}),
mt: (value: keyof Theme['space'] | (string & {})) => ({
mt: (value: keyof Theme['space'] | (number | (string & {}))) => ({
marginTop: value,
}),
mr: (value: keyof Theme['space'] | (string & {})) => ({
mr: (value: keyof Theme['space'] | (number | (string & {}))) => ({
marginRight: value,
}),
mb: (value: keyof Theme['space'] | (string & {})) => ({
mb: (value: keyof Theme['space'] | (number | (string & {}))) => ({
marginBottom: value,
}),
ml: (value: keyof Theme['space'] | (string & {})) => ({
ml: (value: keyof Theme['space'] | (number | (string & {}))) => ({
marginLeft: value,
}),
mx: (value: keyof Theme['space'] | (string & {})) => ({
mx: (value: keyof Theme['space'] | (number | (string & {}))) => ({
marginLeft: value,
marginRight: value,
}),
my: (value: keyof Theme['space'] | (string & {})) => ({
my: (value: keyof Theme['space'] | (number | (string & {}))) => ({
marginTop: value,
marginBottom: value,
}),
Expand Down
26 changes: 24 additions & 2 deletions src/pages/components/Menu.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Menu
---

import { Playground } from 'dokz';
import { Box, Button, Menu, Icon } from '@fxtrot/ui';
import { Box, Button, Menu, Icon, ThemeProvider } from '@fxtrot/ui';
import { HiChevronDown } from 'react-icons/hi';

## Menu
Expand All @@ -18,7 +18,7 @@ For big lists use `action` prop on `Menu.Item` and `onAction` callback to handle
<Icon as={HiChevronDown} size="lg" />
</Menu.Button>
<Menu.List>
{Array(1000)
{Array(100)
.fill(null)
.map((el, i) => (
<Menu.Item key={i} action={`Item ${i}`}>
Expand All @@ -29,3 +29,25 @@ For big lists use `action` prop on `Menu.Item` and `onAction` callback to handle
</Menu>
</Box>
</Playground>

<Playground>
<ThemeProvider theme="teal">
<Box css={{ p: '$8' }}>
<Menu onAction={typeof window !== 'undefined' ? window.alert : null}>
<Menu.Button>
<span>Open menu</span>
<Icon as={HiChevronDown} size="lg" />
</Menu.Button>
<Menu.List>
{Array(100)
.fill(null)
.map((el, i) => (
<Menu.Item key={i} action={`Item ${i}`}>
Item {i}
</Menu.Item>
))}
</Menu.List>
</Menu>
</Box>
</ThemeProvider>
</Playground>
Loading

0 comments on commit 9e01147

Please sign in to comment.