कॉन्टेक्स्ट मेन्यू

संदर्भ मेन्यू में, उन कार्रवाइयों की सूची होती है जो उपयोगकर्ता किसी कॉम्पोनेंट पर कर सकता है. जैसे, वर्कस्पेस, ब्लॉक या वर्कस्पेस पर की गई टिप्पणी. संदर्भ मेन्यू, टच डिवाइस पर दायां क्लिक करने या दबाकर रखने पर दिखता है. अगर @blockly/keyboard-navigation प्लगिन का इस्तेमाल किया जाता है, तो इसे कीबोर्ड शॉर्टकट के साथ भी दिखाया जाता है. Windows पर यह डिफ़ॉल्ट रूप से Ctrl+Enter या Mac पर Command+Enter होता है.

किसी ब्लॉक के लिए डिफ़ॉल्ट कॉन्टेक्स्ट मेन्यू

कॉन्टेक्स्ट मेन्यू, उन कार्रवाइयों को जोड़ने के लिए एक अच्छी जगह है जिन्हें उपयोगकर्ता कभी-कभी करता है. जैसे, स्क्रीनशॉट डाउनलोड करना. अगर आपको लगता है कि किसी कार्रवाई का इस्तेमाल ज़्यादातर लोग करेंगे, तो उसे शुरू करने का ऐसा तरीका बनाएं जिसे आसानी से खोजा जा सके.

संदर्भ मेन्यू, वर्कस्पेस, ब्लॉक, वर्कस्पेस में की गई टिप्पणियों, बबल्स, और कनेक्शन के साथ काम करते हैं. आपके पास इन्हें अपने कस्टम कॉम्पोनेंट पर लागू करने का विकल्प भी है. Blockly, स्टैंडर्ड कॉन्टेक्स्ट मेन्यू उपलब्ध कराता है. इन्हें अपनी पसंद के मुताबिक बनाया जा सकता है. आपके पास हर फ़ाइल फ़ोल्डर या हर ब्लॉक के हिसाब से, फ़ाइल फ़ोल्डर और ब्लॉक पर मौजूद कॉन्टेक्स्ट मेन्यू को पसंद के मुताबिक बनाने का विकल्प भी होता है.

कॉन्टेक्स्ट मेन्यू कैसे काम करते हैं

Blockly में एक रजिस्ट्री होती है. इसमें मेन्यू के सभी आइटम के टेंप्लेट होते हैं. हर टेंप्लेट में, संदर्भ मेन्यू में किसी आइटम को बनाने का तरीका बताया गया है. जब कोई उपयोगकर्ता किसी कॉम्पोनेंट पर कॉन्टेक्स्ट मेन्यू खोलता है, तो कॉम्पोनेंट:

  1. यह कुकी, रजिस्ट्री से ऐसे मेन्यू आइटम की ऐरे बनाने के लिए कहती है जो कॉम्पोनेंट पर लागू होते हैं. रजिस्ट्री, हर टेंप्लेट से यह पूछती है कि क्या वह कॉम्पोनेंट पर लागू होता है. अगर ऐसा होता है, तो वह उससे जुड़ा मेन्यू आइटम, ऐरे में जोड़ देती है.

  2. अगर कॉम्पोनेंट कोई वर्कस्पेस या ब्लॉक है, तो यह जांच करता है कि जिस वर्कस्पेस या ब्लॉक पर मेन्यू को शुरू किया गया था उसमें संदर्भ मेन्यू को पसंद के मुताबिक बनाने का फ़ंक्शन है या नहीं. अगर ऐसा होता है, तो यह फ़ंक्शन को ऐरे पास करता है. इससे ऐरे के एलिमेंट जोड़े, मिटाए या उनमें बदलाव किए जा सकते हैं.

  3. यह कॉन्टेक्स्ट मेन्यू आइटम के (बदले गए) कलेक्शन का इस्तेमाल करके, कॉन्टेक्स्ट मेन्यू दिखाता है.

