Плагин jQuery UI Tabs не может обрабатывать 2 уровня вкладок - PullRequest
0 голосов
/ 06 ноября 2019

Я работаю над устаревшим проектом, который использует вкладки пользовательского интерфейса jQuery для структурированного отображения пользовательских данных. Система не ограничивает количество вкладок, которые может создать пользователь (но обычно их около 30), а вкладки jQuery UI просто оборачивают вкладки, что выглядит очень непрофессионально.

Wrapping tabs in my project.

Я наткнулся на красивый плагин Пола Бланделла OverflowTabs с 2014 года (вот его JSFiddle: http://jsfiddle.net/o1wLbtj4/,), но, к сожалению, я не могу заставить его работатьпрямо с моими 2 уровнями вкладок. Я использую jQuery 1.10.2 и jQuery UI 1.10.4. Gathered tabs in a dropdown menu in Paul Blundell's JSFiddle.

Я сделал упрощенный JSFiddle, который показывает, что я пытаюсь сделать: откройте диалоговое окно, содержащее данные на двух уровнях вкладок. Если вкладок слишком много, предполагается, что оба уровня помещают переполняющиеся вкладки в меню, открывающее раскрывающийся список при нажатии, но в данный момент (на уровне с именами) отображается только последняя вкладка, а кнопка раскрывающегося списка не работает. и читает 0: http://jsfiddle.net/megahra/6s2xwgec/18/

Насколько я могу судить, плагин правильно удаляет вкладки из первого уровня и добавляет их в div переполнения (функция _hideTab), но по какой-то причине позже он перезаписывается. Я подозреваю, что плагин не может правильно работать со вторым уровнем, но я не знаю, где это исправить.

Я был бы очень признателен, если бы кто-нибудь мог указать мне правильное направление!

EDIT:
Благодаря помощи Twisty плагин теперь работает для нескольких уровней вкладок, и я исправил несколько ошибок.
Вот рабочий JSFiddle: https://jsfiddle.net/megahra/uexr4qfw/289/
- Известная ошибка: этот плагин не применяется к вкладкам в div с style="display: none" во время инициализации.

$.widget("ui.tabs", $.ui.tabs, {
  options: {
    overflowTabs: false,
    tabPadding: 25,
    containerPadding: 0,
    dropdownSize: 50
  },

  _create: function() {
    this._super("_create");
    this.tabsWidth = 0;
    this.containerWidth = 0;
    this.overflowTabsId = "id" + this._createUniqueId();

    $(this.element).addClass(this.overflowTabsId);

    if (!this.options.overflowTabs)
      return;

    // update the tabs
    this.updateOverflowTabs();

    // Detect a window resize and check the tabs again
    var that = this;
    var el = this.element;

    $(window).resize(function() {
      // Add a slight delay after resize, to fix Maximise issue.
      setTimeout(function() {
        that.updateOverflowTabs();
      }, 150);
    });

    // Detect dropdown click
    $(el).on("click", '> .overflow-selector', function(e) {
      $('> .ui-tabs-overflow', el).toggleClass('hide');

      $overflowTabs = $('.ui-tabs-overflow').not($('> .ui-tabs-overflow', el));

      //if there is more than the currently clicked one, close all others
      if($overflowTabs) {
        $overflowTabs.toggleClass('hide', true);
        }
    });

    //Detect tab click
    $('li a').on("click", function(e) {
        //close dropdown if open
        $('> .ui-tabs-overflow', el).toggleClass('hide', true);
        //ToDo: apply overflowTabs plugin to content of new tab (if it contains tabs)

    });
  },

  refresh: function() {
    this._super("refresh");
    this.updateOverflowTabs();
  },

  updateOverflowTabs: function() {
    var failsafe = 0;
    this._calculateWidths();
    var el = this.element;

    // Loop until tabsWidth is less than the containerWidth
    while (this.tabsWidth > this.containerWidth -10 && failsafe < 30) {
      this._hideTab();
      this._calculateWidths();
      failsafe++;
    }

    // Finish now if there are no tabs in the overflow list
    if ($('> .ui-tabs-overflow li', el).length === 0)
      return;

    // Reset
    failsafe = 0;

    // Get the first tab in the overflow list
    var next = this._nextTab();

    // Loop until we cannot fit any more tabs
    while (next.totalSize < this.containerWidth && $('> .ui-tabs-overflow li', el).length > 0 && failsafe < 30) {
      this._showTab(next.tab);
      this._calculateWidths();
      next = this._nextTab();
      failsafe++;
    }

    $('> .overflow-selector .total', el).html($('> .ui-tabs-overflow li', el).length);
  },

  _calculateWidths: function() {
    var width = 0;
    $(this.element).find('> .ui-tabs-nav > li').each(function() {
      width += $(this).outerWidth(true);
    });
    this.tabsWidth = width;
    this.containerWidth = $(this.element).parent().width() - this.options.containerPadding - this.options.dropdownSize;
  },
  _hideTab: function() {
    if (!$(this.element).find('> .ui-tabs-overflow').length) {
      $(this.element).find('> .ui-tabs-nav').after('<ul class="ui-tabs-overflow hide"></ul>');
      $(this.element).find('> .ui-tabs-overflow').after('<div class="overflow-selector">&#8595 <span class="total">0</span></div>');
      //calculate position of overflow-selector relativ to tab row (overflow-selector is 15px high)
      let topOffset = ($(this.element).find('> .ui-tabs-nav').innerHeight() * 0.48) - 12;
      $(this.element).find('> .overflow-selector').css('top', topOffset);
    }
    var lastTab = $('> .ui-tabs-nav li', this.element).last();
    lastTab.prependTo($('> .ui-tabs-overflow', this.element));
  },
  _showTab: function(tab) {
    tab.appendTo($('> .ui-tabs-nav', this.element));

    // Check to see if overflow list is now empty
    if ($(this.element).find('> .ui-tabs-overflow li').size() == 0) {
      $(this.element).find('> .ui-tabs-overflow').remove();
      $(this.element).find('> .overflow-selector').remove();
    }
  },
  _nextTab: function() {
    var result = {};
    var firstTab = $(this.element).find('> .ui-tabs-overflow li').first();
    result.tab = firstTab;
    result.totalSize = this.tabsWidth + this._textWidth(firstTab) + this.options.tabPadding;
    return result;
  },
  _textWidth: function(element) {
    var self = $(element),
      children = self.children(),
      calculator = $('<span style="display: inline-block;" />'),
      width;

    children.wrap(calculator);
    width = children.parent().width();
    children.unwrap();

    return width;
  },

  _createUniqueId: function() {
    // Math.random should be unique because of its seeding algorithm.
    // Convert it to base 36 (numbers + letters), and grab the first 9 characters
    // after the decimal.
    return '_' + Math.random().toString(36).substr(2, 9);
    }
});

1 Ответ

0 голосов
/ 07 ноября 2019

Обнаружено использование устаревшего кода и небольшая очистка. Я обнаружил несколько проблем.

Рабочий пример: https://jsfiddle.net/Twisty/tbvasj1g/

  1. Событие клика было жадным и скрывало элементы списка, которые не нужно было скрывать
  2. Добавить / удалить класс - это метод перед переключением. Обновлено для использования .toggleClass()
  3. .size() устарело для length

    Примечание. Этот метод был удален в jQuery 3.0. Вместо этого используйте свойство .length. Метод .size() функционально эквивалентен свойству .length;однако свойство .length предпочтительнее , так как оно не содержит служебных данных при вызове функции.

  4. $(this.element).find(selector) заменено сокращением $(selector, this.element).

  5. Перемещено .total обновление из функции вычисления в функцию обновления. Что-то сбрасывало его обратно на 0 в конце цикла. не уверен что. Просто не имело смысла обновлять его там, поэтому я переместил его, и теперь оно работает.

Widget Factory

$.widget("ui.tabs", $.ui.tabs, {
  options: {
    overflowTabs: false,
    tabPadding: 25,
    containerPadding: 0,
    dropdownSize: 50
  },
  _create: function() {
    this._super("_create");
    this.tabsWidth = 0;
    this.containerWidth = 0;
    if (!this.options.overflowTabs)
      return;

    // update the tabs
    this.updateOverflowTabs();

    // Detect a window resize and check the tabs again
    var that = this;
    var el = this.element;

    $(window).resize(function() {
      // Add a slight delay after resize, to fix Maximise issue.
      setTimeout(function() {
        that.updateOverflowTabs();
      }, 150);
    });

    // Detect dropdown click
    $(".tabStructure").on("click", ".overflow-selector", function(e) {
      $('.ui-tabs-overflow', el).toggleClass('hide');
    });
  },

  refresh: function() {
    this._super("refresh");
    this.updateOverflowTabs();
  },

  updateOverflowTabs: function() {
    var failsafe = 0;
    this._calculateWidths();
    var el = this.element;
    console.log("cWidth", this.containerWidth);

    // Loop until tabsWidth is less than the containerWidth
    while (this.tabsWidth > this.containerWidth && failsafe < 30) {
      //debugger;
      this._hideTab();
      this._calculateWidths();
      failsafe++;
    }

    // Finish now if there are no tabs in the overflow list
    if ($('.ui-tabs-overflow li', el).length === 0)
      return;

    // Reset
    failsafe = 0;

    // Get the first tab in the overflow list
    var next = this._nextTab();

    // Loop until we cannot fit any more tabs
    while (next.totalSize < this.containerWidth && $('.ui-tabs-overflow li', el).length > 0 && failsafe < 30) {
      this._showTab(next.tab);
      this._calculateWidths();
      next = this._nextTab();
      failsafe++;
    }

    $('.overflow-selector .total', el).html($('.ui-tabs-overflow li', el).length);
  },

  _calculateWidths: function() {
    var width = 0;
    $(this.element).find('.ui-tabs-nav > li').each(function() {
      width += $(this).outerWidth(true);
      //debugger;
    });
    this.tabsWidth = width;
    this.containerWidth = $(this.element).parent().width() - this.options.containerPadding - this.options.dropdownSize;
  },
  _hideTab: function() {
    if (!$('.ui-tabs-overflow').length) {
      $(this.element).find('.ui-tabs-nav').after('<ul class="ui-tabs-overflow hide"></ul>');
      $(this.element).find('.ui-tabs-overflow').after('<div class="overflow-selector">&#8595 <span class="total">0</span></div>');
    }
    var lastTab = $(this.element).find('.ui-tabs-nav li').last();
    lastTab.appendTo($(this.element).find('.ui-tabs-overflow'));
  },
  _showTab: function(tab) {
    tab.appendTo($(this.element).find('.ui-tabs-nav'));

    // Check to see if overflow list is now empty
    if ($(this.element).find('.ui-tabs-overflow li').size() == 0) {
      $(this.element).find('.ui-tabs-overflow').remove();
      $(this.element).find('.overflow-selector').remove();
    }
  },
  _nextTab: function() {
    var result = {};
    var firstTab = $(this.element).find('.ui-tabs-overflow li').first();
    result.tab = firstTab;
    result.totalSize = this.tabsWidth + this._textWidth(firstTab) + this.options.tabPadding;
    return result;
  },
  _textWidth: function(element) {
    var self = $(element),
      children = self.children(),
      calculator = $('<span style="display: inline-block;" />'),
      width;

    children.wrap(calculator);
    width = children.parent().width();
    children.unwrap();

    return width;
  }
});

$(function() {
  $("#rowDialog").dialog({
    title: 'Test modal',
    closeOnEscape: false,
    minHeight: 'auto',
    width: '600px',
    modal: true,
    resizable: true,
    buttons: [{
      // Close button
      text: 'Close',
      click: function() {
        $(this).dialog('close')
      },
    }]
  });

  $(".tabStructure").tabs({
    overflowTabs: true
  });
});

Не фанатэтого решения в целом. Я бы посоветовал нумерацию вкладок. Если на других вкладках нет содержимого или его не много, раскрывающийся список обрезается. Кроме того, если вы выберете вкладку в раскрывающемся списке, она будет скрыта, и у вас нет надежного способа узнать, какую вкладку вы просматриваете, их нет визуального индикатора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...