setFocusedEditor('C')} style={{ height: 600 }}>
+
setFocusedEditor((window as any).EDITOR_C)}
+ style={{ height: 600 }}
+ >
{
;(window as any).EDITOR_C = editor
}}
diff --git a/apps/examples/src/examples/scroll/ScrollExample.tsx b/apps/examples/src/examples/scroll/ScrollExample.tsx
index 49e2cf025..5b582e2ae 100644
--- a/apps/examples/src/examples/scroll/ScrollExample.tsx
+++ b/apps/examples/src/examples/scroll/ScrollExample.tsx
@@ -14,10 +14,7 @@ export default function ScrollExample() {
}}
>
-
+
)
diff --git a/apps/vscode/editor/src/app.tsx b/apps/vscode/editor/src/app.tsx
index be1bc9261..7b82b381a 100644
--- a/apps/vscode/editor/src/app.tsx
+++ b/apps/vscode/editor/src/app.tsx
@@ -126,7 +126,6 @@ function TldrawInner({ uri, assetSrc, isDarkMode, fileContents }: TLDrawInnerPro
persistenceKey={uri}
onMount={handleMount}
components={components}
- autoFocus
>
{/*
*/}
diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md
index a8fa65e97..9cd534bdc 100644
--- a/packages/editor/api-report.md
+++ b/packages/editor/api-report.md
@@ -666,7 +666,7 @@ export class Edge2d extends Geometry2d {
// @public (undocumented)
export class Editor extends EventEmitter
{
- constructor({ store, user, shapeUtils, bindingUtils, tools, getContainer, cameraOptions, initialState, inferDarkMode, }: TLEditorOptions);
+ constructor({ store, user, shapeUtils, bindingUtils, tools, getContainer, cameraOptions, initialState, autoFocus, inferDarkMode, }: TLEditorOptions);
addOpenMenu(id: string): this;
alignShapes(shapes: TLShape[] | TLShapeId[], operation: 'bottom' | 'center-horizontal' | 'center-vertical' | 'left' | 'right' | 'top'): this;
animateShape(partial: null | TLShapePartial | undefined, opts?: Partial<{
@@ -774,6 +774,7 @@ export class Editor extends EventEmitter {
findCommonAncestor(shapes: TLShape[] | TLShapeId[], predicate?: (shape: TLShape) => boolean): TLShapeId | undefined;
findShapeAncestor(shape: TLShape | TLShapeId, predicate: (parent: TLShape) => boolean): TLShape | undefined;
flipShapes(shapes: TLShape[] | TLShapeId[], operation: 'horizontal' | 'vertical'): this;
+ focus(): this;
getAncestorPageId(shape?: TLShape | TLShapeId): TLPageId | undefined;
getAsset(asset: TLAsset | TLAssetId): TLAsset | undefined;
getAssetForExternalContent(info: TLExternalAssetContent): Promise;
@@ -2201,6 +2202,7 @@ export type TLEditorComponents = Partial<{
// @public (undocumented)
export interface TLEditorOptions {
+ autoFocus?: boolean;
bindingUtils: readonly TLBindingUtilConstructor[];
cameraOptions?: Partial;
getContainer: () => HTMLElement;
diff --git a/packages/editor/src/lib/TldrawEditor.tsx b/packages/editor/src/lib/TldrawEditor.tsx
index 565a73607..cb1ce795e 100644
--- a/packages/editor/src/lib/TldrawEditor.tsx
+++ b/packages/editor/src/lib/TldrawEditor.tsx
@@ -30,10 +30,8 @@ import {
useEditorComponents,
} from './hooks/useEditorComponents'
import { useEvent } from './hooks/useEvent'
-import { useFocusEvents } from './hooks/useFocusEvents'
import { useForceUpdate } from './hooks/useForceUpdate'
import { useLocalStore } from './hooks/useLocalStore'
-import { useSafariFocusOutFix } from './hooks/useSafariFocusOutFix'
import { useZoomCss } from './hooks/useZoomCss'
import { stopEventPropagation } from './utils/dom'
import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
@@ -305,6 +303,7 @@ function TldrawEditorWithReadyStore({
const { ErrorFallback } = useEditorComponents()
const container = useContainer()
const [editor, setEditor] = useState(null)
+ const [initialAutoFocus] = useState(autoFocus)
useLayoutEffect(() => {
const editor = new Editor({
@@ -315,6 +314,7 @@ function TldrawEditorWithReadyStore({
getContainer: () => container,
user,
initialState,
+ autoFocus: initialAutoFocus,
inferDarkMode,
cameraOptions,
})
@@ -331,6 +331,7 @@ function TldrawEditorWithReadyStore({
store,
user,
initialState,
+ initialAutoFocus,
inferDarkMode,
cameraOptions,
])
@@ -374,30 +375,18 @@ function TldrawEditorWithReadyStore({
) : (
-
- {children ?? (Canvas ? : null)}
-
+ {children ?? (Canvas ? : null)}
)}
)
}
-function Layout({
- children,
- onMount,
- autoFocus,
-}: {
- children: ReactNode
- autoFocus: boolean
- onMount?: TLOnMountHandler
-}) {
+function Layout({ children, onMount }: { children: ReactNode; onMount?: TLOnMountHandler }) {
useZoomCss()
useCursor()
useDarkMode()
- useSafariFocusOutFix()
useForceUpdate()
- useFocusEvents(autoFocus)
useOnMount(onMount)
return (
diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts
index eecc85b6a..64596a0c2 100644
--- a/packages/editor/src/lib/editor/Editor.ts
+++ b/packages/editor/src/lib/editor/Editor.ts
@@ -131,6 +131,7 @@ import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage
import { getSvgJsx } from './getSvgJsx'
import { ClickManager } from './managers/ClickManager'
import { EnvironmentManager } from './managers/EnvironmentManager'
+import { FocusManager } from './managers/FocusManager'
import { HistoryManager } from './managers/HistoryManager'
import { ScribbleManager } from './managers/ScribbleManager'
import { SnapManager } from './managers/SnapManager/SnapManager'
@@ -203,6 +204,10 @@ export interface TLEditorOptions {
* The editor's initial active tool (or other state node id).
*/
initialState?: string
+ /**
+ * Whether to automatically focus the editor when it mounts.
+ */
+ autoFocus?: boolean
/**
* Whether to infer dark mode from the user's system preferences. Defaults to false.
*/
@@ -224,6 +229,7 @@ export class Editor extends EventEmitter {
getContainer,
cameraOptions,
initialState,
+ autoFocus,
inferDarkMode,
}: TLEditorOptions) {
super()
@@ -677,6 +683,9 @@ export class Editor extends EventEmitter {
this.root.enter(undefined, 'initial')
+ this.focusManager = new FocusManager(this, autoFocus)
+ this.disposables.add(this.focusManager.dispose.bind(this.focusManager))
+
if (this.getInstanceState().followingUserId) {
this.stopFollowingUser()
}
@@ -756,6 +765,13 @@ export class Editor extends EventEmitter {
*/
readonly sideEffects: StoreSideEffects
+ /**
+ * A manager for ensuring correct focus. See FocusManager for details.
+ *
+ * @internal
+ */
+ private focusManager: FocusManager
+
/**
* The current HTML element containing the editor.
*
@@ -8066,6 +8082,21 @@ export class Editor extends EventEmitter {
return this
}
+ /**
+ * Dispatch a focus event.
+ *
+ * @example
+ * ```ts
+ * editor.focus()
+ * ```
+ *
+ * @public
+ */
+ focus(): this {
+ this.focusManager.focus()
+ return this
+ }
+
/**
* A manager for recording multiple click events.
*
diff --git a/packages/editor/src/lib/editor/managers/FocusManager.ts b/packages/editor/src/lib/editor/managers/FocusManager.ts
new file mode 100644
index 000000000..3cd21d4ff
--- /dev/null
+++ b/packages/editor/src/lib/editor/managers/FocusManager.ts
@@ -0,0 +1,46 @@
+import type { Editor } from '../Editor'
+
+/**
+ * A manager for ensuring correct focus across the editor.
+ * It will listen for changes in the instance state to make sure the
+ * container is focused when the editor is focused.
+ * Also, it will make sure that the focus is on things like text
+ * labels when the editor is in editing mode.
+ *
+ * @internal
+ */
+export class FocusManager {
+ private disposeSideEffectListener?: () => void
+
+ constructor(
+ public editor: Editor,
+ autoFocus?: boolean
+ ) {
+ this.disposeSideEffectListener = editor.sideEffects.registerAfterChangeHandler(
+ 'instance',
+ (prev, next) => {
+ if (prev.isFocused !== next.isFocused) {
+ next.isFocused ? this.focus() : this.blur()
+ }
+ }
+ )
+
+ const currentFocusState = editor.getInstanceState().isFocused
+ if (autoFocus !== currentFocusState) {
+ editor.updateInstanceState({ isFocused: !!autoFocus })
+ }
+ }
+
+ focus() {
+ this.editor.getContainer().focus()
+ }
+
+ blur() {
+ this.editor.complete() // stop any interaction
+ this.editor.getContainer().blur() // blur the container
+ }
+
+ dispose() {
+ this.disposeSideEffectListener?.()
+ }
+}
diff --git a/packages/editor/src/lib/hooks/useCanvasEvents.ts b/packages/editor/src/lib/hooks/useCanvasEvents.ts
index f0aabfbbb..beea46239 100644
--- a/packages/editor/src/lib/hooks/useCanvasEvents.ts
+++ b/packages/editor/src/lib/hooks/useCanvasEvents.ts
@@ -40,15 +40,6 @@ export function useCanvasEvents() {
name: 'pointer_down',
...getPointerInfo(e),
})
-
- if (editor.getOpenMenus().length > 0) {
- editor.updateInstanceState({
- openMenus: [],
- })
-
- document.body.click()
- editor.getContainer().focus()
- }
}
function onPointerMove(e: React.PointerEvent) {
@@ -98,9 +89,6 @@ export function useCanvasEvents() {
function onTouchStart(e: React.TouchEvent) {
;(e as any).isKilled = true
- // todo: investigate whether this effects keyboard shortcuts
- // god damn it, but necessary for long presses to open the context menu
- document.body.click()
preventDefault(e)
}
diff --git a/packages/editor/src/lib/hooks/useDocumentEvents.ts b/packages/editor/src/lib/hooks/useDocumentEvents.ts
index 6b7fa7b6f..e8325168b 100644
--- a/packages/editor/src/lib/hooks/useDocumentEvents.ts
+++ b/packages/editor/src/lib/hooks/useDocumentEvents.ts
@@ -125,7 +125,7 @@ export function useDocumentEvents() {
// will break additional shortcuts. We need to
// refocus the container in order to keep these
// shortcuts working.
- container.focus()
+ editor.focus()
}
return
}
diff --git a/packages/editor/src/lib/hooks/useFocusEvents.ts b/packages/editor/src/lib/hooks/useFocusEvents.ts
deleted file mode 100644
index e2bc7fe68..000000000
--- a/packages/editor/src/lib/hooks/useFocusEvents.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useLayoutEffect } from 'react'
-import { useContainer } from './useContainer'
-import { useEditor } from './useEditor'
-
-/** @internal */
-export function useFocusEvents(autoFocus: boolean) {
- const editor = useEditor()
- const container = useContainer()
- useLayoutEffect(() => {
- if (autoFocus) {
- // When autoFocus is true, update the editor state to be focused
- // unless it's already focused
- if (!editor.getInstanceState().isFocused) {
- editor.updateInstanceState({ isFocused: true })
- }
-
- // Note: Focus is also handled by the side effect manager in tldraw.
- // Importantly, if a user manually sets isFocused to true (or if it
- // changes for any reason from false to true), the side effect manager
- // in tldraw will also take care of the focus. However, it may be that
- // on first mount the editor already has isFocused: true in the model,
- // so we also need to focus it here just to be sure.
- editor.getContainer().focus()
- } else {
- // When autoFocus is false, update the editor state to be not focused
- // unless it's already not focused
- if (editor.getInstanceState().isFocused) {
- editor.updateInstanceState({ isFocused: false })
- }
- }
- }, [editor, container, autoFocus])
-}
diff --git a/packages/editor/src/lib/hooks/useSafariFocusOutFix.ts b/packages/editor/src/lib/hooks/useSafariFocusOutFix.ts
deleted file mode 100644
index 40029f09d..000000000
--- a/packages/editor/src/lib/hooks/useSafariFocusOutFix.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import * as React from 'react'
-import { useEditor } from './useEditor'
-
-let isMobileSafari = false
-
-if (typeof window !== 'undefined') {
- const ua = window.navigator.userAgent
- const iOS = !!ua.match(/iPad/i) || !!ua.match(/iPhone/i)
- const webkit = !!ua.match(/WebKit/i)
- isMobileSafari = iOS && webkit && !ua.match(/CriOS/i)
-}
-
-export function useSafariFocusOutFix(): void {
- const editor = useEditor()
-
- React.useEffect(() => {
- if (!isMobileSafari) return
-
- function handleFocusOut(e: FocusEvent) {
- if (
- (e.target instanceof HTMLInputElement && e.target.type === 'text') ||
- e.target instanceof HTMLTextAreaElement
- ) {
- editor.complete()
- }
- }
-
- // Send event on iOS when a user presses the "Done" key while editing a text element.
- document.addEventListener('focusout', handleFocusOut)
- return () => document.removeEventListener('focusout', handleFocusOut)
- }, [editor])
-}
diff --git a/packages/store/src/lib/RecordType.ts b/packages/store/src/lib/RecordType.ts
index 9fd0dea7e..7f71be3e8 100644
--- a/packages/store/src/lib/RecordType.ts
+++ b/packages/store/src/lib/RecordType.ts
@@ -8,7 +8,7 @@ export type RecordTypeRecord> = ReturnType void;
+ handleBlur: typeof noop;
handleChange: (e: React_2.ChangeEvent) => void;
handleDoubleClick: (e: any) => any;
handleFocus: typeof noop;
diff --git a/packages/tldraw/src/lib/defaultSideEffects.ts b/packages/tldraw/src/lib/defaultSideEffects.ts
index f3c04b187..45a91e0ba 100644
--- a/packages/tldraw/src/lib/defaultSideEffects.ts
+++ b/packages/tldraw/src/lib/defaultSideEffects.ts
@@ -2,16 +2,6 @@ import { Editor } from '@tldraw/editor'
export function registerDefaultSideEffects(editor: Editor) {
return [
- editor.sideEffects.registerAfterChangeHandler('instance', (prev, next) => {
- if (prev.isFocused !== next.isFocused) {
- if (next.isFocused) {
- editor.getContainer().focus()
- } else {
- editor.complete() // stop any interaction
- editor.getContainer().blur() // blur the container
- }
- }
- }),
editor.sideEffects.registerAfterChangeHandler('instance_page_state', (prev, next) => {
if (prev.croppingShapeId !== next.croppingShapeId) {
const isInCroppingState = editor.isInAny(
diff --git a/packages/tldraw/src/lib/shapes/frame/components/FrameHeading.tsx b/packages/tldraw/src/lib/shapes/frame/components/FrameHeading.tsx
index b03c5fe7e..7af6fe235 100644
--- a/packages/tldraw/src/lib/shapes/frame/components/FrameHeading.tsx
+++ b/packages/tldraw/src/lib/shapes/frame/components/FrameHeading.tsx
@@ -58,14 +58,6 @@ export const FrameHeading = function FrameHeading({
// On iOS, we must focus here
el.focus()
el.select()
-
- requestAnimationFrame(() => {
- // On desktop, the input may have lost focus, so try try try again!
- if (document.activeElement !== el) {
- el.focus()
- el.select()
- }
- })
}
}, [rInput, isEditing])
diff --git a/packages/tldraw/src/lib/shapes/shared/useEditableText.ts b/packages/tldraw/src/lib/shapes/shared/useEditableText.ts
index aaf888b58..58523bf72 100644
--- a/packages/tldraw/src/lib/shapes/shared/useEditableText.ts
+++ b/packages/tldraw/src/lib/shapes/shared/useEditableText.ts
@@ -13,7 +13,6 @@ import { INDENT, TextHelpers } from './TextHelpers'
export function useEditableText(id: TLShapeId, type: string, text: string) {
const editor = useEditor()
const rInput = useRef(null)
- const rSelectionRanges = useRef()
const isEditing = useValue('isEditing', () => editor.getEditingShapeId() === id, [editor])
const isEditingAnything = useValue('isEditingAnything', () => !!editor.getEditingShapeId(), [
editor,
@@ -21,98 +20,36 @@ export function useEditableText(id: TLShapeId, type: string, text: string) {
useEffect(() => {
function selectAllIfEditing({ shapeId }: { shapeId: TLShapeId }) {
- // We wait a tick, because on iOS, the keyboard will not show if we focus immediately.
- requestAnimationFrame(() => {
- if (shapeId === id) {
- const elm = rInput.current
- if (elm) {
- if (document.activeElement !== elm) {
- elm.focus()
- }
- elm.select()
- }
- }
- })
+ if (shapeId === id) {
+ rInput.current?.select()
+ }
}
editor.on('select-all-text', selectAllIfEditing)
return () => {
editor.off('select-all-text', selectAllIfEditing)
}
- }, [editor, id])
+ }, [editor, id, isEditing])
useEffect(() => {
if (!isEditing) return
- const elm = rInput.current
- if (!elm) return
-
- // Focus if we're not already focused
- if (document.activeElement !== elm) {
- elm.focus()
-
- // On mobile etc, just select all the text when we start focusing
- if (editor.getInstanceState().isCoarsePointer) {
- elm.select()
- }
- } else {
- // This fixes iOS not showing the cursor sometimes. This "shakes" the cursor
- // awake.
- if (editor.environment.isSafari) {
- elm.blur()
- elm.focus()
- }
+ if (document.activeElement !== rInput.current) {
+ rInput.current?.focus()
}
- // When the selection changes, save the selection ranges
- function updateSelection() {
- const selection = window.getSelection?.()
- if (selection && selection.type !== 'None') {
- const ranges: Range[] = []
- for (let i = 0; i < selection.rangeCount; i++) {
- ranges.push(selection.getRangeAt?.(i))
- }
- rSelectionRanges.current = ranges
- }
+ if (editor.getInstanceState().isCoarsePointer) {
+ rInput.current?.select()
}
- document.addEventListener('selectionchange', updateSelection)
- return () => {
- document.removeEventListener('selectionchange', updateSelection)
+ // XXX(mime): This fixes iOS not showing the cursor sometimes.
+ // This "shakes" the cursor awake.
+ if (editor.environment.isSafari) {
+ rInput.current?.blur()
+ rInput.current?.focus()
}
}, [editor, isEditing])
- // 2. Restore the selection changes (and focus) if the element blurs
- // When the label blurs, deselect all of the text and complete.
- // This makes it so that the canvas does not have to be focused
- // in order to exit the editing state and complete the editing state
- const handleBlur = useCallback(() => {
- const ranges = rSelectionRanges.current
-
- requestAnimationFrame(() => {
- const elm = rInput.current
- const editingShapeId = editor.getEditingShapeId()
-
- // Did we move to a different shape?
- if (editingShapeId) {
- // important! these ^v are two different things
- // is that shape OUR shape?
- if (elm && editingShapeId === id) {
- elm.focus()
-
- if (ranges && ranges.length) {
- const selection = window.getSelection()
- if (selection) {
- ranges.forEach((range) => selection.addRange(range))
- }
- }
- }
- } else {
- window.getSelection()?.removeAllRanges()
- }
- })
- }, [editor, id])
-
// When the user presses ctrl / meta enter, complete the editing state.
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
@@ -186,7 +123,7 @@ export function useEditableText(id: TLShapeId, type: string, text: string) {
return {
rInput,
handleFocus: noop,
- handleBlur,
+ handleBlur: noop,
handleKeyDown,
handleChange,
handleInputPointerDown,
diff --git a/packages/tldraw/src/lib/shapes/text/TextArea.tsx b/packages/tldraw/src/lib/shapes/text/TextArea.tsx
index 1c14bf6cd..d71d03a0d 100644
--- a/packages/tldraw/src/lib/shapes/text/TextArea.tsx
+++ b/packages/tldraw/src/lib/shapes/text/TextArea.tsx
@@ -36,7 +36,6 @@ export const TextArea = forwardRef(function
autoCapitalize="off"
autoCorrect="off"
autoSave="off"
- // autoFocus
placeholder=""
spellCheck="true"
wrap="off"
diff --git a/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingShape.ts b/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingShape.ts
index 1355a8ac0..67efe39ab 100644
--- a/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingShape.ts
+++ b/packages/tldraw/src/lib/tools/SelectTool/childStates/PointingShape.ts
@@ -217,7 +217,7 @@ export class PointingShape extends StateNode {
if (this.editor.getInstanceState().isReadonly) return
// Re-focus the editor, just in case the text label of the shape has stolen focus
- this.editor.getContainer().focus()
+ this.editor.focus()
this.parent.transition('translating', info)
}
diff --git a/packages/tldraw/src/lib/ui/components/EditLinkDialog.tsx b/packages/tldraw/src/lib/ui/components/EditLinkDialog.tsx
index b13d25313..b021f1721 100644
--- a/packages/tldraw/src/lib/ui/components/EditLinkDialog.tsx
+++ b/packages/tldraw/src/lib/ui/components/EditLinkDialog.tsx
@@ -150,7 +150,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
ref={rInput}
className="tlui-edit-link-dialog__input"
label="edit-link-dialog.url"
- autofocus
+ autoFocus
value={urlInputState.actual}
onValueChange={handleChange}
onComplete={handleComplete}
diff --git a/packages/tldraw/src/lib/ui/components/EmbedDialog.tsx b/packages/tldraw/src/lib/ui/components/EmbedDialog.tsx
index 598c6dade..0aad51e53 100644
--- a/packages/tldraw/src/lib/ui/components/EmbedDialog.tsx
+++ b/packages/tldraw/src/lib/ui/components/EmbedDialog.tsx
@@ -51,7 +51,7 @@ export const EmbedDialog = track(function EmbedDialog({ onClose }: TLUiDialogPro
className="tlui-embed-dialog__input"
label="embed-dialog.url"
placeholder="http://example.com"
- autofocus
+ autoFocus
onValueChange={(value) => {
// Set the url that the user has typed into the input
setUrl(value)
diff --git a/packages/tldraw/src/lib/ui/components/PageMenu/PageItemInput.tsx b/packages/tldraw/src/lib/ui/components/PageMenu/PageItemInput.tsx
index b80aa34e2..659206152 100644
--- a/packages/tldraw/src/lib/ui/components/PageMenu/PageItemInput.tsx
+++ b/packages/tldraw/src/lib/ui/components/PageMenu/PageItemInput.tsx
@@ -34,8 +34,8 @@ export const PageItemInput = function PageItemInput({
onValueChange={handleChange}
onFocus={handleFocus}
shouldManuallyMaintainScrollPositionWhenFocused
- autofocus={isCurrentPage}
- autoselect
+ autoFocus={isCurrentPage}
+ autoSelect
/>
)
}
diff --git a/packages/tldraw/src/lib/ui/components/primitives/Button/TldrawUiButton.tsx b/packages/tldraw/src/lib/ui/components/primitives/Button/TldrawUiButton.tsx
index 73681b403..014927f28 100644
--- a/packages/tldraw/src/lib/ui/components/primitives/Button/TldrawUiButton.tsx
+++ b/packages/tldraw/src/lib/ui/components/primitives/Button/TldrawUiButton.tsx
@@ -1,4 +1,3 @@
-import { useEditor } from '@tldraw/editor'
import classnames from 'classnames'
import * as React from 'react'
@@ -11,15 +10,6 @@ export interface TLUiButtonProps extends React.HTMLAttributes
/** @public */
export const TldrawUiButton = React.forwardRef(
function TldrawUiButton({ children, disabled, type, ...props }, ref) {
- const editor = useEditor()
-
- // If the button is getting disabled while it's focused, move focus to the editor
- // so that the user can continue using keyboard shortcuts
- const current = (ref as React.MutableRefObject)?.current
- if (disabled && current === document.activeElement) {
- editor.getContainer().focus()
- }
-
return (