Blockly, वर्कस्पेस, ब्लॉक, और वर्कस्पेस की टिप्पणियों के लिए कॉन्टेक्स्ट मेन्यू के लिए, टेंप्लेट का एक स्टैंडर्ड सेट तय करता है. यह वर्कस्पेस और ब्लॉक के टेंप्लेट को रजिस्ट्री में पहले से लोड करता है. अगर आपको Workspace में की गई टिप्पणियों के लिए टेंप्लेट इस्तेमाल करने हैं, तो आपको उन्हें खुद ही रजिस्ट्री में लोड करना होगा.

रजिस्ट्री में टेंप्लेट जोड़ने, मिटाने, और उनमें बदलाव करने के बारे में जानने के लिए, रजिस्ट्री को पसंद के मुताबिक बनाएं लेख पढ़ें.

दायरा

संदर्भ मेन्यू को अलग-अलग तरह के कॉम्पोनेंट से लागू किया जाता है. इनमें वर्कस्पेस, वर्कस्पेस में की गई टिप्पणियां, कनेक्शन, ब्लॉक, बबल्स, और आपके कस्टम कॉम्पोनेंट शामिल हैं. इन सभी कॉम्पोनेंट टाइप के लिए संदर्भ मेन्यू में अलग-अलग आइटम हो सकते हैं. साथ ही, कॉम्पोनेंट टाइप के आधार पर आइटम अलग-अलग तरीके से काम कर सकते हैं. इसलिए, कॉन्टेक्स्ट मेन्यू सिस्टम को यह पता होना चाहिए कि इसे किस कॉम्पोनेंट पर शुरू किया गया था.

इस समस्या को हल करने के लिए, रजिस्ट्री Scope ऑब्जेक्ट का इस्तेमाल करती है. जिस कॉम्पोनेंट पर कॉन्टेक्स्ट मेन्यू को शुरू किया गया था उसे focusedNode प्रॉपर्टी में एक ऐसे ऑब्जेक्ट के तौर पर सेव किया जाता है जो IFocusableNode को लागू करता है. (IFocusableNode को उन सभी कॉम्पोनेंट में लागू किया जाता है जिन पर उपयोगकर्ता फ़ोकस कर सकते हैं. इनमें वे कॉम्पोनेंट भी शामिल हैं जो कॉन्टेक्स्ट मेन्यू लागू करते हैं. ज़्यादा जानकारी के लिए, फ़ोकस सिस्टम देखें.)

Scope ऑब्जेक्ट को टेंप्लेट के कई फ़ंक्शन में पास किया जाता है. Scope ऑब्जेक्ट पाने वाले किसी भी फ़ंक्शन में, focusedNode प्रॉपर्टी में मौजूद ऑब्जेक्ट के टाइप के आधार पर यह तय किया जा सकता है कि क्या करना है. उदाहरण के लिए, यह देखा जा सकता है कि कॉम्पोनेंट, ब्लॉक है या नहीं. इसके लिए, यह देखा जा सकता है कि:

if (scope.focusedNode instanceof Blockly.BlockSvg) {
  // do something with the block
}

Scope ऑब्जेक्ट में अन्य वैकल्पिक प्रॉपर्टी होती हैं. अब इनके इस्तेमाल का सुझाव नहीं दिया जाता, लेकिन इन्हें अब भी सेट किया जा सकता है:

  • block सिर्फ़ तब सेट होता है, जब मेन्यू दिखाने वाला कॉम्पोनेंट BlockSvg हो.
  • workspace को सिर्फ़ तब सेट किया जाता है, जब कॉम्पोनेंट WorkspaceSvg हो.
  • comment को सिर्फ़ तब सेट किया जाता है, जब कॉम्पोनेंट RenderedWorkspaceComment हो.

इन प्रॉपर्टी में, ऐसे सभी कॉम्पोनेंट शामिल नहीं होते जिनमें कॉन्टेक्स्ट मेन्यू हो सकता है. इसलिए, आपको focusedNode प्रॉपर्टी का इस्तेमाल करना चाहिए.

RegistryItem टाइप

