diff --git a/apps/dotcom/client/e2e/fixtures/WorkspaceInviteDialog.ts b/apps/dotcom/client/e2e/fixtures/WorkspaceInviteDialog.ts index 926bf10484db..7f925bafaa56 100644 --- a/apps/dotcom/client/e2e/fixtures/WorkspaceInviteDialog.ts +++ b/apps/dotcom/client/e2e/fixtures/WorkspaceInviteDialog.ts @@ -7,7 +7,7 @@ export class WorkspaceInviteDialog { public readonly declineButton: Locator constructor(public readonly page: Page) { - this.dialog = this.page.getByText('You have been invited to join workspace') + this.dialog = this.page.getByText('You have been invited to join:') this.acceptButton = this.page.getByRole('button', { name: 'Accept and join workspace' }) this.declineButton = this.page.getByRole('button', { name: 'No thanks' }) } diff --git a/apps/dotcom/client/e2e/tests/ui.scenario.spec.ts b/apps/dotcom/client/e2e/tests/ui.scenario.spec.ts index 9b1e8b20bf9e..dbd4e5b6ce6a 100644 --- a/apps/dotcom/client/e2e/tests/ui.scenario.spec.ts +++ b/apps/dotcom/client/e2e/tests/ui.scenario.spec.ts @@ -240,7 +240,7 @@ test.describe('UI scenarios', () => { await expect(ownerDialog.getByPlaceholder('Workspace name')).toBeVisible() await expect(ownerDialog.getByText('Invite teammates')).toBeVisible() await expect(ownerDialog.getByRole('button', { name: 'Copy invite link' })).toBeVisible() - await expect(ownerDialog.getByText(/Members \(\d+\)/)).toBeVisible() + await expect(ownerDialog.getByRole('tab', { name: 'Members' })).toBeVisible() await expect(ownerDialog.getByText(/\(you\)/)).toBeVisible() const memberRoleSelect = ownerDialog.locator(`[id="workspace-member-role-${memberUserId}"]`) await expect(memberRoleSelect).toHaveText('Member') @@ -297,6 +297,61 @@ test.describe('UI scenarios', () => { await member.sidebar.expectFileNotVisible(fileName) }) + test('home workspace settings show a private rename dialog, not an empty one', async ({ + owner, + scenario, + }) => { + // Regression: the home workspace ("My workspace") used to render an empty dialog — the + // component returned null for it while the sidebar still opened the dialog, so the page + // just dimmed with no content. It's private: it can be renamed, but not shared or managed. + await scenario.ensureGroupsReady(owner) + await owner.sidebar.switchToHomeWorkspace() + await owner.page.getByTestId('tla-sidebar-workspace-settings').click() + + const dialog = owner.page.getByRole('dialog', { name: 'Manage workspace' }) + await expect(dialog).toBeVisible() + // Renameable: the name field is present and editable. + await expect(dialog.getByPlaceholder('Workspace name')).toBeEnabled() + // Private: a disabled invite control with an explanatory note (no shareable link). + await expect( + dialog.getByText( + 'This is your private workspace. Create a new workspace to invite teammates.' + ) + ).toBeVisible() + await expect(dialog.getByRole('button', { name: 'Copy invite link' })).toBeDisabled() + // Nothing to manage, so the dialog has no Members/Settings tabs at all. + await expect(dialog.getByRole('tab')).toHaveCount(0) + + await owner.page.keyboard.press('Escape') + }) + + test('the home workspace can be renamed from its settings dialog', async ({ + owner, + scenario, + }) => { + // Regression: the home workspace is renameable (the reason its settings dialog exists at + // all), so the rename must apply. Restore the name afterwards so later serial tests still + // see the original. + await scenario.ensureGroupsReady(owner) + await owner.sidebar.switchToHomeWorkspace() + const originalName = await owner.page.getByTestId('tla-active-workspace-name').innerText() + const newName = scenario.name('home renamed') + + await owner.page.getByTestId('tla-sidebar-workspace-settings').click() + const dialog = owner.page.getByRole('dialog', { name: 'Manage workspace' }) + await dialog.getByPlaceholder('Workspace name').fill(newName) + await owner.page.getByRole('button', { name: 'Close' }).click() + await owner.waitForMutationResolution() + await owner.sidebar.expectActiveWorkspace(newName) + + // Restore the original home workspace name. + await owner.page.getByTestId('tla-sidebar-workspace-settings').click() + await dialog.getByPlaceholder('Workspace name').fill(originalName) + await owner.page.getByRole('button', { name: 'Close' }).click() + await owner.waitForMutationResolution() + await owner.sidebar.expectActiveWorkspace(originalName) + }) + test('workspace settings rename updates labels and persists', async ({ owner, scenario }) => { const workspaceName = scenario.name('settings rename old') const newWorkspaceName = scenario.name('settings rename new') diff --git a/apps/dotcom/client/public/tla/locales-compiled/en.json b/apps/dotcom/client/public/tla/locales-compiled/en.json index e5a607953839..0cff19806c42 100644 --- a/apps/dotcom/client/public/tla/locales-compiled/en.json +++ b/apps/dotcom/client/public/tla/locales-compiled/en.json @@ -111,12 +111,6 @@ "value": "Unpin file" } ], - "12c8a9838c": [ - { - "type": 0, - "value": "Try the tldraw SDK" - } - ], "13ddd375ad": [ { "type": 0, @@ -159,6 +153,26 @@ "value": "Workspace name" } ], + "23dcace54a": [ + { + "type": 0, + "value": "You have been invited to join " + }, + { + "children": [ + { + "type": 1, + "value": "workspaceName" + } + ], + "type": 8, + "value": "strong" + }, + { + "type": 0, + "value": ". Create a free account to continue." + } + ], "250ddea65c": [ { "type": 0, @@ -231,6 +245,12 @@ "value": "Editor" } ], + "34e34c43ec": [ + { + "type": 0, + "value": "Manage" + } + ], "375396d6e5": [ { "type": 0, @@ -345,6 +365,12 @@ "value": "Unable to unpublish the file." } ], + "51317dcad5": [ + { + "type": 0, + "value": "This is your private workspace. Create a new workspace to invite teammates." + } + ], "517bb809d9": [ { "type": 0, @@ -385,6 +411,12 @@ "value": "PNG" } ], + "5638176be5": [ + { + "type": 0, + "value": "Create a workspace" + } + ], "5677e381d2": [ { "type": 0, @@ -595,12 +627,6 @@ "value": "Got it" } ], - "78faf88f4d": [ - { - "type": 0, - "value": "Enable invite link" - } - ], "797799f35e": [ { "type": 0, @@ -619,6 +645,12 @@ "value": "User manual" } ], + "7bcdfeab70": [ + { + "type": 0, + "value": "You have been invited to join:" + } + ], "7be10fc592": [ { "type": 0, @@ -927,22 +959,6 @@ "value": "File is getting large" } ], - "a8c9ad7ea5": [ - { - "type": 0, - "value": "You have been invited to join " - }, - { - "children": [ - { - "type": 1, - "value": "workspaceName" - } - ], - "type": 8, - "value": "strong" - } - ], "a9ded1e5ce": [ { "type": 0, @@ -997,6 +1013,12 @@ "value": "Email address" } ], + "b48becc771": [ + { + "type": 0, + "value": "Enable invites" + } + ], "b6d4223e60": [ { "type": 0, @@ -1015,20 +1037,6 @@ "value": "Copy link" } ], - "b9a68fbd56": [ - { - "type": 0, - "value": "Members (" - }, - { - "type": 1, - "value": "count" - }, - { - "type": 0, - "value": ")" - } - ], "ba0318cb81": [ { "type": 0, @@ -1321,12 +1329,6 @@ "value": "or" } ], - "e9e0f16850": [ - { - "type": 0, - "value": "You have been invited to join workspace:" - } - ], "ea4788705e": [ { "type": 0, @@ -1389,6 +1391,12 @@ "value": " to learn more." } ], + "ef53538ae4": [ + { + "type": 0, + "value": "Members" + } + ], "ef7de3f485": [ { "type": 0, diff --git a/apps/dotcom/client/public/tla/locales/en.json b/apps/dotcom/client/public/tla/locales/en.json index db431d5cf607..7134e09f8e31 100644 --- a/apps/dotcom/client/public/tla/locales/en.json +++ b/apps/dotcom/client/public/tla/locales/en.json @@ -53,9 +53,6 @@ "12a6766ba1": { "translation": "Unpin file" }, - "12c8a9838c": { - "translation": "Try the tldraw SDK" - }, "13ddd375ad": { "translation": "Export as" }, @@ -77,6 +74,9 @@ "21f66c9c00": { "translation": "Workspace name" }, + "23dcace54a": { + "translation": "You have been invited to join {workspaceName}. Create a free account to continue." + }, "250ddea65c": { "translation": "About tldraw" }, @@ -113,6 +113,9 @@ "344a7f427f": { "translation": "Editor" }, + "34e34c43ec": { + "translation": "Manage" + }, "375396d6e5": { "translation": "Remove member" }, @@ -170,6 +173,9 @@ "50db1b7e1e": { "translation": "Unable to unpublish the file." }, + "51317dcad5": { + "translation": "This is your private workspace. Create a new workspace to invite teammates." + }, "517bb809d9": { "translation": "Page menu" }, @@ -185,6 +191,9 @@ "55505ba281": { "translation": "PNG" }, + "5638176be5": { + "translation": "Create a workspace" + }, "5677e381d2": { "translation": "We use analytics cookies to make tldraw better." }, @@ -290,9 +299,6 @@ "78e9815992": { "translation": "Got it" }, - "78faf88f4d": { - "translation": "Enable invite link" - }, "797799f35e": { "translation": "Submit an issue on GitHub" }, @@ -302,6 +308,9 @@ "7b1329f5ca": { "translation": "User manual" }, + "7bcdfeab70": { + "translation": "You have been invited to join:" + }, "7be10fc592": { "translation": "Restore version" }, @@ -431,9 +440,6 @@ "a8873770ab": { "translation": "File is getting large" }, - "a8c9ad7ea5": { - "translation": "You have been invited to join {workspaceName}" - }, "a9ded1e5ce": { "translation": "Background" }, @@ -459,6 +465,9 @@ "b357b524e7": { "translation": "Email address" }, + "b48becc771": { + "translation": "Enable invites" + }, "b6d4223e60": { "translation": "Sign in" }, @@ -468,9 +477,6 @@ "b758336699": { "translation": "Copy link" }, - "b9a68fbd56": { - "translation": "Members ({count})" - }, "ba0318cb81": { "translation": "Sign in to share" }, @@ -576,9 +582,6 @@ "e81c4e4f2b": { "translation": "or" }, - "e9e0f16850": { - "translation": "You have been invited to join workspace:" - }, "ea4788705e": { "translation": "Cancel" }, @@ -603,6 +606,9 @@ "ede55d955f": { "translation": "Read our cookie policy to learn more." }, + "ef53538ae4": { + "translation": "Members" + }, "ef7de3f485": { "translation": "selected" }, diff --git a/apps/dotcom/client/src/components/Links.tsx b/apps/dotcom/client/src/components/Links.tsx deleted file mode 100644 index 51ac78549d5b..000000000000 --- a/apps/dotcom/client/src/components/Links.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { TldrawUiMenuGroup, TldrawUiMenuItem } from 'tldraw' -import { useOpenUrlAndTrack } from '../hooks/useOpenUrlAndTrack' - -export function LegacyLinks() { - const openAndTrack = useOpenUrlAndTrack('main-menu') - - return ( - <> - - { - openAndTrack( - 'https://github.com/tldraw/tldraw/blob/main/apps/dotcom/client/TERMS_OF_SERVICE.md' - ) - }} - /> - { - openAndTrack( - 'https://github.com/tldraw/tldraw/blob/main/apps/dotcom/client/PRIVACY_POLICY.md' - ) - }} - /> - - - { - openAndTrack( - 'https://tldraw.dev/?utm_source=dotcom&utm_medium=organic&utm_campaign=learn-more', - true - ) - }} - /> - - - ) -} diff --git a/apps/dotcom/client/src/pages/admin.module.css b/apps/dotcom/client/src/pages/admin.module.css index 3efef117cad3..af1f6a2279c1 100644 --- a/apps/dotcom/client/src/pages/admin.module.css +++ b/apps/dotcom/client/src/pages/admin.module.css @@ -318,49 +318,6 @@ flex-wrap: wrap; } -/* Invites Table */ -.invitesTable { - width: 100%; - border-collapse: collapse; - background-color: var(--tla-color-panel); - border: 1px solid var(--tla-color-border); - border-radius: var(--tla-radius-2); - overflow: hidden; -} - -.invitesTable th { - text-align: left; - padding: 12px; - background-color: var(--tla-color-low); - border-bottom: 1px solid var(--tla-color-border); - font-size: 11px; - font-weight: 600; - color: var(--tla-color-text-3); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.invitesTable td { - text-align: left; - padding: 12px; - border-bottom: 1px solid var(--tla-color-border); - font-size: 12px; - color: var(--tla-color-text-1); -} - -.invitesTable tbody tr:last-child td { - border-bottom: none; -} - -.invitesTable tbody tr:hover { - background-color: var(--tla-color-low); -} - -.tableActions { - display: flex; - gap: 8px; -} - /* Feature Flags */ .featureFlagsNote { margin-bottom: 8px; diff --git a/apps/dotcom/client/src/pages/admin.tsx b/apps/dotcom/client/src/pages/admin.tsx index 7b0fe6e367dc..4e681fb8ac34 100644 --- a/apps/dotcom/client/src/pages/admin.tsx +++ b/apps/dotcom/client/src/pages/admin.tsx @@ -217,7 +217,6 @@ export function Component() { @@ -685,7 +684,7 @@ function HardDeleteFile() { {successMessage &&
{successMessage}
}
- + Delete (cannot be undone)
@@ -1206,7 +1205,6 @@ function RolloutWorkspacesUi() {
(function TlaButton( { @@ -23,6 +24,7 @@ export const TlaButton = forwardRef< ghost = false, variant = 'primary', isLoading = false, + big = false, onClick, ...props }, @@ -42,6 +44,7 @@ export const TlaButton = forwardRef< [styles.primary]: variant === 'primary', [styles.secondary]: variant === 'secondary', [styles.ghost]: ghost, + [styles.big]: big, }, className )} diff --git a/apps/dotcom/client/src/tla/components/TlaButton/button.module.css b/apps/dotcom/client/src/tla/components/TlaButton/button.module.css index ede4a53014db..907f99158d42 100644 --- a/apps/dotcom/client/src/tla/components/TlaButton/button.module.css +++ b/apps/dotcom/client/src/tla/components/TlaButton/button.module.css @@ -47,6 +47,11 @@ animation: tla-spin 1.5s linear infinite; } +.big { + height: 36px; + border-radius: var(--tl-radius-2); +} + @keyframes tla-spin { 0% { transform: rotate(0deg); @@ -82,11 +87,6 @@ color: var(--tla-color-text-1); } -.warning { - background-color: var(--tla-color-secondary); - color: var(--tla-color-warning); -} - .iconRight { position: absolute; right: 8px; @@ -122,11 +122,6 @@ border: none; } -.warning.ghost { - color: var(--tla-color-warning); - background-color: transparent; -} - @media (hover: hover) { .primary:not(:disabled):hover { background-color: var(--tla-color-primary-hover); diff --git a/apps/dotcom/client/src/tla/components/TlaEditor/editor.module.css b/apps/dotcom/client/src/tla/components/TlaEditor/editor.module.css index 51a3ed7e9c09..8c25c8d39be7 100644 --- a/apps/dotcom/client/src/tla/components/TlaEditor/editor.module.css +++ b/apps/dotcom/client/src/tla/components/TlaEditor/editor.module.css @@ -6,35 +6,3 @@ z-index: 1; background-color: var(--tl-color-background); } - -.editorWrapper { - position: relative; - flex-grow: 2; - overflow: hidden; - border-top: 1px solid var(--tla-color-border); - background-color: var(--tl-color-background); -} - -.editorWrapper > .tldraw__editor { - width: 100%; - height: 100%; -} - -@media (min-width: 768px) { - .editorWrapper[data-sidebar='true'] { - border-top-left-radius: 9px; - border-left: 1px solid var(--tla-color-border); - } -} - -.anonStylePanel { - display: flex; - flex-direction: row; - position: relative; - padding-top: 4px; - align-items: flex-start; -} - -.anonStylePanel > button:nth-of-type(1) { - margin-top: 4px; -} diff --git a/apps/dotcom/client/src/tla/components/TlaEditor/top.module.css b/apps/dotcom/client/src/tla/components/TlaEditor/top.module.css index 3979f56f25f5..d65681757a68 100644 --- a/apps/dotcom/client/src/tla/components/TlaEditor/top.module.css +++ b/apps/dotcom/client/src/tla/components/TlaEditor/top.module.css @@ -153,10 +153,6 @@ button.topLeftInputNameWidthSetter { } } -/* .inputWrapper:focus-within::after { - background-color: var(--tl-color-muted-2); -} */ - /* -------------------- Main Menu ------------------- */ .topLeftMainMenuTrigger { diff --git a/apps/dotcom/client/src/tla/components/TlaFileShareMenu/file-share-menu-primitives.tsx b/apps/dotcom/client/src/tla/components/TlaFileShareMenu/file-share-menu-primitives.tsx index 985783348a0b..ade7d90252e6 100644 --- a/apps/dotcom/client/src/tla/components/TlaFileShareMenu/file-share-menu-primitives.tsx +++ b/apps/dotcom/client/src/tla/components/TlaFileShareMenu/file-share-menu-primitives.tsx @@ -9,7 +9,7 @@ export function TlaShareMenuCopyButton({ }: { children: ReactNode onClick(): void | Promise - type?: 'primary' | 'secondary' | 'warning' + type?: 'primary' | 'secondary' }) { const [copied, setCopied] = useState(false) const [isLoading, setIsLoading] = useState(false) diff --git a/apps/dotcom/client/src/tla/components/TlaIcon/TlaIcon.tsx b/apps/dotcom/client/src/tla/components/TlaIcon/TlaIcon.tsx index b91c4e4ce74f..c67641cbdea3 100644 --- a/apps/dotcom/client/src/tla/components/TlaIcon/TlaIcon.tsx +++ b/apps/dotcom/client/src/tla/components/TlaIcon/TlaIcon.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames' -import { CSSProperties, HtmlHTMLAttributes, useLayoutEffect, useRef } from 'react' +import { CSSProperties, useLayoutEffect, useRef } from 'react' import mergedSpriteUrl from '../../../assets/0_merged_tla.svg' import styles from './icon.module.css' @@ -60,7 +60,3 @@ export function TlaIcon({ /> ) } - -export function TlaIconWrapper(props: HtmlHTMLAttributes) { - return -} diff --git a/apps/dotcom/client/src/tla/components/TlaIcon/icon.module.css b/apps/dotcom/client/src/tla/components/TlaIcon/icon.module.css index 9f1b30d2344d..918c1ee13528 100644 --- a/apps/dotcom/client/src/tla/components/TlaIcon/icon.module.css +++ b/apps/dotcom/client/src/tla/components/TlaIcon/icon.module.css @@ -13,27 +13,3 @@ .inline { display: inline-flex; } - -.iconWrapper { - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - color: inherit; - flex-shrink: 0; -} - -.iconWrapper > .icon { - height: 15px; - width: 15px; -} - -.iconWrapper[data-size='m'] { - width: 32px; -} - -.iconWrapper[data-size='m'] > .icon { - height: 20px; - width: 20px; -} diff --git a/apps/dotcom/client/src/tla/components/TlaSidebar/components/TlaSidebarWorkspaceActions.tsx b/apps/dotcom/client/src/tla/components/TlaSidebar/components/TlaSidebarWorkspaceActions.tsx index 20b33331afc5..a0713686fb41 100644 --- a/apps/dotcom/client/src/tla/components/TlaSidebar/components/TlaSidebarWorkspaceActions.tsx +++ b/apps/dotcom/client/src/tla/components/TlaSidebar/components/TlaSidebarWorkspaceActions.tsx @@ -11,7 +11,7 @@ import styles from '../sidebar.module.css' const messages = defineMessages({ newFile: { defaultMessage: 'New file' }, - workspaceSettings: { defaultMessage: 'Settings' }, + workspaceSettings: { defaultMessage: 'Manage' }, }) /** diff --git a/apps/dotcom/client/src/tla/components/TlaSidebar/sidebar.module.css b/apps/dotcom/client/src/tla/components/TlaSidebar/sidebar.module.css index 7d591db711b7..b6ddaffc73d2 100644 --- a/apps/dotcom/client/src/tla/components/TlaSidebar/sidebar.module.css +++ b/apps/dotcom/client/src/tla/components/TlaSidebar/sidebar.module.css @@ -29,30 +29,6 @@ border-left: 1px solid transparent; } -.sidebarDragOverlay { - position: absolute; - inset: 0px; - display: flex; - align-items: center; - justify-content: center; - font-size: 12px; - font-weight: 600; - z-index: 1000; - background-color: var(--tla-color-sidebar); - content: 'Upload .tldr files'; - opacity: 0; - animation: 0.2s fade-in 0.25s forwards; -} - -@keyframes fade-in { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - .sidebarOverlayMobile { all: unset; position: absolute; @@ -154,10 +130,6 @@ visibility: visible; } -.hoverable:has(*[data-state='open']) .collaborator { - display: none; -} - @media (hover: hover) { .hoverable:hover::after { visibility: visible; @@ -284,19 +256,6 @@ width: 64px; } -.sidebarWorkspaceButtonLabel { - /* for alignment with other buttons */ - display: block; - flex-grow: 2; - flex: 1 1 auto; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding: 0 0px; - position: relative; - z-index: 1; -} - /* Create file button */ .sidebarCreateFileButton { @@ -1035,30 +994,6 @@ } } -/* Renaming */ - -.sidebarFileLinkRenameInputWrapper { - background-color: var(--tla-color-sidebar); - outline: 1px solid var(--tl-color-primary); - outline-offset: -2px; - border-radius: 6px; -} - -.sidebarFileLinkRenameInputWrapper > div { - height: 36px; - padding: 0px 8px 0px 8px; -} - -.sidebarFileLinkRenameInput { - height: 36px; - flex: 1 1 auto; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - position: relative; - z-index: 1; -} - /* Guest badge */ .sidebarFileListItemGuestBadge { @@ -1086,92 +1021,6 @@ color: var(--tla-color-text-1); } -/* Dot dev link */ - -.sidebarDotDevLink { - position: relative; - font-size: 12px; - border: 1px solid var(--tla-color-text-2); - border-radius: 2px; - margin-left: -4px; - margin-right: -6px; - margin-bottom: 8px; - padding: 2px; - display: flex; - align-items: center; - height: 36px; - justify-content: space-between; - background-color: var(--tla-color-sidebar); -} - -.sidebarDotDevLink:after { - content: ''; - position: absolute; - top: 4px; - left: -4px; - right: 4px; - bottom: -4px; - border-radius: 2px; - border: 1px dashed var(--tla-color-text-2); - z-index: -2; - pointer-events: none; -} - -.sidebarDotDevLink a { - position: relative; - height: 100%; - padding: 0px 8px; - display: flex; - align-items: center; - flex: 1; - gap: 4px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.sidebarDotDevLink a div { - position: relative; - transform: rotate(180deg); - flex-shrink: 0; - transition: transform 3s ease-in-out 0s; -} - -.sidebarDotDevDismissButton { - background: none; - border: none; - cursor: pointer; - position: relative; - height: 28px; - width: 28px; - display: flex; - align-items: center; - justify-content: center; - color: var(--tla-color-inactive); - transition: opacity 0.2s ease-in-out; - min-width: 0; -} - -.sidebarDotDevDismissButton div { - width: 12px; - height: 12px; -} - -@media (hover: hover) { - .sidebarDotDevLink:hover a div { - transform: translate(200px, 0px) rotate(180deg); - transition: transform 8s ease-in-out 1s; - } - - .sidebarDotDevLink:has(a:hover) .sidebarDotDevDismissButton { - opacity: 0; - } - - .sidebarDotDevLink:has(a:hover):after { - background-color: var(--tla-color-hover-2); - } -} - .sidebarFileSectionTitleText { flex: 1 1 auto; white-space: nowrap; diff --git a/apps/dotcom/client/src/tla/components/WorkspaceInviteHandler.tsx b/apps/dotcom/client/src/tla/components/WorkspaceInviteHandler.tsx index 8ec7dd12b3a6..b9d6db29a9e6 100644 --- a/apps/dotcom/client/src/tla/components/WorkspaceInviteHandler.tsx +++ b/apps/dotcom/client/src/tla/components/WorkspaceInviteHandler.tsx @@ -89,6 +89,7 @@ export function WorkspaceInviteHandler() { dialogs.addDialog({ id: 'workspace-invite-sign-in', component: (props) => , + preventBackgroundClose: true, // Push, not replace: dismissing leaves the ?invite URL behind in // history, so the invite stays a real back-navigable state rather than // being erased. diff --git a/apps/dotcom/client/src/tla/components/dialogs/CreateWorkspaceDialog.tsx b/apps/dotcom/client/src/tla/components/dialogs/CreateWorkspaceDialog.tsx index 184f89820bc0..e16928b3df77 100644 --- a/apps/dotcom/client/src/tla/components/dialogs/CreateWorkspaceDialog.tsx +++ b/apps/dotcom/client/src/tla/components/dialogs/CreateWorkspaceDialog.tsx @@ -13,7 +13,7 @@ import { defineMessages, F, useMsg } from '../../utils/i18n' import styles from './dialogs.module.css' const messages = defineMessages({ - title: { defaultMessage: 'Create workspace' }, + title: { defaultMessage: 'Create a workspace' }, name: { defaultMessage: 'Name' }, defaultName: { defaultMessage: 'New workspace' }, placeholder: { defaultMessage: 'Workspace name' }, @@ -59,11 +59,13 @@ export function CreateWorkspaceDialog({ onClose, onCreate }: CreateWorkspaceDial - -
- + +
+
+ +
{workspaceName}', + defaultMessage: + 'You have been invited to join {workspaceName}. Create a free account to continue.', }, }) @@ -203,9 +204,9 @@ function TlaEnterEmailStep({ strong: (chunks) => {chunks}, }} /> -
-
- + {/*
+
*/} + {/* */} ) : ( <> diff --git a/apps/dotcom/client/src/tla/components/dialogs/WorkspaceSettingsDialog.tsx b/apps/dotcom/client/src/tla/components/dialogs/WorkspaceSettingsDialog.tsx index 01fd349bfc4c..96a168ccd81c 100644 --- a/apps/dotcom/client/src/tla/components/dialogs/WorkspaceSettingsDialog.tsx +++ b/apps/dotcom/client/src/tla/components/dialogs/WorkspaceSettingsDialog.tsx @@ -1,10 +1,8 @@ import { MAX_WORKSPACE_NAME_LENGTH, Role, ZErrorCode, can } from '@tldraw/dotcom-shared' import { Tooltip as _Tooltip } from 'radix-ui' -import { useLayoutEffect, useRef, useState } from 'react' +import { useCallback, useLayoutEffect, useRef, useState } from 'react' import { useNavigate } from 'react-router-dom' import { - TldrawUiButton, - TldrawUiButtonLabel, TldrawUiDialogBody, TldrawUiDialogCloseButton, TldrawUiDialogHeader, @@ -47,12 +45,32 @@ interface WorkspaceSettingsDialogProps { onClose(): void } +// Returns a callback ref for a scroll container that shows its scrollbar only when the +// content actually overflows. The container stays `overflow: hidden` (see .tabPage) and +// gets the `.scrollable` class — flipping overflow to auto — once measurement shows the +// content is taller than the (max-height-capped) container. A ResizeObserver on the +// container and its content re-checks as members are added/removed or the window resizes. +function useScrollbarWhenScrollable() { + return useCallback((el: HTMLDivElement | null) => { + if (!el) return + const update = () => + el.classList.toggle(styles.scrollable, el.scrollHeight > el.clientHeight + 1) + update() + const observer = new ResizeObserver(update) + observer.observe(el) + for (const child of el.children) observer.observe(child) + return () => observer.disconnect() + }, []) +} + export function WorkspaceSettingsDialog({ workspaceId, onClose }: WorkspaceSettingsDialogProps) { const app = useApp() const { addDialog } = useDialogs() const [activeTab, setActiveTab] = useState('members') const [copiedInviteLink, setCopiedInviteLink] = useState(false) + const scrollableRef = useScrollbarWhenScrollable() + const namePlaceholderMsg = useMsg(messages.namePlaceholder) const ownerMsg = useMsg(messages.owner) const memberMsg = useMsg(messages.member) @@ -85,10 +103,12 @@ export function WorkspaceSettingsDialog({ workspaceId, onClose }: WorkspaceSetti }) if (!workspaceMembership) return null - // The home workspace has no settings to manage. - if (workspaceId === app.getHomeWorkspaceId()) return null const workspace = workspaceMembership.group if (!workspace) return null + // The home workspace is a private, single-member space: it can be renamed but not shared, + // joined, or deleted, so it shows the name field and a disabled "private workspace" invite + // note — no shareable invite link, members list, or settings tabs. + const isHomeWorkspace = workspaceId === app.getHomeWorkspaceId() const currentUser = workspaceMembership.groupMembers.find( (member) => member.userId === app.getUser().id @@ -283,178 +303,204 @@ export function WorkspaceSettingsDialog({ workspaceId, onClose }: WorkspaceSetti />
-
-
- -
- {inviteLinkEnabled ? ( - + {/* The home workspace is private and can't be shared, so show a disabled invite + control with a note pointing the user to creating a shared workspace instead. */} + {isHomeWorkspace && ( +
+
+ +
+ - ) : ( - - - - )} -
+

+ +

+
+ )} - - {/* The shared tabs inset their labels by the tab padding; pull the strip out + {/* Invite link, members, and settings apply only to shared workspaces. */} + {!isHomeWorkspace && ( + <> +
+
+ +
+ {inviteLinkEnabled ? ( + + + + ) : ( + + + + )} +
+ + + {/* The shared tabs inset their labels by the tab padding; pull the strip out by that amount so the first tab's label lines up with the headings, button, and member rows. The underline still spans the content width. */} -
- - - - - - - - -
+
+ + + + + + + + +
- -
-
- {members.map((member) => { - const isSelf = member.userId === app.getUser().id - // Whether this member is an owner; used to hide non-owner roles from - // viewers who can't manage the workspace. - const memberIsOwner = can(member.role, 'manageWorkspace') - // A workspace must keep at least one owner, so the last owner can't be - // removed (this also covers an owner removing themselves). - const canRemoveMember = !memberIsOwner || ownersCount > 1 - // For the same reason the last owner can't be demoted to member, so - // disable that option rather than letting the change silently no-op. - const memberRoleOptions = canRemoveMember - ? roleOptions - : roleOptions.map((option) => - option.value === 'member' ? { ...option, disabled: true } : option + +
+
+ {members.map((member) => { + const isSelf = member.userId === app.getUser().id + // Whether this member is an owner; used to hide non-owner roles from + // viewers who can't manage the workspace. + const memberIsOwner = can(member.role, 'manageWorkspace') + // A workspace must keep at least one owner, so the last owner can't be + // removed (this also covers an owner removing themselves). + const canRemoveMember = !memberIsOwner || ownersCount > 1 + // For the same reason the last owner can't be demoted to member, so + // disable that option rather than letting the change silently no-op. + const memberRoleOptions = canRemoveMember + ? roleOptions + : roleOptions.map((option) => + option.value === 'member' ? { ...option, disabled: true } : option + ) + return ( +
+
+ {member.userName.charAt(0).toUpperCase()} +
+ + {member.userName} + {isSelf ? ` (${youMsg})` : ''} + + {canManageWorkspace ? ( + + ) : ( + + ), + destructive: true, + disabled: !canRemoveMember, + tooltip: canRemoveMember ? undefined : ( + + ), + onSelect: () => + isSelf + ? openLeaveConfirmDialog() + : openRemoveConfirmDialog(member), + }, + ]} + onChange={async (value) => { + if (value === member.role) return + try { + await app.z.mutate.setWorkspaceMemberRole({ + workspaceId, + targetUserId: member.userId, + role: value, + }).client + } catch (err) { + console.error('Failed to change member role', err) + app.showMutationRejectionToast( + (err as Error).message as ZErrorCode + ) + } + }} + /> + ) : memberIsOwner ? ( + {roleLabels[member.role]} + ) : null} +
) - return ( -
-
- {member.userName.charAt(0).toUpperCase()} -
- - {member.userName} - {isSelf ? ` (${youMsg})` : ''} - - {canManageWorkspace ? ( - - ) : ( - - ), - destructive: true, - disabled: !canRemoveMember, - tooltip: canRemoveMember ? undefined : ( - - ), - onSelect: () => - isSelf ? openLeaveConfirmDialog() : openRemoveConfirmDialog(member), - }, - ]} - onChange={async (value) => { - if (value === member.role) return - try { - await app.z.mutate.setWorkspaceMemberRole({ - workspaceId, - targetUserId: member.userId, - role: value, - }).client - } catch (err) { - console.error('Failed to change member role', err) - app.showMutationRejectionToast((err as Error).message as ZErrorCode) - } - }} - /> - ) : memberIsOwner ? ( - {roleLabels[member.role]} - ) : null} -
- ) - })} -
-
-
+ })} +
+
+
- -
-
- {canManageWorkspace && ( - <> - - - - - - - - - - - - - )} - {canLeave ? ( - - - - - - ) : ( - - } - > - - + +
+
+ {canManageWorkspace && ( + <> + + + + + + + + + )} + {canLeave ? ( +
-
-
- + + ) : ( + + } + > + + + )} + {canManageWorkspace && ( + + )} +
+
+
+
+ + )}
) diff --git a/apps/dotcom/client/src/tla/components/dialogs/auth.module.css b/apps/dotcom/client/src/tla/components/dialogs/auth.module.css index 4942d5f780af..6b6b0de2d173 100644 --- a/apps/dotcom/client/src/tla/components/dialogs/auth.module.css +++ b/apps/dotcom/client/src/tla/components/dialogs/auth.module.css @@ -1,16 +1,3 @@ -/* Utility Classes */ -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; -} - /* Auth Dialog Container */ .authContainer { width: 450px; @@ -48,16 +35,6 @@ height: 100%; } -.authVerificationTitle { - text-align: center; - color: var(--tl-color-text-2); - font-size: 12px; - margin: 0; - line-height: 1.5; - text-wrap: balance; - font-weight: 600; -} - /* Description Text */ .authDescription { text-align: center; @@ -71,7 +48,7 @@ /* Cta button */ .authCtaButton { - height: 40px; + height: 36px; width: 100%; font-size: 12px; font-weight: 500; @@ -129,7 +106,7 @@ border: 1px solid var(--tl-color-muted-2); border-radius: var(--tl-radius-2); font-size: 12px; - height: 40px; + height: 36px; color: var(--tl-color-text-0); } @@ -169,31 +146,10 @@ text-wrap: balance; font-weight: 500; } -/* Continue Button */ -/* .authContinueButton { - width: 100%; - position: relative; - padding: 0; - font-size: 12px; - font-weight: 500; - justify-content: center; - color: var(--tl-color-text-1); - background-color: var(--tl-color-muted-2); - border: 1px solid var(--tl-color-muted-1); - border-radius: var(--tl-radius-2); - transition: background-color 0.12s ease; - margin-top: 16px; -} */ - .authContinueWithEmailButton { margin-top: var(--tl-space-4); } -.authButtonArrow { - margin-left: var(--tl-space-2); - font-size: 12px; -} - /* Checkbox */ .authCheckboxLabel { display: flex; @@ -227,10 +183,6 @@ position: relative; } -.authVerificationInput { - display: none; -} - /* OTP boxes */ .authOtpBoxes { display: grid; @@ -310,25 +262,6 @@ cursor: not-allowed; } -/* Switch Mode */ -.authSwitchMode { - text-align: center; - font-size: 12px; - color: var(--tl-color-text-2); - margin-top: var(--tl-space-2); -} - -.authSwitchModeButton { - background: none; - border: none; - color: var(--tl-color-selected); - text-decoration: underline; - cursor: pointer; - padding: 0; - font-size: 12px; - font-weight: 500; -} - .authTermsAcceptAndContinueButton { margin-top: var(--tl-space-4); } diff --git a/apps/dotcom/client/src/tla/components/dialogs/dialogs.module.css b/apps/dotcom/client/src/tla/components/dialogs/dialogs.module.css index 17dde5b4e915..6c995d1821f7 100644 --- a/apps/dotcom/client/src/tla/components/dialogs/dialogs.module.css +++ b/apps/dotcom/client/src/tla/components/dialogs/dialogs.module.css @@ -9,6 +9,7 @@ flex-direction: column; gap: var(--tl-space-4); max-width: 500px; + overflow: auto; } /* ----------------- Feedback dialog ---------------- */ @@ -175,11 +176,19 @@ padding: 0px; } +.dialogFieldLabelRow { + height: 40px; + display: flex; + align-items: center; +} + .dialogInput { + height: 36px; border: 1px solid var(--tla-color-border); border-radius: var(--tl-radius-2); background-color: var(--tl-color-muted-0); padding: 0px var(--tl-space-3); + margin: 2px 0px; display: flex; align-items: center; justify-content: center; @@ -190,25 +199,17 @@ border: 1px solid var(--tla-color-primary); } -.dialogFooter { - display: flex; - justify-content: flex-end; - flex-shrink: 0; -} - /* ----------------- Group Settings Dialog ---------------- */ .workspaceSettingsBody { + padding-top: 0px; max-width: 360px; + max-height: max(420px, 80%); + min-height: 420px; width: calc(100vw - 48px); - /* Even-width digits for the member count (e.g. the "Members (12)" tab). */ - font-variant-numeric: tabular-nums; } .section { - /* Sections are separated by spacing alone (no dividers), so the gap needs to be - clearly larger than the ~8px spacing within a section. */ - margin-bottom: 20px; display: flex; flex-direction: column; } @@ -219,11 +220,20 @@ } .sectionLabel { - display: block; - margin-bottom: 8px; + height: 36px; + display: flex; + align-items: center; font-weight: 500; } +/* Help text under a section's control (e.g. the home workspace's private note). */ +.sectionHelp { + margin: 8px 0 0; + text-align: center; + text-wrap: balance; + color: var(--tla-color-text-1); +} + .membersList { display: flex; flex-direction: column; @@ -262,7 +272,7 @@ also equals that 12px, the strip now spans the body's full width — so drop the tab nav's bottom rule inset to 0 and let it bleed to the modal edges. */ .workspaceTabs { - margin: 0 -12px; + margin: 20px -12px 0px -12px; --tabs-line-inset: 0px; /* Tighten the gap between the Members and Settings tabs (default leaves too much). */ --tabs-tab-gap: -12px; @@ -281,61 +291,54 @@ padding: 16px 12px 0; margin: 0 -12px; max-height: calc(70vh - 280px); + /* Hidden by default so no scrollbar shows when the content fits. The component + measures the content and adds .scrollable to flip overflow to auto only when it + actually overflows — avoids a permanent scrollbar/gutter on systems that always + render scrollbars. */ + overflow-y: hidden; +} + +.tabPage.scrollable { overflow-y: auto; } -/* Pull the rows out 12px so the menu buttons' built-in 12px label padding (and the - matching padded toggle row) land their labels back on the content edge, aligned - with the headings and tab labels. */ +/* The toggle row and the action links below it sit at the content edge (no inset), so + their labels line up with the section headings and tab labels above. */ .settingsPage { display: flex; flex-direction: column; - margin: 0 -12px; -} - -/* The action rows reuse TldrawUiButton type="menu", whose label sits 12px in (the - button's own padding). Pad the toggle row to match so every label lines up, and - match their 40px height (the control row is otherwise 32px) so the Settings rows - are evenly spaced. */ -.settingsControl { - padding: 0 12px; - min-height: 40px; -} - -/* Destructive action ("Delete workspace") — red label on the reused menu button. */ -.settingsDanger :global(.tlui-button__label) { - color: var(--tl-color-danger); + gap: 8px; } -.modalOverlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; +/* Regenerate / leave / delete render as text links rather than full menu buttons. + align-self keeps each one's hit target tight to its text, while the toggle row above + still stretches full width for its label + switch. */ +.inlineButton { + align-self: flex-start; + padding: 4px 0; + background: none; + border: none; + cursor: pointer; + font-size: 12px; + font-weight: 500; + color: var(--tla-color-text-1); + text-align: left; } -.modalContent { - background-color: white; - padding: 24px; - border-radius: 8px; - max-width: 400px; - text-align: center; +@media (hover: hover) { + .inlineButton:hover:not(:disabled) { + text-decoration: underline; + } } -.modalMessage { - margin-bottom: 16px; +.inlineButton:disabled { + color: var(--tla-color-text-3); + cursor: not-allowed; } -.modalActions { - display: flex; - gap: 8px; - justify-content: center; +/* Destructive action ("Delete workspace"). */ +.inlineButtonDanger { + color: var(--tl-color-danger); } .copyInviteLinkIconRight { diff --git a/apps/dotcom/client/src/tla/components/menu-items/menu-items.tsx b/apps/dotcom/client/src/tla/components/menu-items/menu-items.tsx index 05641816d6b4..be7afee370d0 100644 --- a/apps/dotcom/client/src/tla/components/menu-items/menu-items.tsx +++ b/apps/dotcom/client/src/tla/components/menu-items/menu-items.tsx @@ -4,7 +4,6 @@ import { DropdownMenu as _DropdownMenu } from 'radix-ui' import { useCallback, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { - ColorSchemeMenu, TLDRAW_FILE_EXTENSION, TldrawUiIcon, TldrawUiMenuCheckboxItem, @@ -41,7 +40,6 @@ const messages = defineMessages({ accountMenu: { defaultMessage: 'User settings' }, signOut: { defaultMessage: 'Sign out' }, importFile: { defaultMessage: 'Import file…' }, - dotdev: { defaultMessage: 'Try the tldraw SDK' }, // account menu getHelp: { defaultMessage: 'User manual' }, legalSummary: { defaultMessage: 'Legal summary' }, @@ -88,12 +86,6 @@ export function SignOutMenuItem() { ) } -export function ColorThemeSubmenu() { - const editor = useMaybeEditor() - if (!editor) return null - return -} - const COLOR_SCHEMES = [ { colorScheme: 'light' as const, label: 'theme.light' }, { colorScheme: 'dark' as const, label: 'theme.dark' }, @@ -321,24 +313,6 @@ export function GiveUsFeedbackMenuItem() { ) } -export function DotDevMenuItem() { - const openAndTrack = useOpenUrlAndTrack('main-menu') - return ( - { - openAndTrack( - 'https://tldraw.dev?utm_source=dotcom&utm_medium=organic&utm_campaign=sidebar-menu', - true - ) - }} - /> - ) -} - export function LegalSummaryMenuItem() { const openAndTrack = useOpenUrlAndTrack('main-menu') return ( diff --git a/apps/dotcom/client/src/tla/components/tla-menu/menu.module.css b/apps/dotcom/client/src/tla/components/tla-menu/menu.module.css index e59b81deebcd..723eba1a694f 100644 --- a/apps/dotcom/client/src/tla/components/tla-menu/menu.module.css +++ b/apps/dotcom/client/src/tla/components/tla-menu/menu.module.css @@ -26,9 +26,11 @@ height: 32px; } -.menuControlRow > *:not(:only-child):nth-last-of-type(1) { +/* This 4px nudge on the last control threw off the left alignment of the rows it's used + in (workspace settings, the file share menu, the cookie dialog), so it's disabled. */ +/* .menuControlRow > *:not(:only-child):nth-last-of-type(1) { padding-left: 4px; -} +} */ .menuInfoTriggerContainer { flex-grow: 2; @@ -87,23 +89,10 @@ white-space: nowrap; } -.menuPopover { - padding: 40px 12px 12px 12px; - min-width: 160px; - max-width: 160px; - overflow: hidden; -} - .menuLabel + .menuInfoTrigger { margin-left: -12px; } -.menuPopoverClose { - position: absolute; - top: 0px; - right: 0px; -} - .menuDetailCentered { text-align: center; } @@ -211,10 +200,6 @@ div:has(> .menuSelectContent):not([data-radix-popper-content-wrapper]) { outline-offset: -5px; } -.menuSelectSelect:disabled { - cursor: default; -} - .menuSelectTrigger[data-disabled] .menuSelectLabel { color: var(--tla-color-text-3); } @@ -281,11 +266,6 @@ div:has(> .menuSelectContent):not([data-radix-popper-content-wrapper]) { cursor: pointer; pointer-events: all; } -.menuSwitchContainer.disabled input[type='checkbox'] { - pointer-events: none; - cursor: default; -} - .menuSwitch { --switch-h: 20px; --switch-w: calc(var(--switch-h) * 1.618); @@ -335,11 +315,11 @@ div:has(> .menuSelectContent):not([data-radix-popper-content-wrapper]) { } @media (hover: hover) { - .menuSwitchContainer:not(.disabled):hover > .menuSwitch[data-checked='false'] { + .menuSwitchContainer:hover > .menuSwitch[data-checked='false'] { background-color: var(--tla-color-inactive-hover); } - .menuSwitchContainer:not(.disabled):hover > .menuSwitch[data-checked='true'] { + .menuSwitchContainer:hover > .menuSwitch[data-checked='true'] { background-color: var(--tla-color-primary-hover); } } diff --git a/apps/dotcom/client/src/tla/styles/tla.css b/apps/dotcom/client/src/tla/styles/tla.css index 85dacad7971c..2cfb635ee31c 100644 --- a/apps/dotcom/client/src/tla/styles/tla.css +++ b/apps/dotcom/client/src/tla/styles/tla.css @@ -140,22 +140,6 @@ color: var(--tla-color-text-1); } -.tla-text_ui__section { - font-size: 12px; - line-height: 1.3; - font-weight: 500; - font-family: var(--tla-font-ui); - color: var(--tla-color-text-3); -} - -.tla-text_ui__tiny { - font-size: 6px; - line-height: 1.3; - font-weight: 800; - font-family: var(--tla-font-ui); - color: var(--tla-color-text-1); -} - .tla-text_ui__small { font-size: 11px; line-height: 1.3; @@ -186,27 +170,6 @@ text-rendering: optimizeLegibility; } -/* ---------------------- Auth ---------------------- */ - -.tla-auth_detail { - text-align: center; - color: var(--tla-color-text-3); -} - -.tla-auth_detail button { - background: none; - border: none; - display: inline; - cursor: pointer; - color: var(--tla-color-primary); -} - -.tla-file-header__share { - display: flex; - gap: 8px; - align-items: center; -} - /* --------------------- Editor --------------------- */ .tlui-layout__top { @@ -321,14 +284,6 @@ outline-offset: -5px; } -/** Checkbox */ -.tla:has(.tl-container__focused:not(.tl-container__no-focus-ring)) - :is(nav, .tlui-popover__content, .tlui-dialog__overlay) - .tla-form-checkbox:has(input:focus-visible) - > label { - outline: 2px solid var(--tl-color-focus); -} - /** Text buttons, specifically from the cookie consent */ .tla:has(.tl-container__focused:not(.tl-container__no-focus-ring)) nav a:focus-visible, .tla:has(.tl-container__focused:not(.tl-container__no-focus-ring)) diff --git a/apps/dotcom/client/src/tla/utils/app-ui-events.tsx b/apps/dotcom/client/src/tla/utils/app-ui-events.tsx index 41c89f800b2c..c1474f4e2612 100644 --- a/apps/dotcom/client/src/tla/utils/app-ui-events.tsx +++ b/apps/dotcom/client/src/tla/utils/app-ui-events.tsx @@ -1,5 +1,5 @@ import { TlaFile, TlaUser } from '@tldraw/dotcom-shared' -import { ReactNode, createContext, useContext } from 'react' +import { createContext, useContext } from 'react' import { trackEvent } from '../../utils/analytics' import { TldrawAppSessionState } from './local-session-state' @@ -84,21 +84,6 @@ const defaultEventHandler: TLAppUiContextType = trackEvent /** @internal */ export const EventsContext = createContext(defaultEventHandler) -/** @public */ -export interface TldrawAppUiEventsProviderProps { - onEvent?: TLAppUiHandler - children: ReactNode -} - -/** @public @react */ -export function TldrawAppUiEventsProvider({ onEvent, children }: TldrawAppUiEventsProviderProps) { - return ( - - {children} - - ) -} - /** @public */ export function useTldrawAppUiEvents(): TLAppUiContextType { const eventHandler = useContext(EventsContext) diff --git a/apps/dotcom/client/src/utils/shouldClearDocument.tsx b/apps/dotcom/client/src/utils/shouldClearDocument.tsx deleted file mode 100644 index 9038506d6948..000000000000 --- a/apps/dotcom/client/src/utils/shouldClearDocument.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { - TLUiDialogsContextType, - TldrawUiButton, - TldrawUiButtonLabel, - TldrawUiDialogBody, - TldrawUiDialogCloseButton, - TldrawUiDialogFooter, - TldrawUiDialogHeader, - TldrawUiDialogTitle, - useTranslation, -} from 'tldraw' - -export async function shouldClearDocument(addDialog: TLUiDialogsContextType['addDialog']) { - const shouldContinue = await new Promise((resolve) => { - addDialog({ - component: ({ onClose }) => ( - { - resolve(false) - onClose() - }} - onContinue={() => { - resolve(true) - onClose() - }} - /> - ), - onClose: () => { - resolve(false) - }, - }) - }) - - return shouldContinue -} - -function ConfirmClearDialog({ onCancel, onContinue }: { onCancel(): void; onContinue(): void }) { - const msg = useTranslation() - return ( - <> - - {msg('file-system.confirm-clear.title')} - - - - {msg('file-system.confirm-clear.description')} - - - - {msg('file-system.confirm-clear.cancel')} - - onContinue()}> - {msg('file-system.confirm-clear.continue')} - - - - ) -} diff --git a/apps/dotcom/client/styles/globals.css b/apps/dotcom/client/styles/globals.css index 388c709ff2ac..ba453b2f78b5 100644 --- a/apps/dotcom/client/styles/globals.css +++ b/apps/dotcom/client/styles/globals.css @@ -132,42 +132,6 @@ a { text-decoration: none; } -.icon { - flex-shrink: 0; - width: 20px; - height: 20px; - background-color: currentColor; -} - -.scroll-light { - scrollbar-width: thin; -} -.scroll-light::-webkit-scrollbar { - display: block; - width: 8px; - height: 8px; - position: absolute; - top: 0; - left: 0; - background-color: inherit; -} -.scroll-light::-webkit-scrollbar-button { - display: none; - width: 0; - height: 10px; -} -.scroll-light::-webkit-scrollbar-thumb { - background-clip: padding-box; - width: 0; - min-height: 36px; - border: 2px solid transparent; - border-radius: 6px; - background-color: rgba(0, 0, 0, 0.25); -} -.scroll-light::-webkit-scrollbar-thumb:hover { - background-color: rgba(0, 0, 0, 0.3); -} - /* ------------------- Error Page ------------------- */ .error-page { @@ -264,12 +228,6 @@ a { text-decoration: underline; } -.board-history__restore { - position: fixed; - top: 8px; - right: 8px; -} - .board-history__load-more { display: flex; justify-content: center; @@ -327,211 +285,10 @@ a { height: 14px; } -/* ------------------ Da Share Zone ----------------- */ - .tlui-style-panel__wrapper { margin-top: 4px; } -.tlui-share-zone__connection-status { - width: 8px; - height: 100%; - position: relative; - display: flex; - align-items: center; -} - -.tlui-share-zone__connection-status::after { - content: ''; - width: 8px; - height: 8px; - background-color: currentColor; - border-radius: 100%; -} - -.tlui-share-zone__button-wrapper { - all: unset; - padding: 2px; - position: relative; - pointer-events: all; - position: relative; - cursor: pointer; -} - -.tlui-share-zone__button { - font-family: inherit; - font-size: inherit; - height: 36px; - border: 2px solid var(--tl-color-background); - border-radius: 8px; - background-color: var(--tl-color-selected); - color: var(--tl-color-selected-contrast); - text-shadow: none; - pointer-events: all; - position: relative; - font-weight: 600; -} - -.tlui-share-zone__export-button { - font-family: inherit; - font-size: inherit; - height: 36px; - border: 2px solid var(--tl-color-text); - border-radius: 8px; - background-color: var(--tl-color-text); - color: var(--tl-color-selected-contrast); - text-shadow: none; - pointer-events: all; - position: relative; - font-weight: 600; -} - -.tlui-share-zone__button::before { - position: absolute; - display: block; - content: ''; - inset: 0px; - background-color: var(--tl-color-background); - border-top-left-radius: var(--tl-radius-3); - border-bottom-right-radius: var(--tl-radius-3); - border-bottom-left-radius: var(--tl-radius-3); - z-index: -1; -} - -.tlui-share-zone__button:active { - color: var(--tl-color-selected-contrast); -} - -@media (hover: hover) { - .tlui-share-zone__button:hover { - color: var(--tl-color-selected-contrast); - } - - .tlui-share-zone__button:not(:disabled, :focus-visible):hover { - color: var(--tl-color-selected-contrast); - } -} - -.tlui-share-zone__popover { - font-size: 12px; - font-weight: inherit; - width: 200px; - max-width: 100%; - max-height: 100%; - position: relative; -} - -.tlui-share-zone__qr-code { - width: 200px; - height: 200px; - cursor: pointer; - background: none; - background-color: var(--tl-color-muted-2); - background-size: cover; - border: none; -} - -.tlui-share-zone__spinner { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.tlui-share-zone__details { - font-size: 11px; - font-weight: 400; - padding: var(--tl-space-4); - color: var(--tl-color-text-1); - line-height: 1.5; - margin: 0px; -} - -.tlui-share-zone__status { - height: 100%; - display: flex; - align-items: center; - justify-content: center; - padding: 4px; - position: relative; - left: -4px; -} - -.tlui-share-zone__status > div { - width: 8px; - height: 8px; - border-radius: 100%; -} - -/* ------------------- People Menu ------------------- */ - -/* Document Name */ - -.tlui-document-name__inner { - border-radius: calc(var(--tl-radius-2) + var(--tl-space-2)); - background-color: var(--tl-color-background); - padding: 0; - display: flex; - align-items: center; - justify-content: center; - height: 36px; - margin-top: 4px; - padding-left: 4px; - max-width: 100%; - color: var(--tl-color-text); - text-shadow: 1px 1px 0px var(--tl-color-background); - pointer-events: all; -} - -.tlui-document-name__input__wrapper { - position: relative; - max-width: calc(100% - 36px); - display: flex; - flex: auto; -} - -.tlui-document-name__input, -.tlui-document-name__text { - padding: var(--tl-space-2) var(--tl-space-3); - white-space: pre; - line-height: 24px; - min-width: 62px; -} - -.tlui-document-name__input { - position: absolute; - padding: var(--tl-space-1) var(--tl-space-3); - margin-top: 2px; - width: 100%; - border-radius: var(--tl-radius-2); - color: var(--tl-color-text); - background: transparent; -} -.tlui-document-name__input:focus { - box-shadow: inset 0px 0px 0px 1px var(--tl-color-selected); - border: none; -} - -.tlui-document-name__text { - width: 100%; - color: var(--tl-color-text); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tlui-document-name__text__hidden { - opacity: 0; - pointer-events: none; -} - -.tlui-document-name__menu { - width: 36px; - min-width: 36px; - height: 36px; -} - .tlui-guest-icon { /* https://stackoverflow.com/a/23735769/1810018 */ -webkit-transform: translateZ(0);