mirror of
https://github.com/langgenius/dify.git
synced 2026-06-07 16:23:44 +08:00
fix(web): style issue of add input field panel in human input form co… (#37102)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
d16a012575
commit
edeaac5d4e
@ -1666,11 +1666,6 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"web/app/components/base/prompt-editor/plugins/hitl-input-block/input-field.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"web/app/components/base/prompt-editor/plugins/last-run-block/index.tsx": {
|
||||
"no-barrel-files/no-barrel-files": {
|
||||
"count": 3
|
||||
|
||||
@ -5,7 +5,7 @@ import type {
|
||||
EditorState,
|
||||
} from 'lexical'
|
||||
import type { FC } from 'react'
|
||||
import type { Hotkey, ShortcutPopupInsertHandler } from './plugins/shortcuts-popup-plugin'
|
||||
import type { Hotkey, ShortcutPopupDisplayMode, ShortcutPopupInsertHandler } from './plugins/shortcuts-popup-plugin'
|
||||
import type {
|
||||
ContextBlockType,
|
||||
CurrentBlockType,
|
||||
@ -131,7 +131,11 @@ export type PromptEditorProps = {
|
||||
errorMessageBlock?: ErrorMessageBlockType
|
||||
lastRunBlock?: LastRunBlockType
|
||||
isSupportFileVar?: boolean
|
||||
shortcutPopups?: Array<{ hotkey: Hotkey, Popup: React.ComponentType<{ onClose: () => void, onInsert: ShortcutPopupInsertHandler }> }>
|
||||
shortcutPopups?: Array<{
|
||||
hotkey: Hotkey
|
||||
displayMode?: ShortcutPopupDisplayMode
|
||||
Popup: React.ComponentType<{ onClose: () => void, onInsert: ShortcutPopupInsertHandler }>
|
||||
}>
|
||||
}
|
||||
|
||||
const PromptEditor: FC<PromptEditorProps> = ({
|
||||
|
||||
@ -91,6 +91,29 @@ describe('InputField', () => {
|
||||
lastVarReferencePickerProps = undefined
|
||||
})
|
||||
|
||||
it('should keep the header and actions visible while the field content scrolls internally', () => {
|
||||
const { container } = render(
|
||||
<InputField
|
||||
nodeId="node-layout"
|
||||
isEdit={false}
|
||||
payload={createPayload()}
|
||||
onChange={vi.fn()}
|
||||
onCancel={vi.fn()}
|
||||
/>,
|
||||
)
|
||||
|
||||
const panel = container.firstElementChild
|
||||
const header = panel?.children[0]
|
||||
const scrollBody = panel?.children[1]
|
||||
const footer = panel?.lastElementChild
|
||||
|
||||
expect(panel).toHaveClass('max-h-(--shortcut-popup-max-height)', 'overflow-hidden')
|
||||
expect(header).toHaveClass('shrink-0', 'pb-2')
|
||||
expect(scrollBody).toHaveClass('min-h-0', 'flex-1', 'overflow-y-auto')
|
||||
expect(footer).toHaveClass('shrink-0', 'bg-components-panel-bg')
|
||||
expect(footer).not.toHaveClass('border-t')
|
||||
})
|
||||
|
||||
it('should disable save and show validation error when variable name is invalid', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onChange = vi.fn()
|
||||
|
||||
@ -3,6 +3,7 @@ import type { FormInputItem, FormInputItemDefault, ParagraphFormInput } from '@/
|
||||
import type { UploadFileSetting, ValueSelector } from '@/app/components/workflow/types'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { Kbd, KbdGroup } from '@langgenius/dify-ui/kbd'
|
||||
import { formatForDisplay } from '@tanstack/react-hotkeys'
|
||||
import { produce } from 'immer'
|
||||
@ -11,7 +12,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import TypeSelector from '@/app/components/app/configuration/config-var/config-modal/type-select'
|
||||
import ConfigSelect from '@/app/components/app/configuration/config-var/config-select'
|
||||
import Input from '@/app/components/base/input'
|
||||
import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/file-upload-setting'
|
||||
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
|
||||
import {
|
||||
@ -206,9 +206,11 @@ const InputField: React.FC<InputFieldProps> = ({
|
||||
}, [handleSave])
|
||||
|
||||
return (
|
||||
<div className="flex max-h-[540px] w-[372px] flex-col rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]">
|
||||
<div className="min-h-0 flex-1 overflow-y-auto p-3 pb-0">
|
||||
<div className="flex max-h-(--shortcut-popup-max-height) w-[372px] flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]">
|
||||
<div className="shrink-0 p-3 pb-2">
|
||||
<div className="system-md-semibold text-text-primary">{t(`${i18nPrefix}.title`, { ns: 'workflow' })}</div>
|
||||
</div>
|
||||
<div className="min-h-0 flex-1 overflow-y-auto p-3 pt-0 pb-0">
|
||||
<div className="mt-3">
|
||||
<div className="system-xs-medium text-text-secondary">
|
||||
{t(`${i18nPrefix}.fieldType`, { ns: 'workflow' })}
|
||||
@ -322,7 +324,7 @@ const InputField: React.FC<InputFieldProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="shrink-0 p-3">
|
||||
<div className="shrink-0 bg-components-panel-bg p-3">
|
||||
<div className="flex justify-end space-x-2">
|
||||
<Button onClick={onCancel}>{t('operation.cancel', { ns: 'common' })}</Button>
|
||||
{isEdit
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ShortcutPopupInsertHandler } from '../index'
|
||||
import type { ShortcutPopupDisplayMode, ShortcutPopupInsertHandler } from '../index'
|
||||
import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
||||
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
|
||||
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'
|
||||
@ -50,6 +50,7 @@ const CONTENT_EDITABLE_ID = 'ce'
|
||||
type MinimalEditorProps = {
|
||||
withContainer?: boolean
|
||||
hotkey?: string | string[] | string[][] | ((e: KeyboardEvent) => boolean)
|
||||
displayMode?: ShortcutPopupDisplayMode
|
||||
children?: React.ReactNode | ((close: () => void, onInsert: ShortcutPopupInsertHandler) => React.ReactNode)
|
||||
className?: string
|
||||
onOpen?: () => void
|
||||
@ -59,6 +60,7 @@ type MinimalEditorProps = {
|
||||
const MinimalEditor: React.FC<MinimalEditorProps> = ({
|
||||
withContainer = true,
|
||||
hotkey,
|
||||
displayMode,
|
||||
children,
|
||||
className,
|
||||
onOpen,
|
||||
@ -83,6 +85,7 @@ const MinimalEditor: React.FC<MinimalEditorProps> = ({
|
||||
<ShortcutsPopupPlugin
|
||||
container={withContainer ? containerEl : undefined}
|
||||
hotkey={hotkey}
|
||||
displayMode={displayMode}
|
||||
className={className}
|
||||
onOpen={onOpen}
|
||||
onClose={onClose}
|
||||
@ -179,7 +182,9 @@ describe('ShortcutsPopupPlugin', () => {
|
||||
const host = screen.getByTestId(CONTAINER_ID)
|
||||
focusAndTriggerHotkey('/')
|
||||
const portalContent = await screen.findByText(SHORTCUTS_EMPTY_CONTENT)
|
||||
const floatingDiv = screen.getByTestId('shortcuts-popup')
|
||||
expect(host).toContainElement(portalContent)
|
||||
expect(floatingDiv).toHaveStyle({ position: 'absolute' })
|
||||
})
|
||||
|
||||
it('falls back to document.body when container is not provided', async () => {
|
||||
@ -188,10 +193,65 @@ describe('ShortcutsPopupPlugin', () => {
|
||||
const portalContent = await screen.findByText(SHORTCUTS_EMPTY_CONTENT)
|
||||
const floatingDiv = screen.getByTestId('shortcuts-popup')
|
||||
expect(document.body).toContainElement(portalContent)
|
||||
expect(floatingDiv).toHaveStyle({ position: 'fixed' })
|
||||
expect(floatingDiv).toHaveStyle({ zIndex: '50' })
|
||||
expect(floatingDiv).toHaveStyle({ overflow: 'visible' })
|
||||
})
|
||||
|
||||
it('clips the popup viewport so child popups own their internal scrolling', async () => {
|
||||
render(<MinimalEditor />)
|
||||
focusAndTriggerHotkey('/')
|
||||
await screen.findByText(SHORTCUTS_EMPTY_CONTENT)
|
||||
|
||||
const floatingDiv = screen.getByTestId('shortcuts-popup')
|
||||
expect(floatingDiv.firstElementChild).toHaveClass('overflow-hidden')
|
||||
})
|
||||
|
||||
it('can render fixed next to the workflow panel instead of following the cursor', async () => {
|
||||
const originalInnerWidth = window.innerWidth
|
||||
const originalInnerHeight = window.innerHeight
|
||||
Object.defineProperty(window, 'innerWidth', { configurable: true, value: 1200 })
|
||||
Object.defineProperty(window, 'innerHeight', { configurable: true, value: 900 })
|
||||
|
||||
const rightPanel = document.createElement('div')
|
||||
rightPanel.setAttribute('data-workflow-right-panel', '')
|
||||
rightPanel.getBoundingClientRect = vi.fn(() => ({
|
||||
x: 800,
|
||||
y: 56,
|
||||
width: 400,
|
||||
height: 840,
|
||||
top: 56,
|
||||
right: 1200,
|
||||
bottom: 896,
|
||||
left: 800,
|
||||
toJSON: () => ({}),
|
||||
} as DOMRect))
|
||||
document.body.appendChild(rightPanel)
|
||||
|
||||
try {
|
||||
render(<MinimalEditor withContainer={false} displayMode="workflow-panel-adjacent-center" />)
|
||||
focusAndTriggerHotkey('/')
|
||||
await screen.findByText(SHORTCUTS_EMPTY_CONTENT)
|
||||
|
||||
const floatingDiv = screen.getByTestId('shortcuts-popup')
|
||||
await waitFor(() => {
|
||||
expect(floatingDiv).toHaveStyle({
|
||||
position: 'fixed',
|
||||
right: '404px',
|
||||
top: '474px',
|
||||
transform: 'translateY(-50%)',
|
||||
})
|
||||
})
|
||||
expect(floatingDiv.style.getPropertyValue('--shortcut-popup-max-width')).toBe('400px')
|
||||
expect(floatingDiv.style.getPropertyValue('--shortcut-popup-max-height')).toBe('836px')
|
||||
}
|
||||
finally {
|
||||
rightPanel.remove()
|
||||
Object.defineProperty(window, 'innerWidth', { configurable: true, value: originalInnerWidth })
|
||||
Object.defineProperty(window, 'innerHeight', { configurable: true, value: originalInnerHeight })
|
||||
}
|
||||
})
|
||||
|
||||
// ─── matchHotkey: string hotkey ───
|
||||
it('matches a string hotkey like "mod+/"', async () => {
|
||||
render(<MinimalEditor hotkey="mod+/" />)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { LexicalCommand } from 'lexical'
|
||||
import type { CSSProperties } from 'react'
|
||||
import {
|
||||
autoUpdate,
|
||||
flip,
|
||||
@ -19,11 +20,13 @@ import {
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
useSyncExternalStore,
|
||||
} from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
|
||||
export const SHORTCUTS_EMPTY_CONTENT = 'shortcuts_empty_content'
|
||||
export type ShortcutPopupInsertHandler = <Payload>(command: LexicalCommand<Payload>, params: Payload) => void
|
||||
export type ShortcutPopupDisplayMode = 'selection' | 'workflow-panel-adjacent-center'
|
||||
|
||||
// Hotkey can be:
|
||||
// - string: 'mod+/'
|
||||
@ -37,10 +40,85 @@ type ShortcutPopupPluginProps = {
|
||||
children?: React.ReactNode | ((close: () => void, onInsert: ShortcutPopupInsertHandler) => React.ReactNode)
|
||||
className?: string
|
||||
container?: Element | null
|
||||
displayMode?: ShortcutPopupDisplayMode
|
||||
onOpen?: () => void
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
const VIEWPORT_PADDING = 8
|
||||
const PANEL_GAP = 4
|
||||
const POPUP_MAX_WIDTH = 400
|
||||
|
||||
type FixedPlacementState = {
|
||||
right: number
|
||||
top: number
|
||||
availableWidth: number
|
||||
availableHeight: number
|
||||
}
|
||||
|
||||
function getWorkflowPanelAdjacentPlacement(): FixedPlacementState {
|
||||
const rightPanel = document.querySelector('[data-workflow-right-panel]') as HTMLElement | null
|
||||
const rightPanelRect = rightPanel?.getBoundingClientRect()
|
||||
const rightBoundary = rightPanelRect && rightPanelRect.left > 0
|
||||
? rightPanelRect.left
|
||||
: window.innerWidth
|
||||
const topBoundary = rightPanelRect?.top ?? 56
|
||||
const bottomBoundary = window.innerHeight - VIEWPORT_PADDING
|
||||
const availableWidth = Math.max(0, rightBoundary - VIEWPORT_PADDING * 2)
|
||||
const availableHeight = Math.max(0, bottomBoundary - topBoundary)
|
||||
|
||||
return {
|
||||
right: Math.max(VIEWPORT_PADDING, window.innerWidth - rightBoundary + PANEL_GAP),
|
||||
top: topBoundary + availableHeight / 2,
|
||||
availableWidth,
|
||||
availableHeight,
|
||||
}
|
||||
}
|
||||
|
||||
function getWorkflowPanelAdjacentPlacementSnapshot() {
|
||||
/* v8 ignore next 2 -- server/non-browser fallback for a client-only positioning branch. @preserve */
|
||||
if (typeof window === 'undefined' || typeof document === 'undefined')
|
||||
return '0|0|0|0'
|
||||
|
||||
const placement = getWorkflowPanelAdjacentPlacement()
|
||||
return [
|
||||
placement.right,
|
||||
placement.top,
|
||||
placement.availableWidth,
|
||||
placement.availableHeight,
|
||||
].join('|')
|
||||
}
|
||||
|
||||
function parseWorkflowPanelAdjacentPlacement(snapshot: string): FixedPlacementState {
|
||||
const [right = '0', top = '0', availableWidth = '0', availableHeight = '0'] = snapshot.split('|')
|
||||
return {
|
||||
right: Number(right),
|
||||
top: Number(top),
|
||||
availableWidth: Number(availableWidth),
|
||||
availableHeight: Number(availableHeight),
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeWorkflowPanelAdjacentPlacement(callback: () => void) {
|
||||
/* v8 ignore next 2 -- server/non-browser fallback for a client-only positioning branch. @preserve */
|
||||
if (typeof window === 'undefined' || typeof document === 'undefined')
|
||||
return () => {}
|
||||
|
||||
window.addEventListener('resize', callback)
|
||||
|
||||
const rightPanel = document.querySelector('[data-workflow-right-panel]')
|
||||
const resizeObserver = rightPanel && typeof ResizeObserver !== 'undefined'
|
||||
? new ResizeObserver(callback)
|
||||
: null
|
||||
if (rightPanel)
|
||||
resizeObserver?.observe(rightPanel)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', callback)
|
||||
resizeObserver?.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
const META_ALIASES = new Set(['meta', 'cmd', 'command'])
|
||||
const CTRL_ALIASES = new Set(['ctrl'])
|
||||
const ALT_ALIASES = new Set(['alt', 'option'])
|
||||
@ -134,11 +212,17 @@ export default function ShortcutsPopupPlugin({
|
||||
children,
|
||||
className,
|
||||
container,
|
||||
displayMode = 'selection',
|
||||
onOpen,
|
||||
onClose,
|
||||
}: ShortcutPopupPluginProps): React.ReactPortal | null {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
const [open, setOpen] = useState(false)
|
||||
const workflowPanelAdjacentPlacementSnapshot = useSyncExternalStore(
|
||||
subscribeWorkflowPanelAdjacentPlacement,
|
||||
getWorkflowPanelAdjacentPlacementSnapshot,
|
||||
() => '0|0|0|0',
|
||||
)
|
||||
const portalRef = useRef<HTMLDivElement | null>(null)
|
||||
const lastSelectionRef = useRef<Range | null>(null)
|
||||
|
||||
@ -148,6 +232,7 @@ export default function ShortcutsPopupPlugin({
|
||||
|
||||
const { refs, floatingStyles, isPositioned } = useFloating({
|
||||
placement: 'bottom-start',
|
||||
strategy: useContainer ? 'absolute' : 'fixed',
|
||||
middleware: [
|
||||
offset(0), // fix hide cursor
|
||||
shift({
|
||||
@ -192,6 +277,12 @@ export default function ShortcutsPopupPlugin({
|
||||
}, [editor])
|
||||
|
||||
const openPortal = useCallback(() => {
|
||||
if (displayMode !== 'selection') {
|
||||
setOpen(true)
|
||||
onOpen?.()
|
||||
return
|
||||
}
|
||||
|
||||
const domSelection = window.getSelection()
|
||||
let range: Range | null = null
|
||||
if (domSelection && domSelection.rangeCount > 0)
|
||||
@ -237,7 +328,7 @@ export default function ShortcutsPopupPlugin({
|
||||
|
||||
setOpen(true)
|
||||
onOpen?.()
|
||||
}, [editor, onOpen, refs])
|
||||
}, [displayMode, editor, onOpen, refs])
|
||||
|
||||
const closePortal = useCallback(() => {
|
||||
setOpen(false)
|
||||
@ -292,25 +383,44 @@ export default function ShortcutsPopupPlugin({
|
||||
if (!open || !containerEl)
|
||||
return null
|
||||
|
||||
const isFixedPanelAdjacent = displayMode === 'workflow-panel-adjacent-center'
|
||||
const fixedPlacementState = parseWorkflowPanelAdjacentPlacement(workflowPanelAdjacentPlacementSnapshot)
|
||||
const fixedPanelAdjacentStyles: CSSProperties = isFixedPanelAdjacent
|
||||
? {
|
||||
position: 'fixed',
|
||||
right: fixedPlacementState.right,
|
||||
top: fixedPlacementState.top,
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 50,
|
||||
overflow: 'visible',
|
||||
visibility: 'visible',
|
||||
['--shortcut-popup-max-width' as string]: `${Math.min(POPUP_MAX_WIDTH, fixedPlacementState.availableWidth)}px`,
|
||||
['--shortcut-popup-max-height' as string]: `${fixedPlacementState.availableHeight}px`,
|
||||
} as CSSProperties
|
||||
: {}
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
data-testid="shortcuts-popup"
|
||||
ref={(node) => {
|
||||
portalRef.current = node
|
||||
refs.setFloating(node)
|
||||
if (!isFixedPanelAdjacent)
|
||||
refs.setFloating(node)
|
||||
}}
|
||||
className={cn(
|
||||
'absolute rounded-xl bg-components-panel-bg-blur shadow-lg',
|
||||
className,
|
||||
)}
|
||||
style={{
|
||||
...floatingStyles,
|
||||
zIndex: useContainer ? undefined : 50,
|
||||
overflow: 'visible',
|
||||
visibility: isPositioned ? 'visible' : 'hidden',
|
||||
}}
|
||||
style={isFixedPanelAdjacent
|
||||
? fixedPanelAdjacentStyles
|
||||
: {
|
||||
...floatingStyles,
|
||||
zIndex: useContainer ? undefined : 50,
|
||||
overflow: 'visible',
|
||||
visibility: isPositioned ? 'visible' : 'hidden',
|
||||
}}
|
||||
>
|
||||
<div className="max-h-(--shortcut-popup-max-height) max-w-(--shortcut-popup-max-width) overflow-x-hidden overflow-y-auto rounded-xl">
|
||||
<div className="max-h-(--shortcut-popup-max-height) max-w-(--shortcut-popup-max-width) overflow-hidden rounded-xl">
|
||||
{typeof children === 'function' ? children(closePortal, handleInsert) : (children ?? SHORTCUTS_EMPTY_CONTENT)}
|
||||
</div>
|
||||
</div>,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { EditorState } from 'lexical'
|
||||
import type { FC } from 'react'
|
||||
import type { Hotkey, ShortcutPopupInsertHandler } from './plugins/shortcuts-popup-plugin'
|
||||
import type { Hotkey, ShortcutPopupDisplayMode, ShortcutPopupInsertHandler } from './plugins/shortcuts-popup-plugin'
|
||||
import type {
|
||||
ContextBlockType,
|
||||
CurrentBlockType,
|
||||
@ -68,6 +68,7 @@ import {
|
||||
|
||||
type ShortcutPopup = {
|
||||
hotkey: Hotkey
|
||||
displayMode?: ShortcutPopupDisplayMode
|
||||
Popup: React.ComponentType<{ onClose: () => void, onInsert: ShortcutPopupInsertHandler }>
|
||||
}
|
||||
|
||||
@ -144,8 +145,8 @@ const PromptEditorContent: FC<PromptEditorContentProps> = ({
|
||||
)}
|
||||
ErrorBoundary={LexicalErrorBoundary}
|
||||
/>
|
||||
{shortcutPopups.map(({ hotkey, Popup }, idx) => (
|
||||
<ShortcutsPopupPlugin key={idx} hotkey={hotkey}>
|
||||
{shortcutPopups.map(({ hotkey, displayMode, Popup }, idx) => (
|
||||
<ShortcutsPopupPlugin key={idx} hotkey={hotkey} displayMode={displayMode}>
|
||||
{(closePortal, onInsert) => <Popup onClose={closePortal} onInsert={onInsert} />}
|
||||
</ShortcutsPopupPlugin>
|
||||
))}
|
||||
|
||||
@ -165,6 +165,12 @@ describe('FormContent', () => {
|
||||
|
||||
expect(mockPromptEditor).toHaveBeenCalledWith(expect.objectContaining({
|
||||
editable: true,
|
||||
shortcutPopups: [
|
||||
expect.objectContaining({
|
||||
hotkey: ['mod', '/'],
|
||||
displayMode: 'workflow-panel-adjacent-center',
|
||||
}),
|
||||
],
|
||||
hitlInputBlock: expect.objectContaining({
|
||||
workflowNodesMap: expect.objectContaining({
|
||||
'node-1': expect.objectContaining({ title: 'Start' }),
|
||||
|
||||
@ -132,6 +132,7 @@ const FormContent: FC<FormContentProps> = ({
|
||||
|
||||
return [{
|
||||
hotkey: ['mod', '/'],
|
||||
displayMode: 'workflow-panel-adjacent-center' as const,
|
||||
// Keep this component type stable while the popup is open; it reads fresh props from a ref.
|
||||
// eslint-disable-next-line react/no-nested-component-definitions
|
||||
Popup: ({ onClose, onInsert }: {
|
||||
|
||||
@ -135,6 +135,7 @@ const Panel: FC<PanelProps> = ({
|
||||
return (
|
||||
<div
|
||||
ref={rightPanelRef}
|
||||
data-workflow-right-panel
|
||||
tabIndex={-1}
|
||||
className={cn('absolute top-14 right-0 bottom-1 z-10 flex outline-hidden')}
|
||||
key={`${isRestoring}`}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user