टेंप्लेट का टाइप ContextMenuRegistry.RegistryItem होता है. इसमें ये प्रॉपर्टी शामिल होती हैं. ध्यान दें कि preconditionFn, displayText, और callback प्रॉपर्टी, separator प्रॉपर्टी के साथ इस्तेमाल नहीं की जा सकतीं.

आईडी

id प्रॉपर्टी एक यूनीक स्ट्रिंग होनी चाहिए. इससे पता चलता है कि आपके कॉन्टेक्स्ट मेन्यू आइटम का क्या काम है.

const collapseTemplate = {
  id: 'collapseBlock',
  // ...
};

प्रीकंडिशन फ़ंक्शन

preconditionFn का इस्तेमाल करके, यह तय किया जा सकता है कि संदर्भ मेन्यू का कोई आइटम कब और कैसे दिखाया जाना चाहिए.

इसे स्ट्रिंग के सेट में से एक वैल्यू दिखानी चाहिए: 'enabled', 'disabled' या 'hidden'.

मान ब्यौरा इमेज
चालू किया गया इससे पता चलता है कि आइटम चालू है. चालू किया गया विकल्प
बंद है इससे पता चलता है कि आइटम चालू नहीं है. बंद किया गया विकल्प
छिपा हुआ है इससे आइटम छिप जाता है.

preconditionFn को Scope भी पास किया जाता है. इसका इस्तेमाल यह तय करने के लिए किया जा सकता है कि मेन्यू किस तरह के कॉम्पोनेंट पर खोला गया था और उस कॉम्पोनेंट की स्थिति क्या थी.

उदाहरण के लिए, हो सकता है कि आपको किसी आइटम को सिर्फ़ ब्लॉक के लिए दिखाना हो. साथ ही, सिर्फ़ तब दिखाना हो, जब वे ब्लॉक किसी खास स्थिति में हों:

const collapseTemplate = {
  // ...
  preconditionFn: (scope) => {
    if (scope.focusedNode instanceof Blockly.BlockSvg) {
      if (!scope.focusedNode.isCollapsed()) {
        // The component is a block and it is not already collapsed
        return 'enabled';
      } else {
        // The block is already collapsed
        return 'disabled';
      }
    }
    // The component is not a block
    return 'hidden';
  },
  // ...
}

डिसप्ले टेक्स्ट

displayText को मेन्यू आइटम के तौर पर उपयोगकर्ता को दिखाया जाना चाहिए. डिस्प्ले टेक्स्ट, स्ट्रिंग, एचटीएमएल या ऐसा फ़ंक्शन हो सकता है जो स्ट्रिंग या एचटीएमएल दिखाता है.

const collapseTemplate = {
  // ...
  displayText: 'Collapse block',
  // ...
};

अगर आपको Blockly.Msg से अनुवाद दिखाना है, तो आपको फ़ंक्शन का इस्तेमाल करना होगा. अगर वैल्यू सीधे तौर पर असाइन करने की कोशिश की जाती है, तो हो सकता है कि मैसेज लोड न हों. इसके बजाय, आपको undefined वैल्यू मिलेगी.

const collapseTemplate = {
  // ...
  displayText: () => Blockly.Msg['MY_COLLAPSE_BLOCK_TEXT'],
  // ...
};

अगर किसी फ़ंक्शन का इस्तेमाल किया जाता है, तो उसे Scope वैल्यू भी पास की जाती है. इसका इस्तेमाल करके, डिसप्ले टेक्स्ट में एलिमेंट के बारे में जानकारी जोड़ी जा सकती है.

const collapseTemplate = {
  // ...
  displayText: (scope) => {
    if (scope.focusedNode instanceof Blockly.Block) {
      return `Collapse ${scope.focusedNode.type} block`;
    }
    // Shouldn't be possible, as our preconditionFn only shows this item for blocks
    return '';
  },
  // ...
}

वज़न

weight से यह तय होता है कि संदर्भ मेन्यू में आइटम किस क्रम में दिखेंगे. ज़्यादा पॉज़िटिव वैल्यू, कम पॉज़िटिव वैल्यू की तुलना में सूची में नीचे दिखती हैं. (आप ऐसा मान सकते हैं कि ज़्यादा वज़न वाले आइटम "भारी" होते हैं, इसलिए वे नीचे की ओर चले जाते हैं.)

