diff --git a/packages/design-system/atoms/copy-area/CopyArea.stories.tsx b/packages/design-system/atoms/copy-area/CopyArea.stories.tsx
new file mode 100644
index 0000000..0a0b834
--- /dev/null
+++ b/packages/design-system/atoms/copy-area/CopyArea.stories.tsx
@@ -0,0 +1,11 @@
+import { CopyArea } from './CopyArea';
+
+export default {
+ title: 'Atoms/Copy Area',
+ component: CopyArea,
+ args: {
+ value: 'Hello world',
+ },
+};
+
+export const copyArea = (args) => ;
diff --git a/packages/design-system/atoms/copy-area/CopyArea.styled.ts b/packages/design-system/atoms/copy-area/CopyArea.styled.ts
new file mode 100644
index 0000000..8b804b4
--- /dev/null
+++ b/packages/design-system/atoms/copy-area/CopyArea.styled.ts
@@ -0,0 +1,19 @@
+import { palette } from '@roleypoly/design-system/atoms/colors';
+import { StyledTextInput } from '@roleypoly/design-system/atoms/text-input';
+import styled, { css } from 'styled-components';
+
+export const CopyAreaStyled = styled.div`
+ cursor: pointer;
+`;
+
+export const CopyAreaTextInput = styled(StyledTextInput)<{ copied?: boolean }>`
+ margin: 0.5em 0;
+ cursor: pointer;
+ font-size: 1em;
+ ${({ copied }) =>
+ copied &&
+ css`
+ border-color: ${palette.green400} !important;
+ box-shadow: inset 0 0 2px ${palette.green200} !important;
+ `}
+`;
diff --git a/packages/design-system/atoms/copy-area/CopyArea.tsx b/packages/design-system/atoms/copy-area/CopyArea.tsx
new file mode 100644
index 0000000..3435abf
--- /dev/null
+++ b/packages/design-system/atoms/copy-area/CopyArea.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import ReactTooltip from 'react-tooltip';
+import { CopyAreaStyled, CopyAreaTextInput } from './CopyArea.styled';
+
+type CopyAreaProps = {
+ value: string;
+};
+
+enum CopyState {
+ NotCopied = 0,
+ Copied = 1,
+}
+
+export const CopyArea = (props: CopyAreaProps) => {
+ const [copied, setCopied] = React.useState(CopyState.NotCopied);
+
+ const inputRef = React.useRef(null);
+
+ const handleClick = () => {
+ inputRef.current?.setSelectionRange(0, 99999);
+
+ if (navigator.clipboard) {
+ navigator.clipboard.writeText(props.value);
+ setCopied(CopyState.Copied);
+ }
+ };
+
+ const strings = {
+ [CopyState.NotCopied]: `Press ⌘/Ctrl+C to copy`,
+ [CopyState.Copied]: 'Copied to clipboard!',
+ };
+
+ return (
+
+ setCopied(CopyState.NotCopied)}
+ copied={copied === CopyState.Copied}
+ data-tip={strings[copied]}
+ data-for={'copy-area'}
+ />
+
+
+ );
+};
diff --git a/packages/design-system/atoms/copy-area/index.ts b/packages/design-system/atoms/copy-area/index.ts
new file mode 100644
index 0000000..56e15c7
--- /dev/null
+++ b/packages/design-system/atoms/copy-area/index.ts
@@ -0,0 +1 @@
+export * from './CopyArea';
diff --git a/packages/design-system/atoms/text-input/TextInput.tsx b/packages/design-system/atoms/text-input/TextInput.tsx
index 9bfbec3..e19c5bd 100644
--- a/packages/design-system/atoms/text-input/TextInput.tsx
+++ b/packages/design-system/atoms/text-input/TextInput.tsx
@@ -40,7 +40,7 @@ const common = css`
}
`;
-const StyledTextInput = styled.input`
+export const StyledTextInput = styled.input`
${common};
`;
@@ -89,6 +89,7 @@ export const TextInputWithIcon = (props: TextInputWithIconProps) => {
const StyledTextarea = styled.textarea`
${common};
${fontCSS};
+ margin: 0.5em 0;
`;
export const MultilineTextInput = (
diff --git a/packages/design-system/molecules/editor-invite-link/EditorInviteLink.stories.tsx b/packages/design-system/molecules/editor-invite-link/EditorInviteLink.stories.tsx
new file mode 100644
index 0000000..e2cdea4
--- /dev/null
+++ b/packages/design-system/molecules/editor-invite-link/EditorInviteLink.stories.tsx
@@ -0,0 +1,12 @@
+import { mastheadSlugs } from '../../fixtures/storyData';
+import { EditorInviteLink } from './EditorInviteLink';
+
+export default {
+ title: 'Molecules/Editor Invite Link',
+ component: EditorInviteLink,
+ args: {
+ guild: mastheadSlugs[0],
+ },
+};
+
+export const editorInviteLink = (args) => ;
diff --git a/packages/design-system/molecules/editor-invite-link/EditorInviteLink.styled.ts b/packages/design-system/molecules/editor-invite-link/EditorInviteLink.styled.ts
new file mode 100644
index 0000000..13e9543
--- /dev/null
+++ b/packages/design-system/molecules/editor-invite-link/EditorInviteLink.styled.ts
@@ -0,0 +1,2 @@
+import styled from 'styled-components';
+export const EditorInviteLinkStyled = styled.div``;
diff --git a/packages/design-system/molecules/editor-invite-link/EditorInviteLink.tsx b/packages/design-system/molecules/editor-invite-link/EditorInviteLink.tsx
new file mode 100644
index 0000000..9ba2f48
--- /dev/null
+++ b/packages/design-system/molecules/editor-invite-link/EditorInviteLink.tsx
@@ -0,0 +1,26 @@
+import { palette } from '@roleypoly/design-system/atoms/colors';
+import { CopyArea } from '@roleypoly/design-system/atoms/copy-area';
+import { AmbientLarge } from '@roleypoly/design-system/atoms/typography';
+import { MessageBox } from '@roleypoly/design-system/organisms/role-picker/RolePicker.styled';
+import { Guild } from '@roleypoly/types';
+import { GoInfo } from 'react-icons/go';
+
+export const EditorInviteLink = (props: { guild: Guild }) => {
+ const currentURL = new URL(location.href);
+ currentURL.pathname = `/s/${props.guild.id}`;
+ currentURL.search = '';
+
+ const inviteLink = currentURL.toString();
+
+ return (
+
+ Share this link with your server members, or ping{' '}
+ @roleypoly in your server.
+
+
+
+ This link will never change. Share it with anyone!
+
+
+ );
+};
diff --git a/packages/design-system/molecules/editor-invite-link/index.ts b/packages/design-system/molecules/editor-invite-link/index.ts
new file mode 100644
index 0000000..7ff1745
--- /dev/null
+++ b/packages/design-system/molecules/editor-invite-link/index.ts
@@ -0,0 +1 @@
+export * from './EditorInviteLink';
diff --git a/packages/design-system/organisms/editor-shell/EditorShell.tsx b/packages/design-system/organisms/editor-shell/EditorShell.tsx
index 4f6575c..ba28f60 100644
--- a/packages/design-system/organisms/editor-shell/EditorShell.tsx
+++ b/packages/design-system/organisms/editor-shell/EditorShell.tsx
@@ -1,5 +1,6 @@
import { LinedSpace, Space } from '@roleypoly/design-system/atoms/space';
import { EditableServerMessage } from '@roleypoly/design-system/molecules/editable-server-message';
+import { EditorInviteLink } from '@roleypoly/design-system/molecules/editor-invite-link';
import { ServerMasthead } from '@roleypoly/design-system/molecules/server-masthead';
import { ServerUtilities } from '@roleypoly/design-system/molecules/server-utilities/ServerUtilities';
import { SecondaryEditing } from '@roleypoly/design-system/organisms/masthead';
@@ -60,6 +61,8 @@ export const EditorShell = (props: EditorShellProps) => {
+
+
{
return (
<>
-
+
>
);