Improved button styling
Added toast when collection permissions are saved Removed usage of setState (old habits die hard)
This commit is contained in:
@ -1,60 +1,53 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { darken } from 'polished';
|
import { darken, lighten } from 'polished';
|
||||||
|
|
||||||
const RealButton = styled.button`
|
const RealButton = styled.button`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
background: ${props => props.theme.primary};
|
background: ${props => props.theme.blackLight};
|
||||||
color: ${props => props.theme.white};
|
color: ${props => props.theme.white};
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 2px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 15px;
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
outline: none;
|
outline: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
&::-moz-focus-inner {
|
&::-moz-focus-inner {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
&:hover {
|
|
||||||
background: ${props => darken(0.05, props.theme.primary)};
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
&:hover {
|
||||||
position: relative;
|
background: ${props => darken(0.05, props.theme.blackLight)};
|
||||||
top: 0.05em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 0.6;
|
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
color: ${props => lighten(0.2, props.theme.blackLight)};
|
||||||
}
|
}
|
||||||
|
|
||||||
${props =>
|
${props =>
|
||||||
props.light &&
|
props.neutral &&
|
||||||
`
|
`
|
||||||
color: ${props.theme.slate};
|
background: ${props.theme.white};
|
||||||
background: transparent;
|
color: ${props.theme.text};
|
||||||
border: 1px solid ${props.theme.slate};
|
box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 2px;
|
||||||
|
border: 1px solid ${props.theme.slateLight};
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: transparent;
|
background: ${darken(0.05, props.theme.white)};
|
||||||
color: ${props.theme.slateDark};
|
border: 1px solid ${darken(0.05, props.theme.slateLight)};
|
||||||
border: 1px solid ${props.theme.slateDark};
|
|
||||||
}
|
|
||||||
`} ${props =>
|
|
||||||
props.neutral &&
|
|
||||||
`
|
|
||||||
background: ${props.theme.slate};
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: ${darken(0.05, props.theme.slate)};
|
|
||||||
}
|
}
|
||||||
`} ${props =>
|
`} ${props =>
|
||||||
props.danger &&
|
props.danger &&
|
||||||
@ -72,7 +65,7 @@ const Label = styled.span`
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
${props => props.hasIcon && 'padding-left: 2px;'};
|
${props => props.hasIcon && 'padding-left: 4px;'};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Inner = styled.span`
|
const Inner = styled.span`
|
||||||
@ -84,7 +77,7 @@ const Inner = styled.span`
|
|||||||
|
|
||||||
${props =>
|
${props =>
|
||||||
props.hasIcon &&
|
props.hasIcon &&
|
||||||
(props.small ? 'padding-left: 6px;' : 'padding-left: 10px;')};
|
(props.small ? 'padding-left: 6px;' : 'padding-left: 8px;')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
|
@ -25,38 +25,40 @@ type Props = {
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
class Collections extends React.Component<Props> {
|
class Collections extends React.Component<Props> {
|
||||||
|
isPreloaded: boolean = !!this.props.collections.orderedData.length;
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.collections.fetchPage({ limit: 100 });
|
this.props.collections.fetchPage({ limit: 100 });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { history, location, collections, ui, documents } = this.props;
|
const { history, location, collections, ui, documents } = this.props;
|
||||||
|
const content = (
|
||||||
|
<Flex column>
|
||||||
|
<Header>Collections</Header>
|
||||||
|
{collections.orderedData.map(collection => (
|
||||||
|
<CollectionLink
|
||||||
|
key={collection.id}
|
||||||
|
history={history}
|
||||||
|
location={location}
|
||||||
|
collection={collection}
|
||||||
|
activeDocument={documents.active}
|
||||||
|
prefetchDocument={documents.prefetchDocument}
|
||||||
|
ui={ui}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<SidebarLink
|
||||||
|
onClick={this.props.onCreateCollection}
|
||||||
|
icon={<PlusIcon />}
|
||||||
|
>
|
||||||
|
New collection…
|
||||||
|
</SidebarLink>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
collections.isLoaded && (
|
collections.isLoaded &&
|
||||||
<Fade>
|
(this.isPreloaded ? content : <Fade>{content}</Fade>)
|
||||||
<Flex column>
|
|
||||||
<Header>Collections</Header>
|
|
||||||
{collections.orderedData.map(collection => (
|
|
||||||
<CollectionLink
|
|
||||||
key={collection.id}
|
|
||||||
history={history}
|
|
||||||
location={location}
|
|
||||||
collection={collection}
|
|
||||||
activeDocument={documents.active}
|
|
||||||
prefetchDocument={documents.prefetchDocument}
|
|
||||||
ui={ui}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<SidebarLink
|
|
||||||
onClick={this.props.onCreateCollection}
|
|
||||||
icon={<PlusIcon />}
|
|
||||||
>
|
|
||||||
New collection…
|
|
||||||
</SidebarLink>
|
|
||||||
</Flex>
|
|
||||||
</Fade>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { observable } from 'mobx';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { CloseIcon } from 'outline-icons';
|
import { CloseIcon } from 'outline-icons';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
@ -12,14 +14,10 @@ type Props = {
|
|||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
@observer
|
||||||
isHidden: boolean,
|
class Tip extends React.Component<Props> {
|
||||||
};
|
@observable
|
||||||
|
isHidden: boolean = window.localStorage.getItem(this.storageId) === 'hidden';
|
||||||
class Tip extends React.Component<Props, State> {
|
|
||||||
state = {
|
|
||||||
isHidden: window.localStorage.getItem(this.storageId) === 'hidden',
|
|
||||||
};
|
|
||||||
|
|
||||||
get storageId() {
|
get storageId() {
|
||||||
return `tip-${this.props.id}`;
|
return `tip-${this.props.id}`;
|
||||||
@ -27,28 +25,29 @@ class Tip extends React.Component<Props, State> {
|
|||||||
|
|
||||||
hide = () => {
|
hide = () => {
|
||||||
window.localStorage.setItem(this.storageId, 'hidden');
|
window.localStorage.setItem(this.storageId, 'hidden');
|
||||||
this.setState({ isHidden: true });
|
this.isHidden = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children } = this.props;
|
const { children } = this.props;
|
||||||
if (this.props.disabled || this.state.isHidden) return null;
|
if (this.props.disabled || this.isHidden) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper align="center">
|
<Wrapper align="flex-start">
|
||||||
<span>{children}</span>
|
<span>{children}</span>
|
||||||
|
|
||||||
<Tooltip tooltip="Hide this message" placement="bottom">
|
<Tooltip tooltip="Hide this message" placement="bottom">
|
||||||
<Button
|
<Close type="close" size={32} color="#000" onClick={this.hide} />
|
||||||
onClick={this.hide}
|
|
||||||
icon={<CloseIcon type="close" size={32} color="#FFF" />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Close = styled(CloseIcon)`
|
||||||
|
margin-top: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
const Wrapper = styled(Flex)`
|
const Wrapper = styled(Flex)`
|
||||||
background: ${props => props.theme.primary};
|
background: ${props => props.theme.primary};
|
||||||
color: ${props => props.theme.text};
|
color: ${props => props.theme.text};
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { observable } from 'mobx';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Tip from './Tip';
|
import Tip from './Tip';
|
||||||
import CopyToClipboard from './CopyToClipboard';
|
import CopyToClipboard from './CopyToClipboard';
|
||||||
@ -9,17 +11,12 @@ type Props = {
|
|||||||
team: Team,
|
team: Team,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
@observer
|
||||||
linkCopied: boolean,
|
class TipInvite extends React.Component<Props> {
|
||||||
};
|
@observable linkCopied: boolean = false;
|
||||||
|
|
||||||
class TipInvite extends React.Component<Props, State> {
|
|
||||||
state = {
|
|
||||||
linkCopied: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
handleCopy = () => {
|
handleCopy = () => {
|
||||||
this.setState({ linkCopied: true });
|
this.linkCopied = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -35,7 +32,7 @@ class TipInvite extends React.Component<Props, State> {
|
|||||||
–{' '}
|
–{' '}
|
||||||
<CopyToClipboard text={team.url} onCopy={this.handleCopy}>
|
<CopyToClipboard text={team.url} onCopy={this.handleCopy}>
|
||||||
<a>
|
<a>
|
||||||
{this.state.linkCopied
|
{this.linkCopied
|
||||||
? 'link copied to clipboard!'
|
? 'link copied to clipboard!'
|
||||||
: 'copy a link to share.'}
|
: 'copy a link to share.'}
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { observable } from 'mobx';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -10,14 +12,10 @@ type Props = {
|
|||||||
height?: string,
|
height?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
@observer
|
||||||
isLoaded: boolean,
|
class Frame extends React.Component<Props> {
|
||||||
};
|
|
||||||
|
|
||||||
class Frame extends React.Component<Props, State> {
|
|
||||||
mounted: boolean;
|
mounted: boolean;
|
||||||
|
@observable isLoaded: boolean = false;
|
||||||
state = { isLoaded: false };
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
@ -30,7 +28,7 @@ class Frame extends React.Component<Props, State> {
|
|||||||
|
|
||||||
loadIframe = () => {
|
loadIframe = () => {
|
||||||
if (!this.mounted) return;
|
if (!this.mounted) return;
|
||||||
this.setState({ isLoaded: true });
|
this.isLoaded = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -45,7 +43,7 @@ class Frame extends React.Component<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Rounded width={width} height={height}>
|
<Rounded width={width} height={height}>
|
||||||
{this.state.isLoaded && (
|
{this.isLoaded && (
|
||||||
<Component
|
<Component
|
||||||
ref={forwardedRef}
|
ref={forwardedRef}
|
||||||
sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
|
sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
|
||||||
|
@ -142,27 +142,17 @@ class CollectionScene extends React.Component<Props> {
|
|||||||
{collection ? (
|
{collection ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<PageTitle title={collection.name} />
|
<PageTitle title={collection.name} />
|
||||||
<Heading>
|
|
||||||
{collection.private ? (
|
|
||||||
<PrivateCollectionIcon
|
|
||||||
color={collection.color}
|
|
||||||
size={40}
|
|
||||||
expanded
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<CollectionIcon color={collection.color} size={40} expanded />
|
|
||||||
)}{' '}
|
|
||||||
{collection.name}
|
|
||||||
</Heading>
|
|
||||||
{collection.isEmpty ? (
|
{collection.isEmpty ? (
|
||||||
<React.Fragment>
|
<Centered column>
|
||||||
<HelpText>
|
<HelpText>
|
||||||
Collections are for grouping your knowledge base. Get started
|
<strong>{collection.name}</strong> doesn’t contain any
|
||||||
by creating a new document.
|
documents yet.<br />Get started by creating a new one!
|
||||||
</HelpText>
|
</HelpText>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Link to={newDocumentUrl(collection)}>
|
<Link to={newDocumentUrl(collection)}>
|
||||||
<Button>Create new document</Button>
|
<Button icon={<NewDocumentIcon color="#FFF" />}>
|
||||||
|
Create a document
|
||||||
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
{collection.private && (
|
{collection.private && (
|
||||||
<Button onClick={this.onPermissions} neutral>
|
<Button onClick={this.onPermissions} neutral>
|
||||||
@ -180,9 +170,26 @@ class CollectionScene extends React.Component<Props> {
|
|||||||
onSubmit={this.handlePermissionsModalClose}
|
onSubmit={this.handlePermissionsModalClose}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
</React.Fragment>
|
</Centered>
|
||||||
) : (
|
) : (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
<Heading>
|
||||||
|
{collection.private ? (
|
||||||
|
<PrivateCollectionIcon
|
||||||
|
color={collection.color}
|
||||||
|
size={40}
|
||||||
|
expanded
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CollectionIcon
|
||||||
|
color={collection.color}
|
||||||
|
size={40}
|
||||||
|
expanded
|
||||||
|
/>
|
||||||
|
)}{' '}
|
||||||
|
{collection.name}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
{collection.description && (
|
{collection.description && (
|
||||||
<RichMarkdownEditor
|
<RichMarkdownEditor
|
||||||
key={collection.description}
|
key={collection.description}
|
||||||
@ -220,6 +227,13 @@ class CollectionScene extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Centered = styled(Flex)`
|
||||||
|
text-align: center;
|
||||||
|
margin: 40vh auto 0;
|
||||||
|
max-width: 380px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
`;
|
||||||
|
|
||||||
const TinyPinIcon = styled(PinIcon)`
|
const TinyPinIcon = styled(PinIcon)`
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 4px;
|
top: 4px;
|
||||||
@ -227,6 +241,7 @@ const TinyPinIcon = styled(PinIcon)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const Wrapper = styled(Flex)`
|
const Wrapper = styled(Flex)`
|
||||||
|
justify-content: center;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -45,12 +45,12 @@ class CollectionDelete extends React.Component<Props> {
|
|||||||
<Flex column>
|
<Flex column>
|
||||||
<form onSubmit={this.handleSubmit}>
|
<form onSubmit={this.handleSubmit}>
|
||||||
<HelpText>
|
<HelpText>
|
||||||
Are you sure? Deleting the <strong>{collection.name}</strong>{' '}
|
Are you sure about that? Deleting the{' '}
|
||||||
collection is permanent and will also delete all of the documents
|
<strong>{collection.name}</strong> collection is permanent and will
|
||||||
within it, so be careful with that.
|
also delete all of the documents within it, so be extra careful.
|
||||||
</HelpText>
|
</HelpText>
|
||||||
<Button type="submit" danger>
|
<Button type="submit" danger>
|
||||||
{this.isDeleting ? 'Deleting…' : 'Delete'}
|
{this.isDeleting ? 'Deleting…' : 'I’m sure – Delete'}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -27,7 +27,8 @@ type Props = {
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
class CollectionPermissions extends React.Component<Props> {
|
class CollectionPermissions extends React.Component<Props> {
|
||||||
@observable isSaving: boolean;
|
@observable isEdited: boolean = false;
|
||||||
|
@observable isSaving: boolean = false;
|
||||||
@observable filter: string;
|
@observable filter: string;
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -35,10 +36,17 @@ class CollectionPermissions extends React.Component<Props> {
|
|||||||
this.props.collection.fetchUsers();
|
this.props.collection.fetchUsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.isEdited) {
|
||||||
|
this.props.ui.showToast('Permissions updated');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handlePrivateChange = async (ev: SyntheticInputEvent<*>) => {
|
handlePrivateChange = async (ev: SyntheticInputEvent<*>) => {
|
||||||
const { collection } = this.props;
|
const { collection } = this.props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.isEdited = true;
|
||||||
collection.private = ev.target.checked;
|
collection.private = ev.target.checked;
|
||||||
await collection.save();
|
await collection.save();
|
||||||
|
|
||||||
@ -53,6 +61,7 @@ class CollectionPermissions extends React.Component<Props> {
|
|||||||
|
|
||||||
handleAddUser = user => {
|
handleAddUser = user => {
|
||||||
try {
|
try {
|
||||||
|
this.isEdited = true;
|
||||||
this.props.collection.addUser(user);
|
this.props.collection.addUser(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.props.ui.showToast('Could not add user');
|
this.props.ui.showToast('Could not add user');
|
||||||
@ -61,6 +70,7 @@ class CollectionPermissions extends React.Component<Props> {
|
|||||||
|
|
||||||
handleRemoveUser = user => {
|
handleRemoveUser = user => {
|
||||||
try {
|
try {
|
||||||
|
this.isEdited = true;
|
||||||
this.props.collection.removeUser(user);
|
this.props.collection.removeUser(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.props.ui.showToast('Could not remove user');
|
this.props.ui.showToast('Could not remove user');
|
||||||
|
@ -18,7 +18,7 @@ const UserListItem = ({ user, onAdd, showAdd }: Props) => {
|
|||||||
image={<Avatar src={user.avatarUrl} size={32} />}
|
image={<Avatar src={user.avatarUrl} size={32} />}
|
||||||
actions={
|
actions={
|
||||||
showAdd ? (
|
showAdd ? (
|
||||||
<Button type="button" onClick={onAdd}>
|
<Button type="button" onClick={onAdd} neutral>
|
||||||
Invite
|
Invite
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
|
@ -45,11 +45,12 @@ class DocumentDelete extends React.Component<Props> {
|
|||||||
<Flex column>
|
<Flex column>
|
||||||
<form onSubmit={this.handleSubmit}>
|
<form onSubmit={this.handleSubmit}>
|
||||||
<HelpText>
|
<HelpText>
|
||||||
Are you sure? Deleting the <strong>{document.title}</strong>{' '}
|
Are you sure about that? Deleting the{' '}
|
||||||
document is permanent and will also delete all of its history.
|
<strong>{document.title}</strong> document is permanent, will delete
|
||||||
|
all of its history, and any child documents.
|
||||||
</HelpText>
|
</HelpText>
|
||||||
<Button type="submit" danger>
|
<Button type="submit" danger>
|
||||||
{this.isDeleting ? 'Deleting…' : 'Delete'}
|
{this.isDeleting ? 'Deleting…' : 'I’m sure – Delete'}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -17,7 +17,11 @@ function SlackButton({ state, scopes, redirectUri, label }: Props) {
|
|||||||
(window.location.href = slackAuth(state, scopes, redirectUri));
|
(window.location.href = slackAuth(state, scopes, redirectUri));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button onClick={handleClick} icon={<SpacedSlackLogo size={24} />} neutral>
|
<Button
|
||||||
|
onClick={handleClick}
|
||||||
|
icon={<SpacedSlackLogo size={24} fill="#000" />}
|
||||||
|
neutral
|
||||||
|
>
|
||||||
{label ? (
|
{label ? (
|
||||||
label
|
label
|
||||||
) : (
|
) : (
|
||||||
|
Reference in New Issue
Block a user