const collapseTemplate = {
  // ...
  weight: 10,
  // ...
}

बिल्ट-इन कॉन्टेक्स्ट मेन्यू आइटम के लिए वज़न, बढ़ते क्रम में होते हैं. इनकी शुरुआत 1 से होती है और इनमें 1 की बढ़ोतरी होती है.

कॉलबैक फ़ंक्शन

callback प्रॉपर्टी एक फ़ंक्शन है. यह आपके कॉन्टेक्स्ट मेन्यू आइटम की कार्रवाई को पूरा करता है. इसमें कई पैरामीटर पास किए जाते हैं:

  • scope: यह एक Scope ऑब्जेक्ट है. यह उस कॉम्पोनेंट का रेफ़रंस देता है जिसका मेन्यू खुला है.
  • menuOpenEvent: वह Event जिसकी वजह से कॉन्टेक्स्ट मेन्यू खुला. यह PointerEvent या KeyboardEvent हो सकता है. यह इस बात पर निर्भर करता है कि उपयोगकर्ता ने मेन्यू कैसे खोला.
  • menuSelectEvent: यह Event है, जिसने मेन्यू से इस कॉन्टेक्स्ट मेन्यू आइटम को चुना है. यह PointerEvent या KeyboardEvent हो सकता है. यह इस बात पर निर्भर करता है कि उपयोगकर्ता ने आइटम को कैसे चुना है.
  • location: पिक्सल कोऑर्डिनेट में मौजूद Coordinate, जहां मेन्यू खोला गया था. इसकी मदद से, उदाहरण के लिए, क्लिक की गई जगह पर नया ब्लॉक बनाया जा सकता है.
const collapseTemplate = {
  // ...
  callback: (scope, menuOpenEvent, menuSelectEvent, location) => {
    if (scope.focusedNode instanceof Blockly.BlockSvg) {
      scope.focusedNode.collapse();
    }
  },
}

scope का इस्तेमाल करके, ऐसे टेंप्लेट डिज़ाइन किए जा सकते हैं जो उस कॉम्पोनेंट के आधार पर अलग-अलग तरीके से काम करते हैं जिस पर उन्हें खोला गया था:

const collapseTemplate = {
  // ...
  callback: (scope) => {
    if (scope.focusedNode instance of Blockly.BlockSvg) {
      // On a block, collapse just the block.
      const block = scope.focusedNode;
      block.collapse();
    } else if (scope.focusedNode instanceof Blockly.WorkspaceSvg) {
      // On a workspace, collapse all the blocks.
      let workspace = scope.focusedNode;
      collapseAllBlocks(workspace);
    }
  }
}

सेपरेटर कार्ड

separator प्रॉपर्टी, संदर्भ मेन्यू में एक लाइन बनाती है.

separator प्रॉपर्टी वाले टेंप्लेट में preconditionFn, displayText या callback प्रॉपर्टी नहीं हो सकतीं. साथ ही, इन्हें सिर्फ़ scopeType प्रॉपर्टी के साथ स्कोप किया जा सकता है. दूसरी पाबंदी का मतलब है कि इनका इस्तेमाल सिर्फ़ वर्कस्पेस, ब्लॉक, और वर्कस्पेस की टिप्पणियों के लिए कॉन्टेक्स्ट मेन्यू में किया जा सकता है.

const separatorAfterCollapseBlockTemplate = {
  id: 'separatorAfterCollapseBlock',
  scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
  weight: 11, // Between the weights of the two items you want to separate.
  separator: true,
};

आपको अपने कॉन्टेक्स्ट मेन्यू में मौजूद हर सेपरेटर के लिए, अलग-अलग टेंप्लेट की ज़रूरत होगी. हर सेपरेटर की पोज़िशन सेट करने के लिए, weight प्रॉपर्टी का इस्तेमाल करें.

स्कोप का टाइप

