Change star concept to enabled
Summary: The 'starring' concept of plugins no longer covers the meaning of 'starring', as unstarred plugins will no longer receive data from background plugins, not be available in support request forms due to a lack of data etc. So this diff renames the feature to 'enabled'. Also fixed an issue where selecting a non-enabled plugin wouldn't show it in the sidebar if the additional plugins are collapsed. To make disabled plugins more clear, they are . now always rendered in gray. The toggle button now delays its effect for better visual feedback - [x] update side bar styling - [x] remove bottom bar warning - [x] details screen - [ ] only open connection for active plugins (will be done in a next diff) - [x] check archived / imported devices - [x] make sure device plugins work correctly - [x] check without GK's Reviewed By: jknoxville Differential Revision: D19470326 fbshipit-source-id: 9160a3434287561f56b1b741d5ba282ab6063ea8
This commit is contained in:
committed by
Facebook Github Bot
parent
604511715b
commit
23625f7a89
@@ -28,7 +28,12 @@ import {
|
||||
VBox,
|
||||
View,
|
||||
} from 'flipper';
|
||||
import {StaticView, setStaticView} from './reducers/connections';
|
||||
import {
|
||||
StaticView,
|
||||
setStaticView,
|
||||
pluginIsStarred,
|
||||
starPlugin,
|
||||
} from './reducers/connections';
|
||||
import React, {PureComponent} from 'react';
|
||||
import {connect, ReactReduxContext} from 'react-redux';
|
||||
import {setPluginState} from './reducers/pluginStates';
|
||||
@@ -38,6 +43,7 @@ import {activateMenuItems} from './MenuBar';
|
||||
import {Message} from './reducers/pluginMessageQueue';
|
||||
import {Idler} from './utils/Idler';
|
||||
import {processMessageQueue} from './utils/messageQueue';
|
||||
import {ToggleButton, SmallText} from './ui';
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
width: 0,
|
||||
@@ -95,6 +101,7 @@ type StateFromProps = {
|
||||
selectedApp: string | null;
|
||||
isArchivedDevice: boolean;
|
||||
pendingMessages: Message[] | undefined;
|
||||
pluginIsEnabled: boolean;
|
||||
};
|
||||
|
||||
type DispatchFromProps = {
|
||||
@@ -105,6 +112,7 @@ type DispatchFromProps = {
|
||||
}) => any;
|
||||
setPluginState: (payload: {pluginKey: string; state: any}) => void;
|
||||
setStaticView: (payload: StaticView) => void;
|
||||
starPlugin: typeof starPlugin;
|
||||
};
|
||||
|
||||
type Props = StateFromProps & DispatchFromProps & OwnProps;
|
||||
@@ -167,12 +175,18 @@ class PluginContainer extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
processMessageQueue() {
|
||||
const {pluginKey, pendingMessages, activePlugin} = this.props;
|
||||
const {
|
||||
pluginKey,
|
||||
pendingMessages,
|
||||
activePlugin,
|
||||
pluginIsEnabled,
|
||||
} = this.props;
|
||||
if (pluginKey !== this.pluginBeingProcessed) {
|
||||
this.pluginBeingProcessed = pluginKey;
|
||||
this.cancelCurrentQueue();
|
||||
this.setState({progress: {current: 0, total: 0}});
|
||||
if (
|
||||
pluginIsEnabled &&
|
||||
activePlugin &&
|
||||
activePlugin.persistedStateReducer &&
|
||||
pluginKey &&
|
||||
@@ -200,16 +214,62 @@ class PluginContainer extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {activePlugin, pluginKey, target, pendingMessages} = this.props;
|
||||
const {
|
||||
activePlugin,
|
||||
pluginKey,
|
||||
target,
|
||||
pendingMessages,
|
||||
pluginIsEnabled,
|
||||
} = this.props;
|
||||
if (!activePlugin || !target || !pluginKey) {
|
||||
console.warn(`No selected plugin. Rendering empty!`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!pluginIsEnabled) {
|
||||
return this.renderPluginEnabler();
|
||||
}
|
||||
if (!pendingMessages || pendingMessages.length === 0) {
|
||||
return this.renderPlugin();
|
||||
} else {
|
||||
return this.renderPluginLoader();
|
||||
}
|
||||
return this.renderPluginLoader();
|
||||
}
|
||||
|
||||
renderPluginEnabler() {
|
||||
const activePlugin = this.props.activePlugin!;
|
||||
return (
|
||||
<View grow>
|
||||
<Waiting>
|
||||
<VBox>
|
||||
<FlexRow>
|
||||
<Label
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
color: colors.light30,
|
||||
textTransform: 'uppercase',
|
||||
}}>
|
||||
{activePlugin.title}
|
||||
</Label>
|
||||
</FlexRow>
|
||||
</VBox>
|
||||
<VBox>
|
||||
<ToggleButton
|
||||
toggled={false}
|
||||
onClick={() => {
|
||||
this.props.starPlugin({
|
||||
selectedPlugin: activePlugin.id,
|
||||
selectedApp: (this.props.target as Client).query.app,
|
||||
});
|
||||
}}
|
||||
large
|
||||
/>
|
||||
</VBox>
|
||||
<VBox>
|
||||
<SmallText>Click to enable this plugin</SmallText>
|
||||
</VBox>
|
||||
</Waiting>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderPluginLoader() {
|
||||
@@ -319,6 +379,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
selectedApp,
|
||||
clients,
|
||||
deepLinkPayload,
|
||||
userStarredPlugins,
|
||||
},
|
||||
pluginStates,
|
||||
plugins: {devicePlugins, clientPlugins},
|
||||
@@ -330,24 +391,33 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
| typeof FlipperDevicePlugin
|
||||
| typeof FlipperPlugin
|
||||
| null = null;
|
||||
let pluginIsEnabled = false;
|
||||
|
||||
if (selectedPlugin) {
|
||||
activePlugin = devicePlugins.get(selectedPlugin) || null;
|
||||
target = selectedDevice;
|
||||
if (selectedDevice && activePlugin) {
|
||||
pluginKey = getPluginKey(selectedDevice.serial, activePlugin.id);
|
||||
pluginIsEnabled = true;
|
||||
} else {
|
||||
target =
|
||||
clients.find((client: Client) => client.id === selectedApp) || null;
|
||||
activePlugin = clientPlugins.get(selectedPlugin) || null;
|
||||
if (activePlugin && target) {
|
||||
pluginKey = getPluginKey(target.id, activePlugin.id);
|
||||
pluginIsEnabled = pluginIsStarred(
|
||||
{selectedApp, userStarredPlugins},
|
||||
activePlugin.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
const isArchivedDevice = !selectedDevice
|
||||
? false
|
||||
: selectedDevice instanceof ArchivedDevice;
|
||||
if (isArchivedDevice) {
|
||||
pluginIsEnabled = true;
|
||||
}
|
||||
|
||||
const pendingMessages = pluginKey
|
||||
? pluginMessageQueue[pluginKey]
|
||||
@@ -362,6 +432,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
isArchivedDevice,
|
||||
selectedApp: selectedApp || null,
|
||||
pendingMessages,
|
||||
pluginIsEnabled,
|
||||
};
|
||||
return s;
|
||||
},
|
||||
@@ -369,5 +440,6 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
setPluginState,
|
||||
selectPlugin,
|
||||
setStaticView,
|
||||
starPlugin,
|
||||
},
|
||||
)(PluginContainer);
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
*/
|
||||
|
||||
import {colors} from '../ui/components/colors';
|
||||
import {styled, Glyph} from '../ui';
|
||||
import {styled} from '../ui';
|
||||
import {connect} from 'react-redux';
|
||||
import {State} from '../reducers';
|
||||
import React, {ReactElement} from 'react';
|
||||
import Text from '../ui/components/Text';
|
||||
import {pluginIsStarred} from '../reducers/connections';
|
||||
|
||||
const StatusBarContainer = styled(Text)({
|
||||
backgroundColor: colors.macOSTitleBarBackgroundBlur,
|
||||
@@ -48,39 +47,8 @@ export default connect<Props, void, {}, State>((state: State) => {
|
||||
} = state;
|
||||
if (statusMessages.length > 0) {
|
||||
return {statusMessage: statusMessages[statusMessages.length - 1]};
|
||||
} else if (isPreviewingBackgroundPlugin(state)) {
|
||||
return {
|
||||
statusMessage: (
|
||||
<>
|
||||
<Glyph
|
||||
name="caution-triangle"
|
||||
color={colors.light20}
|
||||
size={12}
|
||||
variant="filled"
|
||||
style={{marginRight: 8}}
|
||||
/>
|
||||
The current plugin would like to send messages while it is in the
|
||||
background. However, since this plugin is not starred, these messages
|
||||
will be dropped. Star this plugin to unlock its full capabilities.
|
||||
</>
|
||||
),
|
||||
};
|
||||
}
|
||||
return {
|
||||
statusMessage: null,
|
||||
};
|
||||
})(statusBarView);
|
||||
|
||||
function isPreviewingBackgroundPlugin(state: State): boolean {
|
||||
const {
|
||||
connections: {selectedApp, selectedPlugin},
|
||||
} = state;
|
||||
if (!selectedPlugin || !selectedApp) {
|
||||
return false;
|
||||
}
|
||||
const activePlugin = state.plugins.clientPlugins.get(selectedPlugin);
|
||||
if (!activePlugin || !activePlugin.persistedStateReducer) {
|
||||
return false;
|
||||
}
|
||||
return !pluginIsStarred(state.connections, selectedPlugin);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
CategoryName,
|
||||
PluginSidebarListItem,
|
||||
NoDevices,
|
||||
getFavoritePlugins,
|
||||
} from './sidebarUtils';
|
||||
|
||||
const SidebarButton = styled(Button)<{small?: boolean}>(({small}) => ({
|
||||
@@ -187,7 +188,7 @@ class MainSidebar extends PureComponent<Props, State> {
|
||||
)}
|
||||
</SidebarButton>
|
||||
</ListItem>
|
||||
{this.renderClientPlugins(client)}
|
||||
{this.renderClientPlugins(selectedDevice, client)}
|
||||
{uninitializedClients.map(entry => (
|
||||
<ListItem key={JSON.stringify(entry.client)}>
|
||||
{entry.client.appName}
|
||||
@@ -287,7 +288,7 @@ class MainSidebar extends PureComponent<Props, State> {
|
||||
));
|
||||
}
|
||||
|
||||
renderClientPlugins(client?: Client) {
|
||||
renderClientPlugins(device: BaseDevice, client?: Client) {
|
||||
if (!client) {
|
||||
return null;
|
||||
}
|
||||
@@ -301,6 +302,8 @@ class MainSidebar extends PureComponent<Props, State> {
|
||||
(p: typeof FlipperPlugin) => client.plugins.indexOf(p.id) > -1,
|
||||
);
|
||||
const favoritePlugins: FlipperPlugins = getFavoritePlugins(
|
||||
device,
|
||||
client,
|
||||
allPlugins,
|
||||
this.props.userStarredPlugins[client.query.app],
|
||||
true,
|
||||
@@ -317,7 +320,7 @@ class MainSidebar extends PureComponent<Props, State> {
|
||||
<>
|
||||
{favoritePlugins.length === 0 ? (
|
||||
<ListItem>
|
||||
<SmallText center>Star your favorite plugins!</SmallText>
|
||||
<SmallText center>No plugins enabled</SmallText>
|
||||
</ListItem>
|
||||
) : (
|
||||
<>
|
||||
@@ -361,6 +364,8 @@ class MainSidebar extends PureComponent<Props, State> {
|
||||
? this.renderPluginsByCategory(
|
||||
client,
|
||||
getFavoritePlugins(
|
||||
device,
|
||||
client,
|
||||
allPlugins,
|
||||
this.props.userStarredPlugins[client.query.app],
|
||||
false,
|
||||
@@ -375,20 +380,6 @@ class MainSidebar extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
function getFavoritePlugins(
|
||||
allPlugins: FlipperPlugins,
|
||||
starredPlugins: undefined | string[],
|
||||
favorite: boolean,
|
||||
): FlipperPlugins {
|
||||
if (!starredPlugins || !starredPlugins.length) {
|
||||
return favorite ? [] : allPlugins;
|
||||
}
|
||||
return allPlugins.filter(plugin => {
|
||||
const idx = starredPlugins.indexOf(plugin.id);
|
||||
return idx === -1 ? !favorite : favorite;
|
||||
});
|
||||
}
|
||||
|
||||
function groupPluginsByCategory(plugins: FlipperPlugins): PluginsByCategory {
|
||||
const sortedPlugins = plugins.slice().sort(sortPluginsByName);
|
||||
const byCategory: {[cat: string]: FlipperPlugins} = {};
|
||||
|
||||
@@ -60,6 +60,7 @@ import {
|
||||
NoClients,
|
||||
NoDevices,
|
||||
getColorByApp,
|
||||
getFavoritePlugins,
|
||||
} from './sidebarUtils';
|
||||
|
||||
type FlipperPlugins = typeof FlipperPlugin[];
|
||||
@@ -127,6 +128,13 @@ const SidebarSection: React.FC<{
|
||||
const [collapsed, setCollapsed] = useState(!!defaultCollapsed);
|
||||
color = color || colors.macOSTitleBarIconActive;
|
||||
|
||||
useEffect(() => {
|
||||
// if default collapsed changed to false, propagate that
|
||||
if (!defaultCollapsed && collapsed) {
|
||||
setCollapsed(!collapsed);
|
||||
}
|
||||
}, [defaultCollapsed]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarSectionButton
|
||||
@@ -395,20 +403,6 @@ function isStaticViewActive(
|
||||
return current && selected && current === selected;
|
||||
}
|
||||
|
||||
function getFavoritePlugins(
|
||||
allPlugins: FlipperPlugins,
|
||||
starredPlugins: undefined | string[],
|
||||
favorite: boolean,
|
||||
): FlipperPlugins {
|
||||
if (!starredPlugins || !starredPlugins.length) {
|
||||
return favorite ? [] : allPlugins;
|
||||
}
|
||||
return allPlugins.filter(plugin => {
|
||||
const idx = starredPlugins.indexOf(plugin.id);
|
||||
return idx === -1 ? !favorite : favorite;
|
||||
});
|
||||
}
|
||||
|
||||
function groupPluginsByCategory(plugins: FlipperPlugins): PluginsByCategory {
|
||||
const sortedPlugins = plugins.slice().sort(sortPluginsByName);
|
||||
const byCategory: {[cat: string]: FlipperPlugins} = {};
|
||||
@@ -511,6 +505,8 @@ const PluginList = memo(function PluginList({
|
||||
(p: typeof FlipperPlugin) => client.plugins.indexOf(p.id) > -1,
|
||||
);
|
||||
const favoritePlugins: FlipperPlugins = getFavoritePlugins(
|
||||
device,
|
||||
client,
|
||||
allPlugins,
|
||||
userStarredPlugins[client.query.app],
|
||||
true,
|
||||
@@ -529,7 +525,7 @@ const PluginList = memo(function PluginList({
|
||||
color={getColorByApp(client.query.app)}>
|
||||
{favoritePlugins.length === 0 ? (
|
||||
<ListItem>
|
||||
<SmallText center>Star your favorite plugins!</SmallText>
|
||||
<SmallText center>No plugins enabled</SmallText>
|
||||
</ListItem>
|
||||
) : (
|
||||
<PluginsByCategory
|
||||
@@ -563,6 +559,8 @@ const PluginList = memo(function PluginList({
|
||||
client={client}
|
||||
device={device}
|
||||
plugins={getFavoritePlugins(
|
||||
device,
|
||||
client,
|
||||
allPlugins,
|
||||
userStarredPlugins[client.query.app],
|
||||
false,
|
||||
@@ -624,7 +622,7 @@ const PluginsByCategory = memo(function PluginsByCategory({
|
||||
plugin={plugin}
|
||||
app={client.query.app}
|
||||
onFavorite={() => onFavorite(plugin.id)}
|
||||
starred={starred}
|
||||
starred={device.isArchived ? undefined : starred}
|
||||
/>
|
||||
))}
|
||||
</Fragment>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
FlexBox,
|
||||
colors,
|
||||
@@ -17,32 +18,39 @@ import {
|
||||
FlexColumn,
|
||||
LoadingIndicator,
|
||||
FlipperBasePlugin,
|
||||
StarButton,
|
||||
ToggleButton,
|
||||
brandColors,
|
||||
Spacer,
|
||||
Heading,
|
||||
Client,
|
||||
BaseDevice,
|
||||
} from 'flipper';
|
||||
import React, {useState, useCallback} from 'react';
|
||||
import {StaticView} from '../../reducers/connections';
|
||||
import {BackgroundColorProperty} from 'csstype';
|
||||
|
||||
export type FlipperPlugins = typeof FlipperPlugin[];
|
||||
export type PluginsByCategory = [string, FlipperPlugins][];
|
||||
|
||||
export const ListItem = styled.div<{active?: boolean}>(({active}) => ({
|
||||
paddingLeft: 10,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: 6,
|
||||
flexShrink: 0,
|
||||
backgroundColor: active ? colors.macOSTitleBarIconSelected : 'none',
|
||||
color: active ? colors.white : colors.macOSSidebarSectionItem,
|
||||
lineHeight: '25px',
|
||||
padding: '0 10px',
|
||||
'&[disabled]': {
|
||||
color: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
}));
|
||||
export const ListItem = styled.div<{active?: boolean; disabled?: boolean}>(
|
||||
({active, disabled}) => ({
|
||||
paddingLeft: 10,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: 6,
|
||||
flexShrink: 0,
|
||||
backgroundColor: active ? colors.macOSTitleBarIconSelected : 'none',
|
||||
color: disabled
|
||||
? 'rgba(0, 0, 0, 0.5)'
|
||||
: active
|
||||
? colors.white
|
||||
: colors.macOSSidebarSectionItem,
|
||||
lineHeight: '25px',
|
||||
padding: '0 10px',
|
||||
'&[disabled]': {
|
||||
color: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
export function PluginIcon({
|
||||
isActive,
|
||||
@@ -143,30 +151,29 @@ export const PluginSidebarListItem: React.FC<{
|
||||
helpRef?: any;
|
||||
provided?: any;
|
||||
onFavorite?: () => void;
|
||||
starred?: boolean;
|
||||
starred?: boolean; // undefined means: not starrable
|
||||
}> = function(props) {
|
||||
const {isActive, plugin, onFavorite, starred} = props;
|
||||
const iconColor = getColorByApp(props.app);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
const onEnter = useCallback(() => setHovered(true), []);
|
||||
const onLeave = useCallback(() => setHovered(false), []);
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
active={isActive}
|
||||
onClick={props.onClick}
|
||||
onMouseEnter={onEnter}
|
||||
onMouseLeave={onLeave}>
|
||||
disabled={starred === false}>
|
||||
<PluginIcon
|
||||
isActive={isActive}
|
||||
name={plugin.icon || 'apps'}
|
||||
backgroundColor={iconColor}
|
||||
backgroundColor={starred === false ? colors.light20 : iconColor}
|
||||
color={colors.white}
|
||||
/>
|
||||
<PluginName>{plugin.title || plugin.id}</PluginName>
|
||||
{starred !== undefined && (hovered || isActive) && (
|
||||
<StarButton onStar={onFavorite!} starred={starred} />
|
||||
{starred !== undefined && (!starred || isActive) && (
|
||||
<ToggleButton
|
||||
onClick={onFavorite}
|
||||
toggled={starred}
|
||||
tooltip="Click to enable / disable this plugin"
|
||||
/>
|
||||
)}
|
||||
</ListItem>
|
||||
);
|
||||
@@ -222,3 +229,28 @@ export const NoClients = () => (
|
||||
No clients connected
|
||||
</ListItem>
|
||||
);
|
||||
|
||||
export function getFavoritePlugins(
|
||||
device: BaseDevice,
|
||||
client: Client,
|
||||
allPlugins: FlipperPlugins,
|
||||
starredPlugins: undefined | string[],
|
||||
returnFavoredPlugins: boolean, // if false, unfavoried plugins are returned
|
||||
): FlipperPlugins {
|
||||
if (device.isArchived) {
|
||||
if (!returnFavoredPlugins) {
|
||||
return [];
|
||||
}
|
||||
// for archived plugins, all stored plugins are enabled
|
||||
return allPlugins.filter(
|
||||
plugin => client.plugins.indexOf(plugin.id) !== -1,
|
||||
);
|
||||
}
|
||||
if (!starredPlugins || !starredPlugins.length) {
|
||||
return returnFavoredPlugins ? [] : allPlugins;
|
||||
}
|
||||
return allPlugins.filter(plugin => {
|
||||
const idx = starredPlugins.indexOf(plugin.id);
|
||||
return idx === -1 ? !returnFavoredPlugins : returnFavoredPlugins;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -607,7 +607,13 @@ export function getSelectedPluginKey(state: State): string | undefined {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export function pluginIsStarred(state: State, pluginId: string): boolean {
|
||||
export function pluginIsStarred(
|
||||
state: {
|
||||
selectedApp: string | null;
|
||||
userStarredPlugins: State['userStarredPlugins'];
|
||||
},
|
||||
pluginId: string,
|
||||
): boolean {
|
||||
const {selectedApp} = state;
|
||||
if (!selectedApp) {
|
||||
return false;
|
||||
|
||||
@@ -7,33 +7,35 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, {useState, useRef, useEffect} from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import {colors} from './colors';
|
||||
import Text from './Text';
|
||||
import FlexRow from './FlexRow';
|
||||
|
||||
export const StyledButton = styled.div<{toggled: boolean}>(props => ({
|
||||
width: '30px',
|
||||
height: '16px',
|
||||
background: props.toggled ? colors.green : colors.grey,
|
||||
display: 'block',
|
||||
borderRadius: '100px',
|
||||
position: 'relative',
|
||||
marginLeft: '15px',
|
||||
flexShrink: 0,
|
||||
'&::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: '3px',
|
||||
left: props.toggled ? '18px' : '3px',
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
background: 'white',
|
||||
export const StyledButton = styled.div<{toggled: boolean; large: boolean}>(
|
||||
({large, toggled}) => ({
|
||||
width: large ? 60 : 30,
|
||||
height: large ? 32 : 16,
|
||||
background: toggled ? colors.green : colors.grey,
|
||||
display: 'block',
|
||||
borderRadius: '100px',
|
||||
transition: 'all cubic-bezier(0.3, 1.5, 0.7, 1) 0.3s',
|
||||
},
|
||||
}));
|
||||
position: 'relative',
|
||||
marginLeft: large ? 0 : 15, // margins in components should die :-/
|
||||
flexShrink: 0,
|
||||
'&::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: large ? 6 : 3,
|
||||
left: large ? (toggled ? 34 : 6) : toggled ? 18 : 3,
|
||||
width: large ? 20 : 10,
|
||||
height: large ? 20 : 10,
|
||||
background: 'white',
|
||||
borderRadius: '100px',
|
||||
transition: 'all cubic-bezier(0.3, 1.5, 0.7, 1) 0.3s',
|
||||
},
|
||||
}),
|
||||
);
|
||||
StyledButton.displayName = 'ToggleSwitch:StyledButton';
|
||||
|
||||
const Container = styled(FlexRow)({
|
||||
@@ -60,6 +62,7 @@ type Props = {
|
||||
className?: string;
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
large?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -72,16 +75,34 @@ type Props = {
|
||||
* <ToggleButton onClick={handler} toggled={boolean}/>
|
||||
* ```
|
||||
*/
|
||||
export default class ToggleButton extends React.Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<Container onClick={this.props.onClick} title={this.props.tooltip}>
|
||||
<StyledButton
|
||||
className={this.props.className}
|
||||
toggled={this.props.toggled || false}
|
||||
/>
|
||||
{this.props.label && <Label>{this.props.label}</Label>}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
export default function ToggleButton(props: Props) {
|
||||
const unmounted = useRef(false);
|
||||
const [switching, setSwitching] = useState(false);
|
||||
useEffect(
|
||||
() => () => {
|
||||
// suppress switching after unmount
|
||||
unmounted.current = true;
|
||||
},
|
||||
[],
|
||||
);
|
||||
return (
|
||||
<Container
|
||||
onClick={e => {
|
||||
setSwitching(true);
|
||||
setTimeout(() => {
|
||||
props?.onClick?.(e);
|
||||
if (unmounted.current === false) {
|
||||
setSwitching(false);
|
||||
}
|
||||
}, 300);
|
||||
}}
|
||||
title={props.tooltip}>
|
||||
<StyledButton
|
||||
large={!!props.large}
|
||||
className={props.className}
|
||||
toggled={switching ? !props.toggled : !!props.toggled}
|
||||
/>
|
||||
{props.label && <Label>{props.label}</Label>}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -332,5 +332,19 @@
|
||||
],
|
||||
"checkmark-circle-outline": [
|
||||
24
|
||||
],
|
||||
"target-outline": [
|
||||
16,
|
||||
24
|
||||
],
|
||||
"internet-outline": [
|
||||
24,
|
||||
32
|
||||
],
|
||||
"profile-outline": [
|
||||
32
|
||||
],
|
||||
"app-react-outline": [
|
||||
16
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user