-
-
-
+
+
+
+
+
+
+
{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 && (
-
-
-
-
-
- )}
-
-
-
-
+
+ ) : (
+
+ }
+ >
+
+
+
+
+ )}
+ {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);