scopeType प्रॉपर्टी अब काम नहीं करती है. पहले, इसका इस्तेमाल यह तय करने के लिए किया जाता था कि किसी ब्लॉक, फ़ाइल फ़ोल्डर में की गई टिप्पणी या फ़ाइल फ़ोल्डर के लिए, संदर्भ मेन्यू में कोई मेन्यू आइटम दिखाना है या नहीं. कॉन्टेक्स्ट मेन्यू को अन्य कॉम्पोनेंट पर भी खोला जा सकता है. इसलिए, scopeType प्रॉपर्टी बहुत ज़्यादा पाबंदी वाली है. इसके बजाय, आपको preconditionFn का इस्तेमाल करना चाहिए, ताकि संबंधित कॉम्पोनेंट के लिए विकल्प दिखाया या छिपाया जा सके.

अगर आपके पास ऐसे मौजूदा कॉन्टेक्स्ट मेन्यू टेंप्लेट हैं जो scopeType का इस्तेमाल करते हैं, तो Blockly सिर्फ़ सही कॉम्पोनेंट के लिए आइटम दिखाना जारी रखेगा.

const collapseTemplate = {
  // ...
  scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
  // ...
};

रजिस्ट्री को पसंद के मुताबिक बनाना

रजिस्ट्री में टेंप्लेट जोड़े जा सकते हैं, मिटाए जा सकते हैं या उनमें बदलाव किया जा सकता है. आपको डिफ़ॉल्ट टेंप्लेट, contextmenu_items.ts में मिल सकते हैं.

टेंप्लेट जोड़ना

किसी टेंप्लेट को रजिस्टर करके, उसे रजिस्ट्री में जोड़ा जा सकता है. आपको ऐसा पेज लोड होने पर सिर्फ़ एक बार करना चाहिए. यह फ़ाइल फ़ोल्डर को इंजेक्ट करने से पहले या बाद में हो सकता है.

const collapseTemplate = { /* properties from above */ };
Blockly.ContextMenuRegistry.registry.register(collapseTemplate);

किसी टेंप्लेट को मिटाना

आईडी के हिसाब से अनरजिस्टर करके, किसी टेम्प्लेट को रजिस्ट्री से हटाया जा सकता है.

Blockly.ContextMenuRegistry.registry.unregister('someID');

किसी टेंप्लेट में बदलाव करना

मौजूदा टेंप्लेट में बदलाव करने के लिए, रजिस्ट्री से टेंप्लेट पाएं. इसके बाद, उसमें बदलाव करें.

const template = Blockly.ContextMenuRegistry.registry.getItem('someID');
template?.displayText = 'some other display text';

कॉन्टेक्स्ट मेन्यू को ब्लॉक करने की सुविधा बंद करें

डिफ़ॉल्ट रूप से, ब्लॉक में एक कॉन्टेक्स्ट मेन्यू होता है. इसकी मदद से उपयोगकर्ता, ब्लॉक में टिप्पणी जोड़ सकते हैं या ब्लॉक डुप्लीकेट कर सकते हैं.

किसी ब्लॉक के कॉन्टेक्स्ट मेन्यू को बंद करने के लिए, यह तरीका अपनाएं:

block.contextMenu = false;

ब्लॉक टाइप की JSON परिभाषा में, enableContextMenu कुंजी का इस्तेमाल करें:

{
  // ...,
  "enableContextMenu": false,
}

ब्लॉक टाइप या Workspace के हिसाब से, संदर्भ मेन्यू को पसंद के मुताबिक बनाना

Blockly के कॉन्टेक्स्ट मेन्यू आइटम का ऐरे जनरेट करने के बाद, इसे अलग-अलग ब्लॉक या वर्कस्पेस के लिए पसंद के मुताबिक बनाया जा सकता है. इसके लिए, BlockSvg.customContextMenu या WorkspaceSvg.configureContextMenu को ऐसे फ़ंक्शन पर सेट करें जो अरे में बदलाव करता हो.

