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

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

كانت تظهر في إضافات 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();
  }