جافاسكريبت - كيف يمكنني الاستماع إلى مجموعة ضغطات مفاتيح باستخدام lexical.js؟


أنا جديد على lexical.js ولا أفهم تمامًا كيف يمكنني الاستماع إلى مجموعات ضغط المفاتيح المختلفة. أريد الاستماع إلى "Ctrl/Cmd+S" ثم تشغيل رد نداء (لحفظ البيانات). كيف يمكنني القيام بذلك؟

باستخدام مربع النص يمكنني القيام بشيء مثل:

const onKeyDown = (event) => {
  // "Ctrl" or "Cmd" + "s"
  if ((event.ctrlKey || event.metaKey) && event.which === 83) {
    // save data
  }
}

<textarea onKeyDown={onKeyDown} />

مع الليكسكال حاولت أن أفعل شيئًا مثل:

const MY_SAVE_COMMAND: LexicalCommand<string> = createCommand('MY_SAVE_COMMAND')

editor.registerCommand<KeyboardEvent>(
  MY_SAVE_COMMAND,
  (event) => {
    console.log('[MY_SAVE_COMMAND] event', event)
    return true
  },
  COMMAND_PRIORITY_HIGH,
)

الذي لا يعمل. أين أدرج الجزء الذي أستمع فيه لضربات المفاتيح، أم أن هناك طريقة مختلفة للقيام بهذا تمامًا؟

الإجابة رقم 1

الأوامر المخصصة تشبه أكثر أنواع الأحداث العالمية. يوفر Lexical الكثير مما يمكنك الاستماع إليه بشكل افتراضي، ولكن بالنسبة لأشياء مثل مستمعي الأحداث العاديين، ما تريد على الأرجح هو ربط الحدث بـ rootElement عبر editor.registerRootListener.

يمكنك استخدام مكون إضافي مخصص لإدارة إرفاق وإزالة مستمع الحدث هذا. في المثال أدناه، لم أقم بتنفيذ onKeyDown، لكنك ستفعل كما تفعل مع أي معالج حدث عادي آخر.

import {useLayoutEffect} from 'react';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';

function CommandSPlugin() {
  const [editor] = useLexicalComposerContext();
  useLayoutEffect(() => {
    const onKeyDown = () => { /* Your handler logic here */ };

    return editor.registerRootListener(
      (
        rootElement: null | HTMLElement,
        prevRootElement: null | HTMLElement,
      ) => {
        if (prevRootElement !== null) {
          prevRootElement.removeEventListener('keydown', onKeyDown);
        }
        if (rootElement !== null) {
          rootElement.addEventListener('keydown', onKeyDown);
        }
      }
    );
  }, [editor]);
}

// Then later...

const initialConfig = { /* ... */ };
function App() {
  return (
    <LexicalComposer initialConfig={initialConfig}>
      <CommandSPlugin />
      <YourEditorHere />
    </LexicalComposer>
  );
}

الإجابة رقم 2

شكراً على الإجابات، لقد ساعدتني كثيراً. أدناه الحل الكامل الذي انتهيت باستخدامه. أنشأت <GlobalEventsPlugin /> مع أمر SAVE_COMMAND يمكن لأي برنامج إضافي بعد ذلك استخدامه:

// GlobalEventsPlugin.tsx
import { useLayoutEffect } from 'react'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { LexicalCommand, createCommand } from 'lexical'

export const SAVE_COMMAND: LexicalCommand<KeyboardEvent> = createCommand('SAVE_COMMAND')

const GlobalEventsPlugin = () => {
  const [editor] = useLexicalComposerContext()

  useLayoutEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      // "Ctrl" or "Cmd" + "s"
      if ((event.ctrlKey || event.metaKey) && event.which === 83) {
        editor.dispatchCommand(SAVE_COMMAND, event)
      }
    }

    return editor.registerRootListener((rootElement: HTMLElement | null, prevRootElement: HTMLElement | null) => {
      if (prevRootElement !== null) {
        prevRootElement.removeEventListener('keydown', onKeyDown)
      }
      if (rootElement !== null) {
        rootElement.addEventListener('keydown', onKeyDown)
      }
    })
  }, [editor])

  return null
}

export default GlobalEventsPlugin
// Editor.tsx
const initialConfig = { /* ... */ }
function App() {
  return (
    <LexicalComposer initialConfig={initialConfig}>
      <GlobalEventsPlugin />
      <YourEditorHere />
    </LexicalComposer>
  )
}
// Any other context that has access to `editor`
editor.registerCommand(
  SAVE_COMMAND,
  (event) => {
    // Do something with `event`, e.g. `event.preventDefault() && saveData()`
  },
  COMMAND_PRIORITY_HIGH,
)