التنقّل في البطاقة

يتم إنشاء معظم الإضافات المستندة إلى البطاقات باستخدام بطاقات متعددة تمثّل "صفحات" مختلفة لواجهة الإضافة. للحصول على تجربة مستخدم فعّالة، عليك استخدام نظام تنقّل بسيط وطبيعي بين البطاقات في الإضافة.

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

التنقّل في بطاقات الصفحة الرئيسية

تقدّم إضافات Google Workspace صفحات رئيسية وبطاقات غير سياقية. لاستيعاب البطاقات السياقية وغير السياقية، تحتوي إضافات Google Workspace على حزمة بطاقات داخلية لكل نوع. عند فتح إضافة في تطبيق مضيف، يتم تشغيل homepageTrigger المقابل لإنشاء بطاقة الصفحة الرئيسية الأولى في الحزمة (بطاقة "الصفحة الرئيسية" الزرقاء الداكنة في الرسم التوضيحي أدناه). في حال عدم تحديد homepageTrigger، يتم إنشاء بطاقة تلقائية وعرضها وإضافتها إلى الحزمة غير السياقية. البطاقة الأولى هي بطاقة جذر.

يمكن للميزة الإضافية إنشاء بطاقات إضافية غير سياقية وإضافتها إلى الحزمة (البطاقات الزرقاء "المضافة" في الرسم البياني) أثناء تنقّل المستخدم في الميزة الإضافية. تعرض واجهة مستخدم الإضافة البطاقة العلوية في الحزمة، لذا يؤدي إرسال بطاقات جديدة إلى الحزمة إلى تغيير العرض، كما يؤدي إزالة البطاقات من الحزمة إلى إعادة العرض إلى البطاقات السابقة.

إذا كانت الإضافة تتضمّن عامل تشغيل سياقي محدّدًا، سيتم تشغيل عامل التشغيل عندما يدخل المستخدم في هذا السياق. تنشئ الدالة المشغّلة البطاقة السياقية، ولكن يتم تعديل عرض واجهة المستخدم استنادًا إلى DisplayStyle للبطاقة الجديدة:

  • إذا كانت قيمة DisplayStyle هي REPLACE (القيمة التلقائية)، ستحل البطاقة السياقية (البطاقة "السياقية" باللون البرتقالي الداكن في الرسم التخطيطي) محل البطاقة المعروضة حاليًا. يؤدي ذلك إلى بدء حزمة بطاقات جديدة مرتبطة بالسياق في أعلى حزمة البطاقات غير المرتبطة بالسياق، وتكون هذه البطاقة المرتبطة بالسياق هي بطاقة الجذر لحزمة البطاقات المرتبطة بالسياق.
  • إذا كانت قيمة DisplayStyle هي PEEK، ستنشئ واجهة المستخدم بدلاً من ذلك عنوانًا منبثقًا يظهر في أسفل الشريط الجانبي للإضافة، ويتم عرضه فوق البطاقة الحالية. يعرض عنوان البطاقة الجديدة في رأس المعاينة، كما يوفّر عناصر تحكّم في زر المستخدم تتيح له تحديد ما إذا كان يريد عرض البطاقة الجديدة أم لا. إذا نقروا على الزر عرض، ستحلّ البطاقة محلّ البطاقة الحالية (كما هو موضّح أعلاه مع REPLACE).

يمكنك إنشاء بطاقات سياقية إضافية وإضافتها إلى الحزمة (البطاقات الصفراء "المضافة" في الرسم التوضيحي). يؤدي تعديل حزمة البطاقات إلى تغيير واجهة مستخدم الإضافة لعرض البطاقة الأعلى. إذا خرج المستخدم من سياق، ستتم إزالة البطاقات السياقية من الحزمة وسيتم تعديل الشاشة لعرض البطاقة غير السياقية الأعلى أو الصفحة الرئيسية.

إذا أدخل المستخدم سياقًا لا تحدّد له الإضافة مشغّلاً سياقيًا، لن يتم إنشاء بطاقة جديدة وستبقى البطاقة الحالية معروضة.

إنّ الإجراءات Navigation الموضّحة أدناه تؤثّر فقط في البطاقات من السياق نفسه. على سبيل المثال، يؤدّي الإجراء popToRoot() من داخل بطاقة سياقية إلى إغلاق جميع البطاقات السياقية الأخرى فقط، ولن يؤثّر في بطاقات الصفحة الرئيسية.

في المقابل، يتوفّر الزر دائمًا للمستخدم للتنقّل من البطاقات السياقية إلى البطاقات غير السياقية.

يمكنك إنشاء انتقالات بين البطاقات من خلال إضافة بطاقات إلى حِزم البطاقات أو إزالتها منها. يوفّر الصنف Navigation وظائف لإضافة البطاقات وإزالتها من الحِزم. لإنشاء عملية تنقّل فعّالة بين البطاقات، عليك ضبط الأدوات لاستخدام إجراءات التنقّل. يمكنك دفع أو إظهار عدة بطاقات في الوقت نفسه، ولكن لا يمكنك إزالة بطاقة الصفحة الرئيسية الأولية التي يتم دفعها أولاً إلى الحزمة عند بدء الميزة الإضافية.

