StoryBook: Fully support themes in pop-up components
This commit is contained in:
parent
6a9d8b86d8
commit
0fc178d887
4 changed files with 71 additions and 11 deletions
|
@ -5,6 +5,7 @@ import classnames from 'classnames';
|
||||||
import * as styles from './styles.scss';
|
import * as styles from './styles.scss';
|
||||||
import messages from '../_locales/en/messages.json';
|
import messages from '../_locales/en/messages.json';
|
||||||
import { I18n } from '../sticker-creator/util/i18n';
|
import { I18n } from '../sticker-creator/util/i18n';
|
||||||
|
import { ThemedProvider } from '../ts/components/PopperRootContext';
|
||||||
|
|
||||||
addDecorator(withKnobs);
|
addDecorator(withKnobs);
|
||||||
|
|
||||||
|
@ -14,9 +15,13 @@ addDecorator((storyFn /* , context */) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.panel}>{contents}</div>
|
<div className={styles.panel}>{contents}</div>
|
||||||
<div className={classnames(styles.darkTheme, styles.panel, 'dark-theme')}>
|
<ThemedProvider themes={['dark']}>
|
||||||
{contents}
|
<div
|
||||||
</div>
|
className={classnames(styles.darkTheme, styles.panel, 'dark-theme')}
|
||||||
|
>
|
||||||
|
{contents}
|
||||||
|
</div>
|
||||||
|
</ThemedProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
"styleguide": "styleguidist server",
|
"styleguide": "styleguidist server",
|
||||||
"ready": "yarn clean-transpile && yarn grunt && yarn lint && yarn test-node && yarn test-electron && yarn lint-deps",
|
"ready": "yarn clean-transpile && yarn grunt && yarn lint && yarn test-node && yarn test-electron && yarn lint-deps",
|
||||||
"dev": "run-p --print-label dev:*",
|
"dev": "run-p --print-label dev:*",
|
||||||
|
"dev:grunt": "yarn grunt dev",
|
||||||
"dev:webpack": "NODE_ENV=development webpack-dev-server --hot",
|
"dev:webpack": "NODE_ENV=development webpack-dev-server --hot",
|
||||||
"dev:typed-scss": "yarn build:typed-scss -w",
|
"dev:typed-scss": "yarn build:typed-scss -w",
|
||||||
"dev:storybook": "start-storybook -p 6006 -s ./",
|
"dev:storybook": "start-storybook -p 6006 -s ./",
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
Props as EmojiPickerProps,
|
Props as EmojiPickerProps,
|
||||||
} from '../../ts/components/emoji/EmojiPicker';
|
} from '../../ts/components/emoji/EmojiPicker';
|
||||||
import { Emoji } from '../../ts/components/emoji/Emoji';
|
import { Emoji } from '../../ts/components/emoji/Emoji';
|
||||||
|
import { PopperRootContext } from '../../ts/components/PopperRootContext';
|
||||||
import { useI18n } from '../util/i18n';
|
import { useI18n } from '../util/i18n';
|
||||||
|
|
||||||
export type Mode = 'removable' | 'pick-emoji' | 'add';
|
export type Mode = 'removable' | 'pick-emoji' | 'add';
|
||||||
|
@ -117,12 +118,13 @@ export const StickerFrame = React.memo(
|
||||||
[timerRef]
|
[timerRef]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { createRoot, removeRoot } = React.useContext(PopperRootContext);
|
||||||
|
|
||||||
// Create popper root and handle outside clicks
|
// Create popper root and handle outside clicks
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (emojiPickerOpen) {
|
if (emojiPickerOpen) {
|
||||||
const root = document.createElement('div');
|
const root = createRoot();
|
||||||
setEmojiPopperRoot(root);
|
setEmojiPopperRoot(root);
|
||||||
document.body.appendChild(root);
|
|
||||||
const handleOutsideClick = ({ target }: MouseEvent) => {
|
const handleOutsideClick = ({ target }: MouseEvent) => {
|
||||||
if (!root.contains(target as Node)) {
|
if (!root.contains(target as Node)) {
|
||||||
setEmojiPickerOpen(false);
|
setEmojiPickerOpen(false);
|
||||||
|
@ -131,27 +133,39 @@ export const StickerFrame = React.memo(
|
||||||
document.addEventListener('click', handleOutsideClick);
|
document.addEventListener('click', handleOutsideClick);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.body.removeChild(root);
|
removeRoot(root);
|
||||||
document.removeEventListener('click', handleOutsideClick);
|
document.removeEventListener('click', handleOutsideClick);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return noop;
|
return noop;
|
||||||
}, [emojiPickerOpen, setEmojiPickerOpen, setEmojiPopperRoot]);
|
}, [
|
||||||
|
createRoot,
|
||||||
|
emojiPickerOpen,
|
||||||
|
removeRoot,
|
||||||
|
setEmojiPickerOpen,
|
||||||
|
setEmojiPopperRoot,
|
||||||
|
]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (mode !== 'pick-emoji' && image && previewActive) {
|
if (mode !== 'pick-emoji' && image && previewActive) {
|
||||||
const root = document.createElement('div');
|
const root = createRoot();
|
||||||
setPreviewPopperRoot(root);
|
setPreviewPopperRoot(root);
|
||||||
document.body.appendChild(root);
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.body.removeChild(root);
|
removeRoot(root);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return noop;
|
return noop;
|
||||||
}, [mode, image, previewActive, setPreviewPopperRoot]);
|
}, [
|
||||||
|
createRoot,
|
||||||
|
image,
|
||||||
|
mode,
|
||||||
|
previewActive,
|
||||||
|
removeRoot,
|
||||||
|
setPreviewPopperRoot,
|
||||||
|
]);
|
||||||
|
|
||||||
const [dragActive, setDragActive] = React.useState<boolean>(false);
|
const [dragActive, setDragActive] = React.useState<boolean>(false);
|
||||||
const containerClass = dragActive ? styles.dragActive : styles.container;
|
const containerClass = dragActive ? styles.dragActive : styles.container;
|
||||||
|
|
40
ts/components/PopperRootContext.tsx
Normal file
40
ts/components/PopperRootContext.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const makeApi = (themes?: Array<string>) => ({
|
||||||
|
createRoot: () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
|
||||||
|
if (themes) {
|
||||||
|
themes.forEach(theme => {
|
||||||
|
div.classList.add(`${theme}-theme`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.appendChild(div);
|
||||||
|
|
||||||
|
return div;
|
||||||
|
},
|
||||||
|
removeRoot: (root: HTMLElement) => {
|
||||||
|
document.body.removeChild(root);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PopperRootContext = React.createContext(makeApi());
|
||||||
|
|
||||||
|
export type ThemedProviderProps = {
|
||||||
|
themes?: Array<string>;
|
||||||
|
children?: React.ReactChildren;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ThemedProvider: React.FunctionComponent<ThemedProviderProps> = ({
|
||||||
|
themes,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const api = React.useMemo(() => makeApi(themes), [themes]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopperRootContext.Provider value={api}>
|
||||||
|
{children}
|
||||||
|
</PopperRootContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in a new issue