ब्लॉक को पास की गई ऐरे में मौजूद ऑब्जेक्ट का टाइप ContextMenuOption होना चाहिए या वे LegacyContextMenuOption इंटरफ़ेस लागू करते हों. वर्कस्पेस को पास किए गए ऑब्जेक्ट का टाइप ContextMenuOption होता है. Blockly इन ऑब्जेक्ट की इन प्रॉपर्टी का इस्तेमाल करता है:

  • text: डिसप्ले टेक्स्ट.
  • enabled: अगर false है, तो आइटम को ग्रे रंग के टेक्स्ट में दिखाएं.
  • callback: आइटम पर क्लिक करने पर कॉल किया जाने वाला फ़ंक्शन.
  • separator: यह आइटम सेपरेटर है. यह अन्य तीन प्रॉपर्टी के साथ म्यूचुअली एक्सक्लूसिव है.

प्रॉपर्टी टाइप और फ़ंक्शन सिग्नेचर के लिए, रेफ़रंस दस्तावेज़ देखें.

उदाहरण के लिए, यहां एक ऐसा फ़ंक्शन दिया गया है जो किसी वर्कस्पेस के कॉन्टेक्स्ट मेन्यू में Hello, World! आइटम जोड़ता है:

workspace.configureContextMenu = function (menuOptions, e) {
  const item = {
    text: 'Hello, World!',
    enabled: true,
    callback: function () {
      alert('Hello, World!');
    },
  };
  // Add the item to the end of the context menu.
  menuOptions.push(item);
}

कस्टम ऑब्जेक्ट पर संदर्भ मेन्यू दिखाना

इन चरणों को पूरा करके, कस्टम कॉम्पोनेंट के लिए कॉन्टेक्स्ट मेन्यू दिखाए जा सकते हैं:

  1. IFocusableNode लागू करें या IFocusableNode लागू करने वाली क्लास को एक्सटेंड करें. इस इंटरफ़ेस का इस्तेमाल, कॉन्टेक्स्ट मेन्यू सिस्टम में अपने कॉम्पोनेंट की पहचान करने के लिए किया जाता है. इससे उपयोगकर्ताओं को कीबोर्ड नेविगेशन प्लगिन का इस्तेमाल करके, आपके कॉम्पोनेंट पर जाने की सुविधा भी मिलती है.
  2. IContextMenu लागू करें. इसमें showContextMenu फ़ंक्शन शामिल है. यह फ़ंक्शन, रजिस्ट्री से कॉन्टेक्स्ट मेन्यू आइटम पाता है. साथ ही, स्क्रीन पर मेन्यू दिखाने की जगह का हिसाब लगाता है. इसके बाद, अगर दिखाने के लिए कोई आइटम है, तो मेन्यू दिखाता है.

    const MyBubble implements IFocusableNode, IContextMenu {
      ...
      showContextMenu(menuOpenEvent) {
        // Get the items from the context menu registry
        const scope = {focusedNode: this};
        const items = Blockly.ContextMenuRegistry.registry.getContextMenuOptions(scope, menuOpenEvent);
    
        // Return early if there are no items available
        if (!items.length) return;
    
        // Show the menu at the same location on screen as this component
        // The location is in pixel coordinates, so translate from workspace coordinates
        const location = Blockly.utils.svgMath.wsToScreenCoordinates(new Coordinate(this.x, this.y));
    
        // Show the context menu
        Blockly.ContextMenu.show(menuOpenEvent, items, this.workspace.RTL, this.workspace, location);
      }
    }
    
  3. एक इवेंट हैंडलर जोड़ें, जो उपयोगकर्ता के आपके कॉम्पोनेंट पर राइट क्लिक करने पर showContextMenu को कॉल करता है. ध्यान दें कि कीबोर्ड नेविगेशन प्लगिन एक इवेंट हैंडलर उपलब्ध कराता है. जब उपयोगकर्ता Ctrl+Enter (Windows) या Command+Enter (Mac) दबाता है, तब यह showContextMenu को कॉल करता है.

  4. अपने कॉन्टेक्स्ट मेन्यू आइटम के लिए, रजिस्ट्री में टेंप्लेट जोड़ें.