للانتقال إلى بطاقة جديدة استجابةً لتفاعل المستخدم مع أداة، اتّبِع الخطوات التالية:

  1. أنشئ عنصر Action واربطه بدالة ردّ اتصال تحدّدها.
  2. استدعِ دالة معالجة التطبيق المصغّر المناسبة للتطبيق المصغّر من أجل ضبط Action على هذا التطبيق المصغّر.
  3. نفِّذ دالة رد الاتصال التي تجري عملية التنقّل. تتلقّى هذه الدالة عنصر حدث إجراء كوسيطة، ويجب أن تنفّذ ما يلي:
    1. أنشئ عنصر Navigation لتحديد تغيير البطاقة. يمكن أن يحتوي عنصر Navigation واحد على خطوات تنقّل متعدّدة، ويتم تنفيذها بالترتيب الذي تمت إضافتها به إلى العنصر.
    2. أنشئ عنصر ActionResponse باستخدام الفئة ActionResponseBuilder والعنصر Navigation.
    3. إرجاع ActionResponse الذي تم إنشاؤه

عند إنشاء عناصر تحكّم في التنقّل، يمكنك استخدام وظائف عنصر Navigation التالية:

الوظيفة الوصف
Navigation.pushCard(Card) يدفع بطاقة إلى الحزمة الحالية. يتطلّب ذلك إنشاء البطاقة بالكامل أولاً.
Navigation.popCard() تزيل هذه البطاقة بطاقة واحدة من أعلى الحزمة. تعادل النقر على سهم الرجوع في صف عنوان الإضافة. لا يؤدي ذلك إلى إزالة البطاقات الأساسية.
Navigation.popToRoot() يزيل هذا الإجراء جميع البطاقات من الحزمة باستثناء البطاقة الجذر. ويؤدي ذلك إلى إعادة ضبط حزمة البطاقات.
Navigation.popToNamedCard(String) تزيل هذه الطريقة البطاقات من الحزمة إلى أن تصل إلى بطاقة بالاسم المحدّد أو إلى البطاقة الجذر للحزمة. يمكنك تعيين أسماء للبطاقات باستخدام الدالة CardBuilder.setName(String).
Navigation.updateCard(Card) يستبدل البطاقة الحالية في مكانها، ويعيد تحميلها في واجهة المستخدم.

إذا كان من المفترض أن يؤدي تفاعل المستخدم أو حدث ما إلى إعادة عرض البطاقات في السياق نفسه، استخدِم الطرق Navigation.pushCard() وNavigation.popCard() وNavigation.updateCard() لاستبدال البطاقات الحالية. إذا كان من المفترض أن يؤدي تفاعل المستخدم أو الحدث إلى إعادة عرض البطاقات في سياق مختلف، استخدِم ActionResponseBuilder.setStateChanged() لفرض إعادة تنفيذ الإضافة في تلك السياقات.

في ما يلي أمثلة على التنقّل:

  • إذا غيّر تفاعل أو حدث حالة البطاقة الحالية (على سبيل المثال، إضافة مهمة إلى قائمة مهام)، استخدِم updateCard().
  • إذا كان التفاعل أو الحدث يقدّم تفاصيل إضافية أو يطلب من المستخدم اتّخاذ إجراء إضافي (على سبيل المثال، النقر على عنوان عنصر للاطّلاع على مزيد من التفاصيل، أو الضغط على زر لإنشاء حدث جديد في "تقويم Google")، استخدِم pushCard() لعرض الصفحة الجديدة مع السماح للمستخدم بالخروج منها باستخدام زر الرجوع.
  • إذا كان تفاعل أو حدث يعدّل الحالة في بطاقة سابقة (على سبيل المثال، تعديل عنوان عنصر من خلال عرض التفاصيل)، استخدِم شيئًا مثل popCard() وpopCard() وpushCard(previous) وpushCard(current) لتعديل البطاقة السابقة والبطاقة الحالية.

إعادة تحميل البطاقات

تمنح إضافات Google Workspace المستخدمين إمكانية إعادة تحميل بطاقتك من خلال إعادة تشغيل دالة المشغّل في "برمجة التطبيقات" المسجّلة في ملف البيان. يُجري المستخدمون عملية إعادة التحميل هذه من خلال عنصر قائمة الإضافة:

الشريط الجانبي لإضافة Google Workspace

يتم تلقائيًا إضافة هذا الإجراء إلى البطاقات التي تنشئها وظائف التشغيل homepageTrigger أو contextualTrigger، كما هو محدّد في ملف بيان الإضافة (الجذور الخاصة بمجموعات البطاقات السياقية وغير السياقية).

إرجاع بطاقات متعدّدة

مثال على بطاقة إضافة

