תפריטי הקשר

תפריט הקשר מכיל רשימה של פעולות שהמשתמש יכול לבצע ברכיב, כמו מרחב עבודה, בלוק או תגובה במרחב העבודה. תפריט ההקשר מוצג בתגובה ללחיצה ימנית או ללחיצה ארוכה במכשיר עם מסך מגע. אם משתמשים בתוסף @blockly/keyboard-navigation, הוא מוצג גם עם מקש קיצור, שמוגדר כברירת מחדל ל-Ctrl+Enter ב-Windows או ל-Command+Enter ב-Mac.

תפריט ההקשר שמוגדר כברירת מחדל לחסימה

תפריטי הקשר הם מקום טוב להוסיף פעולות שהמשתמש מבצע לעיתים רחוקות, כמו הורדת צילום מסך. אם לדעתכם פעולה מסוימת תשמש אתכם לעיתים קרובות, כדאי ליצור דרך קלה יותר להפעלת הפעולה.

תפריטי הקשר נתמכים בסביבות עבודה, בבלוקים, בתגובות בסביבת העבודה, בבועות ובחיבורים. אפשר גם להטמיע אותם ברכיבים מותאמים אישית משלכם. ‫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'.

ערך תיאור תמונה
'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 הוא מה שצריך להציג למשתמש כחלק מפריט התפריט. הטקסט שמוצג יכול להיות מחרוזת, HTML או פונקציה שמחזירה מחרוזת או HTML.

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,
}

התאמה אישית של תפריטי הקשר לפי סוג הבלוק או סביבת העבודה

אחרי ש-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. מוסיפים handler לאירוע שמפעיל את showContextMenu כשהמשתמש לוחץ לחיצה ימנית על הרכיב. שימו לב: התוסף לניווט באמצעות מקלדת מספק handler של אירועים שקורא ל-showContextMenu כשמשתמש לוחץ על Ctrl+Enter (Windows) או על Command+Enter (Mac).

  4. מוסיפים תבניות לרישום של הפריטים בתפריט ההקשר.