From 4b9aaf5413ce84246db644d21b47e11b58faadab Mon Sep 17 00:00:00 2001 From: Andrew Motoshin Date: Tue, 5 Feb 2019 15:29:29 +0700 Subject: [PATCH] 0.1.2 --- lib/base-list/base-list-buffer.js | 6 +- lib/help-bar/README.md | 150 ++++++++++++++++++ lib/input/div-input.css | 15 +- lib/input/div-input.js | 12 +- lib/scroll-text.js | 28 +++- package.json | 2 +- .../with-data-list-add-one.mocha.js | 1 - .../with-data-list-remove-one.mocha.js | 118 ++++++++++++++ 8 files changed, 303 insertions(+), 29 deletions(-) create mode 100644 lib/help-bar/README.md create mode 100644 test/base-list/integration/with-data-list-remove-one.mocha.js diff --git a/lib/base-list/base-list-buffer.js b/lib/base-list/base-list-buffer.js index cb884ad..072ea3d 100644 --- a/lib/base-list/base-list-buffer.js +++ b/lib/base-list/base-list-buffer.js @@ -474,8 +474,10 @@ zb.ui.BaseListBuffer.prototype._updateGlobalEnd = function() { globalEnd = sourceEnd; } else if (isNaN(sourceEnd)) { globalEnd = this._globalEnd; - } else { + } else if (this.isDynamic()) { globalEnd = Math.max(sourceEnd, this._globalEnd); + } else { + globalEnd = sourceEnd; } this.setGlobalEnd(globalEnd); @@ -553,7 +555,7 @@ zb.ui.BaseListBuffer.prototype._isChangeNeeded = function(sourceIndex) { * @protected */ zb.ui.BaseListBuffer.prototype._selectItem = function(newItem, newSourceIndex, oldItem, oldSourceIndex) { - if (this._isChangeNeeded(newSourceIndex)) { + if (this._isChangeNeeded(newSourceIndex) || this._isItemsChanged(this._localItems, this.getLocalItems())) { this._doChangeItems(); } diff --git a/lib/help-bar/README.md b/lib/help-bar/README.md new file mode 100644 index 0000000..7b67e9a --- /dev/null +++ b/lib/help-bar/README.md @@ -0,0 +1,150 @@ +# Виджет zb.ui.HelpBar + +## Описание конструктора +Принимает на вход один необязательный параметр — объект, состоящий из необязательных полей: + +* `itemClass` + * Тип — `Function`. + * Контруктор элементов. + * Значение по умолчанию — `zb.ui.HelpBarItem`. + +## Описание публичных методов + +### Метод `processHelpBarKey(zbKey[, opt_event])` + +#### Параметры +* zbKey + * Тип — `zb.device.input.Keys`. +* opt_event + * Тип — `KeyboardEvent=`. + +#### Описание +Обрабатывает клавиши. + +### Метод `createItem(options)` + +#### Параметры +Объект `options`, состоящий из обязательных полей: + +* `label` + * Тип — `string`. + * Текст элемента. +* `keys` + * Тип — `Array.`. + * Массив из клавиш, которые должен обрабатывать элемент. +* `cssClass` + * Тип — `string`. + * CSS-класс элемента. + +#### Описание +Создает элемент по установленному классу с параметрами `options`. +Возвращает элемент `zb.ui.HelpBar` типа `zb.ui.IHelpBarItem`. + +### Метод `setItems(items)` + +#### Параметры +* items + * Тип — `Array.` + +#### Описание +Устанавливает элементы в `zb.ui.HelpBar`. + +### Метод `setOrder(order)` + +#### Параметры +* order + * Тип — `?Array.`. + +#### Описание +Устанавливает порядок отображения элементов согласно позиции их клавиш в массиве. + +### Метод `clear()` + +#### Описание +Удаляет все элементы `zb.ui.HelpBar`. + +### Метод `getItem(zbKey)` + +#### Параметры +* zbKey + * Тип — `zb.device.input.Keys`. + +#### Описание +Возвращает элемент `zb.ui.HelpBar` обрабатывающий клавишу `zbKey`. + +## Использование паттерна фабрика + +### Создание кнопки + + /** + * @param {function()=} opt_callback + * @return {zb.ui.HelpBarItem} + */ + someApp.widgets.helpBarItemFactory.back = function(opt_callback) { + var item = new zb.ui.HelpBarItem({ + cssClass: '_back', + label: 'Назад', + keys: [zb.device.input.Keys.BACK] + }); + + item.on(item.EVENT_CLICK, function() { + opt_callback ? opt_callback() : app.back(); + }); + + return item; + }; + + /** + * @param {function()=} opt_callback + * @return {zb.ui.HelpBarItem} + */ + someApp.widgets.helpBarItemFactory.exit = function(opt_callback) { + var item = new zb.ui.HelpBarItem({ + cssClass: '_exit', + label: 'Выход', + keys: [zb.device.input.Keys.EXIT] + }); + + item.on(item.EVENT_CLICK, function() { + opt_callback ? opt_callback() : app.exit(); + }); + + return item; + }; + +### Интеграция со сценой + + /** + * @extends {zb.layers.CuteScene} + * @constructor + */ + someApp.scenes.SomeScene = function() { + goog.base(this); + + this._createHelpBar(); + }; + goog.inherits(someApp.scenes.SomeScene, zb.layers.CuteScene); + + /** + * @inheritDoc + */ + someApp.scenes.SomeScene.prototype.processKey = function(zbKey, opt_event) { + if (goog.base(this, 'processKey', zbKey, opt_event)) { + return true; + } + return this._helpBar.processHelpBarKey(zbKey, opt_event); + }; + + /** + * @protected + */ + someApp.scenes.SomeScene.prototype._createHelpBar = function() { + this._helpBar = new zb.ui.HelpBar; + this._helpBar.setItems([ + someApp.widgets.helpBarItemFactory.back(), + someApp.widgets.helpBarItemFactory.exit() + ]); + + this._container.appendChild(this._helpBar.getContainer()); + this.appendWidget(this._helpBar); + }; diff --git a/lib/input/div-input.css b/lib/input/div-input.css index 73e23e7..b63ee0a 100644 --- a/lib/input/div-input.css +++ b/lib/input/div-input.css @@ -12,6 +12,7 @@ display: block; } .w-zbui-div-input__input { + white-space: pre; position: absolute; top: 0; bottom: 0; @@ -26,21 +27,11 @@ } .w-zbui-div-input__input._fake { color: transparent; + border-right: 1px solid #000; } - .w-zbui-div-input__input._fake:after { - width: 1px; - background-color: #000; - content: ''; - display: none; - position: absolute; - top: 0; - right: 0; - bottom: 0; - } .w-zbui-div-input._caret-visible {} - .w-zbui-div-input._caret-visible .w-zbui-div-input__input._fake:after { - display: block; + .w-zbui-div-input._caret-visible .w-zbui-div-input__input._fake { animation: inputCaret 1s linear 0s infinite; } diff --git a/lib/input/div-input.js b/lib/input/div-input.js index c1dfd4e..4d06fef 100644 --- a/lib/input/div-input.js +++ b/lib/input/div-input.js @@ -42,6 +42,7 @@ goog.inherits(zb.ui.DivInput, zb.ui.AbstractInput); zb.ui.DivInput.prototype.afterDOMShow = function() { goog.base(this, 'afterDOMShow'); this.updateInputWidth(); + this._renderInputCaret(this.getCaretPosition()); }; @@ -208,7 +209,7 @@ zb.ui.DivInput.prototype._renderInputCaret = function(caretPosition) { /** - * тут происходит расчет позиции текста, относительно родительского инпута. Если внутренний текст больше инпута, + * Тут происходит расчет позиции текста, относительно родительского инпута. Если внутренний текст больше инпута, * то вычисляется смещение этого текста относительно родительского блока. Далее по тексту: * RI - Real Input, т.е. тот блок, который содержит в себе всю строку целиком и отображается * FI - Fake Input, блок, который содержит в себе только текст до каретки и спрятан через visibility: hidden @@ -219,7 +220,6 @@ zb.ui.DivInput.prototype._renderInputCaret = function(caretPosition) { zb.ui.DivInput.prototype._placeCaretToInput = function(beforeCaretValue) { var textWidth = this._exported.inputText.offsetWidth; var inputTextPosition = Math.abs(this._inputTextPosition); - var newFakeTextPosition = 0; if (this._inputWidth < textWidth) { @@ -232,8 +232,7 @@ zb.ui.DivInput.prototype._placeCaretToInput = function(beforeCaretValue) { * Если ширина FI равна ширине RI с шириной каретки, то смещаем слайдер * относительно родителя влево. FI сдвигаем меньше на ширину каретки, чтобы не сливалась с бордером. */ - widthDiff *= -1; - newFakeTextPosition = widthDiff - this._caretWidth; + newFakeTextPosition = -widthDiff - this._caretWidth; } else if (beforeCaretValue.length === 0) { /** * Это ситуация с крайним левым положением каретки относительно RI, @@ -247,8 +246,7 @@ zb.ui.DivInput.prototype._placeCaretToInput = function(beforeCaretValue) { * смещаем слайдер вправо относительно родителя, FI смещаем на 1px больше, * чтобы не сливался с бордером */ - fakeTextWidth *= -1; - newFakeTextPosition = fakeTextWidth + this._caretWidth; + newFakeTextPosition = -fakeTextWidth + this._caretWidth; } else if (fakeTextWidth - inputTextPosition >= this._inputWidth) { /** * Описывает ситуацию, когда каретка находится в крайнем правом положении относительно родителя, @@ -297,7 +295,7 @@ zb.ui.DivInput.prototype._inputWidth; /** * TODO use CSS, luke! - * Width of slider caret, :after element + * Width of slider caret, right border of fake input * @type {number} * @protected */ diff --git a/lib/scroll-text.js b/lib/scroll-text.js index 3686a42..6968988 100644 --- a/lib/scroll-text.js +++ b/lib/scroll-text.js @@ -38,11 +38,9 @@ goog.inherits(zb.ui.ScrollText, zb.widgets.InlineWidget); * @inheritDoc */ zb.ui.ScrollText.prototype.afterDOMShow = function() { - var base = goog.base(this, 'afterDOMShow'); + goog.base(this, 'afterDOMShow'); - this._calculateSize(); - - return base; + this.updateView(); }; @@ -103,13 +101,32 @@ zb.ui.ScrollText.prototype.moveDown = function() { /** - * @return {number} + * @return {number} in percents */ zb.ui.ScrollText.prototype.getPosition = function() { return this._diff ? this._position * 100 / this._diff : 0; }; +/** + * @param {number} position in percents + */ +zb.ui.ScrollText.prototype.setPosition = function(position) { + this._setPosition(position * this._diff / 100); +}; + + +/** + * + */ +zb.ui.ScrollText.prototype.updateView = function() { + this._calculateSize(); + this._updateThumbSize(); + + this._setPosition(this._position); +}; + + /** * @return {boolean} */ @@ -149,7 +166,6 @@ zb.ui.ScrollText.prototype._calculateSize = function() { this._diff = Math.max(this._sliderSize - this._containerSize, 0); this._bar.calculateSize(); - this._updateThumbSize(); }; diff --git a/package.json b/package.json index 3aa38ef..aebf404 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zombiebox-extension-ui", - "version": "0.1.0", + "version": "0.1.1", "main": "zombiebox-extension.js", "scripts": { "test": "karma start karma.components.conf.js --single-run" diff --git a/test/base-list/integration/with-data-list-add-one.mocha.js b/test/base-list/integration/with-data-list-add-one.mocha.js index 9569788..c9a3ffe 100644 --- a/test/base-list/integration/with-data-list-add-one.mocha.js +++ b/test/base-list/integration/with-data-list-add-one.mocha.js @@ -259,7 +259,6 @@ describe('zb.ui.BaseListDataList: add one item', function() { return then('done'); }); - // TODO: протестировать удаление элемента // TODO: протестировать работу с DynamicList }); diff --git a/test/base-list/integration/with-data-list-remove-one.mocha.js b/test/base-list/integration/with-data-list-remove-one.mocha.js new file mode 100644 index 0000000..5829a47 --- /dev/null +++ b/test/base-list/integration/with-data-list-remove-one.mocha.js @@ -0,0 +1,118 @@ +describe('zb.ui.BaseListDataList: remove one item', function() { + var expect = chai.expect; + var given = mochaTestSteps.given; + var when = mochaTestSteps.when; + var then = mochaTestSteps.then; + + var w = new zb.ui.test.support.World(); + mochaTestSteps.world(w); + + beforeEach(function() { + w.setup.datalist = w.setup.createDataList(); + w.po.spyChange = sinon.spy(w.setup, 'changeCallback'); + w.po.spySelected = sinon.spy(w.setup, 'selectCallback'); + }); + + afterEach(function() { + w.po.spyChange.restore(); + w.po.spySelected.restore(); + }); + + it('Check size on element removing', function() { + // GIVEN + given('created baselist-datalist with source', function() { + return w.createListWithSetupSource(); + }); + // WHEN + when('remove first element', function() { + w.setup.datalist.removeAt(0); + }); + // THEN + then('size should decreased by one', function() { + expect(w.sut.list.getGlobalSize()).eql(25); + expect(w.sut.list.getSourceSize()).eql(25); + expect(w.sut.list.getLocalSize()).eql(6); + }); + // DONE + return then('done'); + }); + + it('Remove selected element', function() { + // GIVEN + given('created baselist-datalist with source', function() { + return w.createListWithSetupSource(); + }); + // WHEN + when('remove first element', function() { + w.setup.datalist.removeAt(0); + }); + // THEN + then('items should be updated before select', function() { + expect(w.po.spyChange).calledBefore(w.po.spySelected); + }); + then('changedCallback should be called', function() { + expect(w.po.spyChange) + .callCount(1) + .calledWith([ + 'B', 'C', 'D', + 'E', 'F', 'G' + ]); + }); + then('selectCallback should be called with next element on same index', function() { + expect(w.po.spySelected) + .callCount(1) + .calledWith('B', 0, null, NaN); + }); + // DONE + return then('done'); + }); + + it('Remove not selected element', function() { + // GIVEN + given('created baselist-datalist with source', function() { + return w.createListWithSetupSource(); + }); + // WHEN + when('remove first element', function() { + w.setup.datalist.removeAt(1); + }); + // THEN + then('changedCallback should be called', function() { + expect(w.po.spyChange) + .callCount(1) + .calledWith([ + 'A', 'C', 'D', + 'E', 'F', 'G' + ]); + }); + then('selectCallback should not be called', function() { + expect(w.po.spySelected) + .callCount(0); + }); + // DONE + return then('done'); + }); + + it('Remove element out of buffer', function() { + // GIVEN + given('created baselist-datalist with source', function() { + return w.createListWithSetupSource(); + }); + // WHEN + when('remove first element', function() { + w.setup.datalist.removeAt(6); + }); + // THEN + then('changedCallback should not be called', function() { + expect(w.po.spyChange) + .callCount(0); + }); + then('selectCallback should not be called', function() { + expect(w.po.spySelected) + .callCount(0); + }); + // DONE + return then('done'); + }); + +});