تُستخدَم وظائف الصفحة الرئيسية أو وظائف التشغيل السياقي لإنشاء وعرض إما كائن Card واحد أو مصفوفة من كائنات Card تعرضها واجهة مستخدم التطبيق.

إذا كانت هناك بطاقة واحدة فقط، تتم إضافتها إلى الحزمة غير السياقية أو السياقية كبطاقة جذر، وتعرضها واجهة مستخدم التطبيق المضيف.

إذا كانت المصفوفة المعروضة تتضمّن أكثر من عنصر Card تم إنشاؤه، يعرض التطبيق المضيف بطاقة جديدة بدلاً من ذلك، تحتوي على قائمة بعناوين كل بطاقة. عندما ينقر المستخدم على أي من هذه العناوين، تعرض واجهة المستخدم البطاقة المقابلة.

عندما يختار المستخدم بطاقة من القائمة، يتم نقل هذه البطاقة إلى الحزمة الحالية ويعرضها التطبيق المضيف. يعيد الزر المستخدم إلى قائمة عناوين البطاقات.

يمكن أن يكون ترتيب البطاقات "المسطّح" مناسبًا إذا كان تطبيقك الإضافي لا يحتاج إلى أي انتقالات بين البطاقات التي تنشئها. في معظم الحالات، من الأفضل تحديد انتقالات البطاقات مباشرةً، وأن تعرض وظائف الصفحة الرئيسية ووظائف المشغّل السياقي كائن بطاقة واحدًا.

مثال

في ما يلي مثال يوضّح كيفية إنشاء عدة بطاقات تتضمّن أزرار تنقّل للتبديل بينها. يمكن إضافة هذه البطاقات إلى الحزمة السياقية أو غير السياقية من خلال إرسال البطاقة التي تعرضها الدالة createNavigationCard() داخل سياق معيّن أو خارجه.

  /**
   *  Create the top-level card, with buttons leading to each of three
   *  'children' cards, as well as buttons to backtrack and return to the
   *  root card of the stack.
   *  @return {Card}
   */
  function createNavigationCard() {
    // Create a button set with actions to navigate to 3 different
    // 'children' cards.
    var buttonSet = CardService.newButtonSet();
    for(var i = 1; i <= 3; i++) {
      buttonSet.addButton(createToCardButton(i));
    }

    // Build the card with all the buttons (two rows)
    var card = CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader().setTitle('Navigation'))
        .addSection(CardService.newCardSection()
            .addWidget(buttonSet)
            .addWidget(buildPreviousAndRootButtonSet()));
    return card.build();
  }

  /**
   *  Create a button that navigates to the specified child card.
   *  @return {TextButton}
   */
  function createToCardButton(id) {
    var action = CardService.newAction()
        .setFunctionName('gotoChildCard')
        .setParameters({'id': id.toString()});
    var button = CardService.newTextButton()
        .setText('Card ' + id)
        .setOnClickAction(action);
    return button;
  }

  /**
   *  Create a ButtonSet with two buttons: one that backtracks to the
   *  last card and another that returns to the original (root) card.
   *  @return {ButtonSet}
   */
  function buildPreviousAndRootButtonSet() {
    var previousButton = CardService.newTextButton()
        .setText('Back')
        .setOnClickAction(CardService.newAction()
            .setFunctionName('gotoPreviousCard'));
    var toRootButton = CardService.newTextButton()
        .setText('To Root')
        .setOnClickAction(CardService.newAction()
            .setFunctionName('gotoRootCard'));

    // Return a new ButtonSet containing these two buttons.
    return CardService.newButtonSet()
        .addButton(previousButton)
        .addButton(toRootButton);
  }

  /**
   *  Create a child card, with buttons leading to each of the other
   *  child cards, and then navigate to it.
   *  @param {Object} e object containing the id of the card to build.
   *  @return {ActionResponse}
   */
  function gotoChildCard(e) {
    var id = parseInt(e.parameters.id);  // Current card ID
    var id2 = (id==3) ? 1 : id + 1;      // 2nd card ID
    var id3 = (id==1) ? 3 : id - 1;      // 3rd card ID
    var title = 'CARD ' + id;

    // Create buttons that go to the other two child cards.
    var buttonSet = CardService.newButtonSet()
      .addButton(createToCardButton(id2))
      .addButton(createToCardButton(id3));

    // Build the child card.
    var card = CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader().setTitle(title))
        .addSection(CardService.newCardSection()
            .addWidget(buttonSet)
            .addWidget(buildPreviousAndRootButtonSet()))
        .build();

    // Create a Navigation object to push the card onto the stack.
    // Return a built ActionResponse that uses the navigation object.
    var nav = CardService.newNavigation().pushCard(card);
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }

  /**
   *  Pop a card from the stack.
   *  @return {ActionResponse}
   */
  function gotoPreviousCard() {
    var nav = CardService.newNavigation().popCard();
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }

  /**
   *  Return to the initial add-on card.
   *  @return {ActionResponse}
   */
  function gotoRootCard() {
    var nav = CardService.newNavigation().popToRoot();
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }