Tldraw/packages/tldraw/src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx

299 wiersze
7.5 KiB
TypeScript

import {
DebugFlag,
Editor,
TLShapePartial,
createShapeId,
debugFlags,
featureFlags,
hardResetEditor,
track,
uniqueId,
useEditor,
} from '@tldraw/editor'
import React from 'react'
import { useDialogs } from '../../context/dialogs'
import { useToasts } from '../../context/toasts'
import { untranslated } from '../../hooks/useTranslation/useTranslation'
import { TldrawUiButton } from '../primitives/Button/TldrawUiButton'
import { TldrawUiButtonCheck } from '../primitives/Button/TldrawUiButtonCheck'
import { TldrawUiButtonLabel } from '../primitives/Button/TldrawUiButtonLabel'
import {
TldrawUiDialogBody,
TldrawUiDialogCloseButton,
TldrawUiDialogFooter,
TldrawUiDialogHeader,
TldrawUiDialogTitle,
} from '../primitives/TldrawUiDialog'
import { TldrawUiMenuCheckboxItem } from '../primitives/menus/TldrawUiMenuCheckboxItem'
import { TldrawUiMenuGroup } from '../primitives/menus/TldrawUiMenuGroup'
import { TldrawUiMenuItem } from '../primitives/menus/TldrawUiMenuItem'
import { TldrawUiMenuSubmenu } from '../primitives/menus/TldrawUiMenuSubmenu'
/** @public */
export function DefaultDebugMenuContent() {
const editor = useEditor()
const { addToast } = useToasts()
const { addDialog } = useDialogs()
const [error, setError] = React.useState<boolean>(false)
return (
<>
<TldrawUiMenuGroup id="items">
<TldrawUiMenuItem
id="add-toast"
onSelect={() => {
addToast({
id: uniqueId(),
title: 'Something good happened',
description: 'Hey, attend to this thing over here. It might be important!',
keepOpen: true,
severity: 'success',
// icon?: string
// title?: string
// description?: string
// actions?: TLUiToastAction[]
})
addToast({
id: uniqueId(),
title: 'Something happened',
description: 'Hey, attend to this thing over here. It might be important!',
keepOpen: true,
severity: 'info',
actions: [
{
label: 'Primary',
type: 'primary',
onClick: () => {
void null
},
},
{
label: 'Normal',
type: 'normal',
onClick: () => {
void null
},
},
{
label: 'Danger',
type: 'danger',
onClick: () => {
void null
},
},
],
// icon?: string
// title?: string
// description?: string
// actions?: TLUiToastAction[]
})
addToast({
id: uniqueId(),
title: 'Something maybe bad happened',
description: 'Hey, attend to this thing over here. It might be important!',
keepOpen: true,
severity: 'warning',
actions: [
{
label: 'Primary',
type: 'primary',
onClick: () => {
void null
},
},
{
label: 'Normal',
type: 'normal',
onClick: () => {
void null
},
},
{
label: 'Danger',
type: 'danger',
onClick: () => {
void null
},
},
],
})
addToast({
id: uniqueId(),
title: 'Something bad happened',
severity: 'error',
keepOpen: true,
})
}}
label={untranslated('Show toast')}
/>
<TldrawUiMenuItem
id="show-dialog"
label={'Show dialog'}
onSelect={() => {
addDialog({
component: ({ onClose }) => (
<ExampleDialog
displayDontShowAgain
onCancel={() => onClose()}
onContinue={() => onClose()}
/>
),
onClose: () => {
void null
},
})
}}
/>
<TldrawUiMenuItem
id="create-shapes"
label={'Create 100 shapes'}
onSelect={() => createNShapes(editor, 100)}
/>
<TldrawUiMenuItem
id="count-nodes"
label={'Count shapes / nodes'}
onSelect={() => {
const selectedShapes = editor.getSelectedShapes()
const shapes =
selectedShapes.length === 0 ? editor.getRenderingShapes() : selectedShapes
window.alert(
`Shapes ${shapes.length}, DOM nodes:${document.querySelector('.tl-shapes')!.querySelectorAll('*')?.length}`
)
}}
/>
{(() => {
if (error) throw Error('oh no!')
return null
})()}
<TldrawUiMenuItem id="throw-error" onSelect={() => setError(true)} label={'Throw error'} />
<TldrawUiMenuItem id="hard-reset" onSelect={hardResetEditor} label={'Hard reset'} />
</TldrawUiMenuGroup>
<TldrawUiMenuGroup id="flags">
<DebugFlags />
<FeatureFlags />
</TldrawUiMenuGroup>
{/* {...children} */}
</>
)
}
/** @public */
export function DebugFlags() {
const items = Object.values(debugFlags)
if (!items.length) return null
return (
<TldrawUiMenuSubmenu id="debug flags" label="Debug Flags">
<TldrawUiMenuGroup id="debug flags">
{items.map((flag) => (
<DebugFlagToggle key={flag.name} flag={flag} />
))}
</TldrawUiMenuGroup>
</TldrawUiMenuSubmenu>
)
}
/** @public */
export function FeatureFlags() {
const items = Object.values(featureFlags)
if (!items.length) return null
return (
<TldrawUiMenuSubmenu id="feature flags" label="Feature Flags">
<TldrawUiMenuGroup id="feature flags">
{items.map((flag) => (
<DebugFlagToggle key={flag.name} flag={flag} />
))}
</TldrawUiMenuGroup>
</TldrawUiMenuSubmenu>
)
}
/** @public */
export function ExampleDialog({
title = 'title',
body = 'hello hello hello',
cancel = 'Cancel',
confirm = 'Continue',
displayDontShowAgain = false,
onCancel,
onContinue,
}: {
title?: string
body?: string
cancel?: string
confirm?: string
displayDontShowAgain?: boolean
onCancel: () => void
onContinue: () => void
}) {
const [dontShowAgain, setDontShowAgain] = React.useState(false)
return (
<>
<TldrawUiDialogHeader>
<TldrawUiDialogTitle>{title}</TldrawUiDialogTitle>
<TldrawUiDialogCloseButton />
</TldrawUiDialogHeader>
<TldrawUiDialogBody style={{ maxWidth: 350 }}>{body}</TldrawUiDialogBody>
<TldrawUiDialogFooter className="tlui-dialog__footer__actions">
{displayDontShowAgain && (
<TldrawUiButton
type="normal"
onClick={() => setDontShowAgain(!dontShowAgain)}
style={{ marginRight: 'auto' }}
>
<TldrawUiButtonCheck checked={dontShowAgain} />
<TldrawUiButtonLabel>Don't show again</TldrawUiButtonLabel>
</TldrawUiButton>
)}
<TldrawUiButton type="normal" onClick={onCancel}>
<TldrawUiButtonLabel>{cancel}</TldrawUiButtonLabel>
</TldrawUiButton>
<TldrawUiButton type="primary" onClick={async () => onContinue()}>
<TldrawUiButtonLabel>{confirm}</TldrawUiButtonLabel>
</TldrawUiButton>
</TldrawUiDialogFooter>
</>
)
}
const DebugFlagToggle = track(function DebugFlagToggle({
flag,
onChange,
}: {
flag: DebugFlag<boolean>
onChange?: (newValue: boolean) => void
}) {
const value = flag.get()
return (
<TldrawUiMenuCheckboxItem
id={flag.name}
title={flag.name}
label={flag.name
.replace(/([a-z0-9])([A-Z])/g, (m) => `${m[0]} ${m[1].toLowerCase()}`)
.replace(/^[a-z]/, (m) => m.toUpperCase())}
checked={value}
onSelect={() => {
flag.set(!value)
onChange?.(!value)
}}
/>
)
})
let t = 0
function createNShapes(editor: Editor, n: number) {
const shapesToCreate: TLShapePartial[] = Array(n)
const cols = Math.floor(Math.sqrt(n))
for (let i = 0; i < n; i++) {
t++
shapesToCreate[i] = {
id: createShapeId('box' + t),
type: 'geo',
x: (i % cols) * 132,
y: Math.floor(i / cols) * 132,
}
}
editor.createShapes(shapesToCreate).setSelectedShapes(shapesToCreate.map((s) => s.id))
}