Add Flipper logs to leftrail
Summary: This adds support for flipper logs in Sandy, including some theme adjustments. Did remove storage and showing of debug messages, as I noticed it tends to crash Flipper after a while since there are so many. Also added a fixed limit of only remembering last 1000 Also converted Toolbar and button with dropdown items to Sandy. Reviewed By: cekkaewnumchai Differential Revision: D23824528 fbshipit-source-id: b89d1182d4f14682251dbb482d93c2c009ddc7a4
This commit is contained in:
committed by
Facebook GitHub Bot
parent
191df465b7
commit
aea04dd0cf
@@ -12,8 +12,13 @@ import {Button, Toolbar, ButtonGroup, Layout} from '../ui';
|
||||
import React from 'react';
|
||||
import {Console, Hook} from 'console-feed';
|
||||
import type {Methods} from 'console-feed/lib/definitions/Methods';
|
||||
import type {Styles} from 'console-feed/lib/definitions/Styles';
|
||||
import {createState, useValue} from 'flipper-plugin';
|
||||
import {useLocalStorage} from '../utils/useLocalStorage';
|
||||
import {theme, useIsDarkMode} from '../sandy-chrome/theme';
|
||||
import {useIsSandy} from '../sandy-chrome/SandyContext';
|
||||
|
||||
const MAX_LOG_ITEMS = 1000;
|
||||
|
||||
const logsAtom = createState<any[]>([]);
|
||||
export const errorCounterAtom = createState(0);
|
||||
@@ -23,7 +28,12 @@ export function enableConsoleHook() {
|
||||
Hook(
|
||||
window.console,
|
||||
(log) => {
|
||||
logsAtom.set([...logsAtom.get(), log]);
|
||||
if (log.method === 'debug') {
|
||||
return; // See below, skip debug messages which are generated very aggressively by Flipper
|
||||
}
|
||||
const newLogs = logsAtom.get().slice(-MAX_LOG_ITEMS);
|
||||
newLogs.push(log);
|
||||
logsAtom.set(newLogs);
|
||||
if (log.method === 'error' || log.method === 'assert') {
|
||||
errorCounterAtom.set(errorCounterAtom.get() + 1);
|
||||
}
|
||||
@@ -39,7 +49,8 @@ function clearLogs() {
|
||||
|
||||
const allLogLevels: Methods[] = [
|
||||
'log',
|
||||
'debug',
|
||||
// 'debug', We typically don't want to allow users to enable the debug logs, as they are used very intensively by flipper itself,
|
||||
// making Flipper / console-feed. For debug level logging, use the Chrome devtools.
|
||||
'info',
|
||||
'warn',
|
||||
'error',
|
||||
@@ -54,6 +65,8 @@ const allLogLevels: Methods[] = [
|
||||
const defaultLogLevels: Methods[] = ['warn', 'error', 'table', 'assert'];
|
||||
|
||||
export function ConsoleLogs() {
|
||||
const isSandy = useIsSandy();
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const logs = useValue(logsAtom);
|
||||
const [logLevels, setLogLevels] = useLocalStorage<Methods[]>(
|
||||
'console-logs-loglevels',
|
||||
@@ -77,6 +90,8 @@ export function ConsoleLogs() {
|
||||
);
|
||||
}, [logLevels, setLogLevels]);
|
||||
|
||||
const styles = useMemo(() => buildTheme(isSandy), [isSandy]);
|
||||
|
||||
return (
|
||||
<Layout.Top scrollable>
|
||||
<Toolbar>
|
||||
@@ -87,7 +102,46 @@ export function ConsoleLogs() {
|
||||
<Button dropdown={dropdown}>Log Levels</Button>
|
||||
</ButtonGroup>
|
||||
</Toolbar>
|
||||
<Console logs={logs} filter={logLevels} variant="light" />
|
||||
<Console
|
||||
logs={logs}
|
||||
filter={logLevels}
|
||||
variant={isDarkMode || !isSandy ? 'dark' : 'light'}
|
||||
styles={styles}
|
||||
/>
|
||||
</Layout.Top>
|
||||
);
|
||||
}
|
||||
|
||||
function buildTheme(isSandy: boolean): Styles {
|
||||
if (!isSandy) {
|
||||
const bg = '#333';
|
||||
return {
|
||||
BASE_BACKGROUND_COLOR: bg,
|
||||
BASE_COLOR: 'white',
|
||||
LOG_BACKGROUND: bg,
|
||||
};
|
||||
}
|
||||
return {
|
||||
// See: https://github.com/samdenty/console-feed/blob/master/src/definitions/Styles.d.ts
|
||||
BASE_BACKGROUND_COLOR: 'transparent',
|
||||
BASE_COLOR: theme.textColorPrimary,
|
||||
LOG_COLOR: theme.textColorPrimary,
|
||||
LOG_BACKGROUND: 'transparent',
|
||||
LOG_INFO_BACKGROUND: 'transparent',
|
||||
LOG_COMMAND_BACKGROUND: 'transparent',
|
||||
LOG_RESULT_BACKGROUND: 'transparent',
|
||||
LOG_WARN_BACKGROUND: theme.warningColor,
|
||||
LOG_ERROR_BACKGROUND: theme.errorColor,
|
||||
LOG_INFO_COLOR: theme.textColorPrimary,
|
||||
LOG_COMMAND_COLOR: theme.textColorSecondary,
|
||||
LOG_RESULT_COLOR: theme.textColorSecondary,
|
||||
LOG_WARN_COLOR: 'white',
|
||||
LOG_ERROR_COLOR: 'white',
|
||||
LOG_INFO_BORDER: theme.dividerColor,
|
||||
LOG_COMMAND_BORDER: theme.dividerColor,
|
||||
LOG_RESULT_BORDER: theme.dividerColor,
|
||||
LOG_WARN_BORDER: theme.dividerColor,
|
||||
LOG_ERROR_BORDER: theme.dividerColor,
|
||||
LOG_BORDER: theme.dividerColor,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ exports[`SettingsSheet snapshot with nothing enabled 1`] = `
|
||||
TestDevicePlugin
|
||||
</span>
|
||||
<div
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<input
|
||||
checked={false}
|
||||
@@ -84,7 +84,7 @@ exports[`SettingsSheet snapshot with nothing enabled 1`] = `
|
||||
TestPlugin
|
||||
</span>
|
||||
<div
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<input
|
||||
checked={false}
|
||||
@@ -109,7 +109,7 @@ exports[`SettingsSheet snapshot with nothing enabled 1`] = `
|
||||
className="css-17wo7w2-View-FlexBox-FlexRow epz0qe20"
|
||||
>
|
||||
<div
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<div
|
||||
className="css-12n892b"
|
||||
@@ -188,7 +188,7 @@ exports[`SettingsSheet snapshot with one plugin enabled 1`] = `
|
||||
TestDevicePlugin
|
||||
</span>
|
||||
<div
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<input
|
||||
checked={false}
|
||||
@@ -229,7 +229,7 @@ exports[`SettingsSheet snapshot with one plugin enabled 1`] = `
|
||||
TestPlugin
|
||||
</span>
|
||||
<div
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<input
|
||||
checked={true}
|
||||
@@ -254,7 +254,7 @@ exports[`SettingsSheet snapshot with one plugin enabled 1`] = `
|
||||
className="css-17wo7w2-View-FlexBox-FlexRow epz0qe20"
|
||||
>
|
||||
<div
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<div
|
||||
className="css-12n892b"
|
||||
|
||||
@@ -27,7 +27,7 @@ exports[`ShareSheetPendingDialog is rendered with status update 1`] = `
|
||||
className="css-17wo7w2-View-FlexBox-FlexRow epz0qe20"
|
||||
>
|
||||
<div
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<div
|
||||
className="css-xing9h-StyledButton enfqd40"
|
||||
@@ -77,7 +77,7 @@ exports[`ShareSheetPendingDialog is rendered without status update 1`] = `
|
||||
className="css-17wo7w2-View-FlexBox-FlexRow epz0qe20"
|
||||
>
|
||||
<div
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
className="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<div
|
||||
className="css-xing9h-StyledButton enfqd40"
|
||||
|
||||
@@ -6,7 +6,7 @@ exports[`load PluginInstaller list 1`] = `
|
||||
class="css-13jp8bd-View-FlexBox-FlexColumn"
|
||||
>
|
||||
<div
|
||||
class="css-mx54fh-View-FlexBox-FlexRow-Toolbar e13mj6h80"
|
||||
class="css-1qqef1i-View-FlexBox-FlexRow-ToolbarContainer e13mj6h80"
|
||||
>
|
||||
<div
|
||||
class="css-awcbnc-View-FlexBox-SearchBox e271nro1"
|
||||
@@ -137,7 +137,7 @@ exports[`load PluginInstaller list 1`] = `
|
||||
World?
|
||||
</span>
|
||||
<div
|
||||
class="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
class="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<span
|
||||
class="css-ad6n9d-StyledLink e1mzoj7l0"
|
||||
@@ -204,7 +204,7 @@ exports[`load PluginInstaller list 1`] = `
|
||||
Hello?
|
||||
</span>
|
||||
<div
|
||||
class="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
class="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<span
|
||||
class="css-ad6n9d-StyledLink e1mzoj7l0"
|
||||
@@ -235,7 +235,7 @@ exports[`load PluginInstaller list 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="css-mx54fh-View-FlexBox-FlexRow-Toolbar e13mj6h80"
|
||||
class="css-1qqef1i-View-FlexBox-FlexRow-ToolbarContainer e13mj6h80"
|
||||
>
|
||||
<div
|
||||
class="css-1stmykz-View-FlexBox-FlexRow-Container ersmi541"
|
||||
@@ -301,7 +301,7 @@ exports[`load PluginInstaller list with one plugin installed 1`] = `
|
||||
class="css-13jp8bd-View-FlexBox-FlexColumn"
|
||||
>
|
||||
<div
|
||||
class="css-mx54fh-View-FlexBox-FlexRow-Toolbar e13mj6h80"
|
||||
class="css-1qqef1i-View-FlexBox-FlexRow-ToolbarContainer e13mj6h80"
|
||||
>
|
||||
<div
|
||||
class="css-awcbnc-View-FlexBox-SearchBox e271nro1"
|
||||
@@ -432,7 +432,7 @@ exports[`load PluginInstaller list with one plugin installed 1`] = `
|
||||
World?
|
||||
</span>
|
||||
<div
|
||||
class="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
class="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<span
|
||||
class="css-ad6n9d-StyledLink e1mzoj7l0"
|
||||
@@ -498,7 +498,7 @@ exports[`load PluginInstaller list with one plugin installed 1`] = `
|
||||
Hello?
|
||||
</span>
|
||||
<div
|
||||
class="css-te359u-View-FlexBox-Spacer e13mj6h81"
|
||||
class="css-te359u-View-FlexBox-Spacer e13mj6h82"
|
||||
/>
|
||||
<span
|
||||
class="css-ad6n9d-StyledLink e1mzoj7l0"
|
||||
@@ -529,7 +529,7 @@ exports[`load PluginInstaller list with one plugin installed 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="css-mx54fh-View-FlexBox-FlexRow-Toolbar e13mj6h80"
|
||||
class="css-1qqef1i-View-FlexBox-FlexRow-ToolbarContainer e13mj6h80"
|
||||
>
|
||||
<div
|
||||
class="css-1stmykz-View-FlexBox-FlexRow-Container ersmi541"
|
||||
|
||||
@@ -27,6 +27,10 @@ import {toggleLeftSidebarVisible} from '../reducers/application';
|
||||
import {theme} from './theme';
|
||||
import SettingsSheet from '../chrome/SettingsSheet';
|
||||
import WelcomeScreen from './WelcomeScreen';
|
||||
import {isStaticViewActive} from '../chrome/mainsidebar/sidebarUtils';
|
||||
import {ConsoleLogs, errorCounterAtom} from '../chrome/ConsoleLogs';
|
||||
import {setStaticView} from '../reducers/connections';
|
||||
import {useValue} from 'flipper-plugin';
|
||||
|
||||
const LeftRailContainer = styled(FlexColumn)({
|
||||
background: theme.backgroundDefault,
|
||||
@@ -109,10 +113,7 @@ export function LeftRail() {
|
||||
title="Notifications"
|
||||
/>
|
||||
<LeftRailDivider />
|
||||
<LeftRailButton
|
||||
icon={<FileExclamationOutlined />}
|
||||
title="Flipper Logs"
|
||||
/>
|
||||
<DebugLogsButton />
|
||||
</LeftRailSection>
|
||||
<LeftRailSection>
|
||||
<LeftRailButton
|
||||
@@ -159,6 +160,25 @@ function LeftSidebarToggleButton() {
|
||||
);
|
||||
}
|
||||
|
||||
function DebugLogsButton() {
|
||||
const staticView = useStore((state) => state.connections.staticView);
|
||||
const active = isStaticViewActive(staticView, ConsoleLogs);
|
||||
const errorCount = useValue(errorCounterAtom);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<LeftRailButton
|
||||
icon={<FileExclamationOutlined />}
|
||||
title="Flipper Logs"
|
||||
selected={active}
|
||||
count={errorCount}
|
||||
onClick={() => {
|
||||
dispatch(setStaticView(ConsoleLogs));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function ShowSettingsButton() {
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const onClose = useCallback(() => setShowSettings(false), []);
|
||||
|
||||
@@ -29,8 +29,10 @@ export function SandyApp({logger}: {logger: Logger}) {
|
||||
}, []);
|
||||
|
||||
const mainMenuVisible = useStore(
|
||||
(state) => state.application.leftSidebarVisible,
|
||||
(state) =>
|
||||
state.application.leftSidebarVisible && !state.connections.staticView,
|
||||
);
|
||||
const staticView = useStore((state) => state.connections.staticView);
|
||||
|
||||
return (
|
||||
<SandyContext.Provider value={true}>
|
||||
@@ -51,7 +53,13 @@ export function SandyApp({logger}: {logger: Logger}) {
|
||||
<Layout.Right initialSize={300} minSize={200}>
|
||||
<MainContentWrapper>
|
||||
<ContentContainer>
|
||||
<TemporarilyContent />
|
||||
{staticView ? (
|
||||
React.createElement(staticView, {
|
||||
logger: logger,
|
||||
})
|
||||
) : (
|
||||
<TemporarilyContent />
|
||||
)}
|
||||
</ContentContainer>
|
||||
</MainContentWrapper>
|
||||
<MainContentWrapper>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {useStore} from '../utils/useStore';
|
||||
|
||||
// Exposes all the variables defined in themes/base.less:
|
||||
|
||||
export const theme = {
|
||||
@@ -33,3 +35,14 @@ export const theme = {
|
||||
smallBody: '12px',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* This hook returns whether dark mode is currently being used.
|
||||
* Generally should be avoided in favor of using the above theme object,
|
||||
* which will provide colors that reflect the theme
|
||||
*/
|
||||
export function useIsDarkMode(): boolean {
|
||||
return useStore(
|
||||
(state) => state.settingsState.enableSandy && state.settingsState.darkMode,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React, {useContext, useState, useRef, useCallback} from 'react';
|
||||
import React, {useContext, useState, useRef, useCallback, useMemo} from 'react';
|
||||
import electron, {MenuItemConstructorOptions} from 'electron';
|
||||
import styled from '@emotion/styled';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
import {keyframes} from 'emotion';
|
||||
import {Button as AntdButton} from 'antd';
|
||||
import {Button as AntdButton, Dropdown, Menu} from 'antd';
|
||||
|
||||
import {colors} from './colors';
|
||||
import Glyph, {IconSize} from './Glyph';
|
||||
@@ -20,6 +20,8 @@ import {ButtonGroupContext} from './ButtonGroup';
|
||||
import {useStore} from '../../utils/useStore';
|
||||
import {useIsSandy} from '../../sandy-chrome/SandyContext';
|
||||
import type {ButtonProps} from 'antd/lib/button';
|
||||
import {DownOutlined, CheckOutlined} from '@ant-design/icons';
|
||||
import {theme} from '../../sandy-chrome/theme';
|
||||
|
||||
type ButtonType = 'primary' | 'success' | 'warning' | 'danger';
|
||||
|
||||
@@ -217,7 +219,7 @@ type Props = {
|
||||
/**
|
||||
* Type of button.
|
||||
*/
|
||||
type?: ButtonType;
|
||||
type?: ButtonType; // TODO: normalize to Sandy
|
||||
/**
|
||||
* Children.
|
||||
*/
|
||||
@@ -264,11 +266,15 @@ type Props = {
|
||||
* A simple button, used in many parts of the application.
|
||||
*/
|
||||
export default function Button(props: Props) {
|
||||
const isSandy = useIsSandy();
|
||||
return isSandy ? <SandyButton {...props} /> : <ClassicButton {...props} />;
|
||||
}
|
||||
|
||||
function ClassicButton(props: Props) {
|
||||
const windowIsFocused = useStore(
|
||||
(state) => state.application.windowIsFocused,
|
||||
);
|
||||
const inButtonGroup = useContext(ButtonGroupContext);
|
||||
const isSandy = useIsSandy();
|
||||
const [active, setActive] = useState(false);
|
||||
const [wasClosed, setWasClosed] = useState(false);
|
||||
|
||||
@@ -359,19 +365,7 @@ export default function Button(props: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
return isSandy ? (
|
||||
<AntdButton
|
||||
{...restProps}
|
||||
type={props.type === 'primary' ? 'primary' : 'default'}
|
||||
danger={props.type === 'danger'}
|
||||
ref={_ref}
|
||||
onClick={onClick}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseUp={onMouseUp}
|
||||
icon={iconComponent}>
|
||||
{children}
|
||||
</AntdButton>
|
||||
) : (
|
||||
return (
|
||||
<StyledButton
|
||||
{...restProps}
|
||||
ref={_ref as any}
|
||||
@@ -385,3 +379,108 @@ export default function Button(props: Props) {
|
||||
</StyledButton>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple button, used in many parts of the application.
|
||||
*/
|
||||
export function SandyButton({
|
||||
compact,
|
||||
disabled,
|
||||
icon,
|
||||
children,
|
||||
iconSize,
|
||||
iconVariant,
|
||||
dropdown,
|
||||
type,
|
||||
onClick,
|
||||
href,
|
||||
...restProps
|
||||
}: Props) {
|
||||
const [dropdownVisible, setDropdownVible] = useState(false);
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
if (disabled === true) {
|
||||
return;
|
||||
}
|
||||
onClick?.(e);
|
||||
if (href != null) {
|
||||
electron.shell.openExternal(href); // TODO: decouple from Electron
|
||||
}
|
||||
},
|
||||
[disabled, onClick, href],
|
||||
);
|
||||
const handleVisibleChange = useCallback((flag: boolean) => {
|
||||
setDropdownVible(flag);
|
||||
}, []);
|
||||
let iconComponent;
|
||||
if (icon != null) {
|
||||
iconComponent = (
|
||||
<Icon
|
||||
name={icon}
|
||||
size={iconSize || (compact === true ? 12 : 16)}
|
||||
color={theme.textColorPrimary}
|
||||
variant={iconVariant || 'filled'}
|
||||
hasText={Boolean(children)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const dropdownItems = useMemo(
|
||||
() =>
|
||||
dropdown && (
|
||||
<Menu>
|
||||
{dropdown!.map((item, idx) => (
|
||||
<Menu.Item
|
||||
onClick={(e) => {
|
||||
// @ts-ignore this event args are bound to electron, remove in the future
|
||||
item.click();
|
||||
if (item.checked !== undefined) {
|
||||
// keep the menu item for check lists
|
||||
e.domEvent.stopPropagation();
|
||||
e.domEvent.preventDefault();
|
||||
}
|
||||
}}
|
||||
disabled={item.enabled === false}
|
||||
icon={
|
||||
item.checked !== undefined && (
|
||||
<CheckOutlined
|
||||
style={{visibility: item.checked ? 'visible' : 'hidden'}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
key={idx}>
|
||||
{item.label}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
),
|
||||
[dropdown],
|
||||
);
|
||||
|
||||
const button = (
|
||||
<AntdButton
|
||||
/* Probably more properties need passing on, but lets be explicit about it */
|
||||
style={restProps.style}
|
||||
disabled={disabled}
|
||||
type={type === 'primary' ? 'primary' : 'default'}
|
||||
danger={type === 'danger'}
|
||||
onClick={handleClick}
|
||||
icon={iconComponent}>
|
||||
{children}
|
||||
{dropdown ? <DownOutlined /> : null}
|
||||
</AntdButton>
|
||||
);
|
||||
if (dropdown) {
|
||||
return (
|
||||
<Dropdown
|
||||
overlay={dropdownItems!}
|
||||
visible={dropdownVisible}
|
||||
onVisibleChange={handleVisibleChange}>
|
||||
{button}
|
||||
</Dropdown>
|
||||
);
|
||||
} else {
|
||||
return button;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
import React, {createContext} from 'react';
|
||||
import {useIsSandy} from '../../sandy-chrome/SandyContext';
|
||||
import {Space} from 'antd';
|
||||
|
||||
const ButtonGroupContainer = styled.div({
|
||||
display: 'inline-flex',
|
||||
@@ -33,7 +35,12 @@ export const ButtonGroupContext = createContext(false);
|
||||
* ```
|
||||
*/
|
||||
export default function ButtonGroup({children}: {children: React.ReactNode}) {
|
||||
return (
|
||||
const isSandy = useIsSandy(); // according to Ant design guides buttons should only be grouped if they are radios
|
||||
return isSandy ? (
|
||||
<ButtonGroupContext.Provider value={true}>
|
||||
<Space>{children}</Space>
|
||||
</ButtonGroupContext.Provider>
|
||||
) : (
|
||||
<ButtonGroupContext.Provider value={true}>
|
||||
<ButtonGroupContainer>{children}</ButtonGroupContainer>
|
||||
</ButtonGroupContext.Provider>
|
||||
|
||||
@@ -7,15 +7,19 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {colors} from './colors';
|
||||
import FlexRow from './FlexRow';
|
||||
import FlexBox from './FlexBox';
|
||||
import styled from '@emotion/styled';
|
||||
import {Space} from 'antd';
|
||||
import {useIsSandy} from '../../sandy-chrome/SandyContext';
|
||||
import {theme} from '../../sandy-chrome/theme';
|
||||
|
||||
/**
|
||||
* A toolbar.
|
||||
*/
|
||||
const Toolbar = styled(FlexRow)<{
|
||||
const ToolbarContainer = styled(FlexRow)<{
|
||||
position?: 'bottom' | 'top';
|
||||
compact?: boolean;
|
||||
}>((props) => ({
|
||||
@@ -36,11 +40,35 @@ const Toolbar = styled(FlexRow)<{
|
||||
padding: 6,
|
||||
width: '100%',
|
||||
}));
|
||||
Toolbar.displayName = 'Toolbar';
|
||||
ToolbarContainer.displayName = 'ToolbarContainer';
|
||||
|
||||
const SandyToolbarContainer = styled(Space)({
|
||||
width: '100%',
|
||||
padding: theme.space.small,
|
||||
boxShadow: `inset 0px -1px 0px ${theme.dividerColor}`,
|
||||
});
|
||||
|
||||
export const Spacer = styled(FlexBox)({
|
||||
flexGrow: 1,
|
||||
});
|
||||
Spacer.displayName = 'Spacer';
|
||||
|
||||
export default Toolbar;
|
||||
export default function Toolbar({
|
||||
children,
|
||||
style,
|
||||
...rest
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
position?: 'bottom' | 'top';
|
||||
compact?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
}) {
|
||||
const isSandy = useIsSandy();
|
||||
return isSandy ? (
|
||||
<SandyToolbarContainer style={style}>{children}</SandyToolbarContainer>
|
||||
) : (
|
||||
<ToolbarContainer style={style} {...rest}>
|
||||
{children}
|
||||
</ToolbarContainer>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user