diff options
author | dhollowa@chromium.org <dhollowa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-06 22:36:05 +0000 |
---|---|---|
committer | dhollowa@chromium.org <dhollowa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-06 22:36:05 +0000 |
commit | d778644fc02d5ac61a642a1eb3fadf43933c28b4 (patch) | |
tree | 0758435ce437d375d6d5bf0e3ded087b211d1770 | |
parent | 0fb012aabaf28f0dad93d68c76faef78bcd44ac3 (diff) | |
download | chromium_src-d778644fc02d5ac61a642a1eb3fadf43933c28b4.zip chromium_src-d778644fc02d5ac61a642a1eb3fadf43933c28b4.tar.gz chromium_src-d778644fc02d5ac61a642a1eb3fadf43933c28b4.tar.bz2 |
Autofill extend profiles to include multi-valued fields, part 4 (UI)
Adds multi-valued editing support for Autofill address information. Name, phone, fax, and email fields have been converted to editable lists that allow the user to store variants for a given address.
BUG=65625
TEST=Manual: add, update, and delete multi-value items for name, phone, fax, and email.
Review URL: http://codereview.chromium.org/6735065
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80710 0039d316-1c4b-4281-b951-d872f2087c98
6 files changed, 347 insertions, 47 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 04c8df5a..2297e1e 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -7196,6 +7196,18 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_AUTOFILL_DIALOG_FAX" desc="The label of the Fax entry."> Fax </message> + <message name="IDS_AUTOFILL_DIALOG_ADD_NEW_NAME" desc="The placeholder text of the name field."> + Add new name + </message> + <message name="IDS_AUTOFILL_DIALOG_ADD_NEW_PHONE" desc="The placeholder text of the phone field."> + Add new phone + </message> + <message name="IDS_AUTOFILL_DIALOG_ADD_NEW_FAX" desc="The placeholder text of the fax field."> + Add new fax + </message> + <message name="IDS_AUTOFILL_DIALOG_ADD_NEW_EMAIL" desc="The placeholder text of the email field."> + Add new email + </message> <message name="IDS_AUTOFILL_DIALOG_NAME_ON_CARD" desc="The label of the Name on card entry."> Name on card </message> diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.html b/chrome/browser/resources/options/autofill_edit_address_overlay.html index 0dbee9f..2579302 100644 --- a/chrome/browser/resources/options/autofill_edit_address_overlay.html +++ b/chrome/browser/resources/options/autofill_edit_address_overlay.html @@ -4,7 +4,8 @@ <div class="input"> <label> <div><span i18n-content="fullNameLabel"></span></div> - <input type="text" class="autofill-form" id="full-name"> + <list id="full-name-list" + i18n-values="placeholder:addNewNamePlaceholder"></list> </label> </div> <div class="input"> @@ -60,31 +61,36 @@ <div class="table"> <div class="row"> <div class="input cell"> - <label for="phone"> + <label for="phone-list"> <span i18n-content="phoneLabel"></span> </label> </div> <div class="input cell"> - <label for="fax"> + <label for="fax-list"> <span i18n-content="faxLabel"></span> </label> </div> + <div class="input cell"> + <label for="email-list"> + <span i18n-content="emailLabel"></span> + </label> + </div> </div> <div class="row"> <div class="input cell"> - <input type="text" class="autofill-form" id="phone"> + <list id="phone-list" + i18n-values="placeholder:addNewPhonePlaceholder"></list> + </div> + <div class="input cell"> + <list id="fax-list" + i18n-values="placeholder:addNewFaxPlaceholder"></list> </div> <div class="input cell"> - <input type="text" class="autofill-form" id="fax"> + <list id="email-list" + i18n-values="placeholder:addNewEmailPlaceholder"></list> </div> </div> </div> - <div class="input"> - <label> - <div><span i18n-content="emailLabel"></span></div> - <input type="text" class="autofill-form" id="email"> - </label> - </div> </div> <div class="action-area button-strip"> <button type="reset" id="autofill-edit-address-cancel-button" diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.js b/chrome/browser/resources/options/autofill_edit_address_overlay.js index c1ce694..e682759 100644 --- a/chrome/browser/resources/options/autofill_edit_address_overlay.js +++ b/chrome/browser/resources/options/autofill_edit_address_overlay.js @@ -4,6 +4,7 @@ cr.define('options', function() { const OptionsPage = options.OptionsPage; + const ArrayDataModel = cr.ui.ArrayDataModel; // The GUID of the loaded address. var guid; @@ -30,6 +31,8 @@ cr.define('options', function() { initializePage: function() { OptionsPage.prototype.initializePage.call(this); + this.createMultiValueLists_(); + var self = this; $('autofill-edit-address-cancel-button').onclick = function(event) { self.dismissOverlay_(); @@ -46,6 +49,51 @@ cr.define('options', function() { }, /** + * Creates, decorates and initializes the multi-value lists for full name, + * phone, fax, and email. + * @private + */ + createMultiValueLists_: function() { + var list = $('full-name-list'); + options.autofillOptions.AutofillValuesList.decorate(list); + list.autoExpands = true; + + list = $('phone-list'); + options.autofillOptions.AutofillValuesList.decorate(list); + list.autoExpands = true; + + list = $('fax-list'); + options.autofillOptions.AutofillValuesList.decorate(list); + list.autoExpands = true; + + list = $('email-list'); + options.autofillOptions.AutofillValuesList.decorate(list); + list.autoExpands = true; + }, + + /** + * Updates the data model for the list named |listName| with the values from + * |entries|. + * @param {String} listName The id of the list. + * @param {Array} entries The list of items to be added to the list. + */ + setMultiValueList_: function(listName, entries) { + // Add data entries, filtering null or empty strings. + var list = $(listName); + list.dataModel = new ArrayDataModel( + entries.filter(function(i) {return i})); + + // Add special entry for adding new values. + list.dataModel.splice(list.dataModel.length, 0, null); + + var self = this; + list.dataModel.addEventListener( + 'splice', function(event) { self.inputFieldChanged_(); }); + list.dataModel.addEventListener( + 'change', function(event) { self.inputFieldChanged_(); }); + }, + + /** * Clears any uncommitted input, resets the stored GUID and dismisses the * overlay. * @private @@ -64,7 +112,8 @@ cr.define('options', function() { saveAddress_: function() { var address = new Array(); address[0] = this.guid; - address[1] = $('full-name').value; + var list = $('full-name-list'); + address[1] = list.dataModel.slice(0, list.dataModel.length - 1); address[2] = $('company-name').value; address[3] = $('addr-line-1').value; address[4] = $('addr-line-2').value; @@ -72,9 +121,12 @@ cr.define('options', function() { address[6] = $('state').value; address[7] = $('postal-code').value; address[8] = $('country').value; - address[9] = $('phone').value; - address[10] = $('fax').value; - address[11] = $('email').value; + list = $('phone-list'); + address[9] = list.dataModel.slice(0, list.dataModel.length - 1); + list = $('fax-list'); + address[10] = list.dataModel.slice(0, list.dataModel.length - 1); + list = $('email-list'); + address[11] = list.dataModel.slice(0, list.dataModel.length - 1); chrome.send('setAddress', address); }, @@ -87,10 +139,9 @@ cr.define('options', function() { */ connectInputEvents_: function() { var self = this; - $('full-name').oninput = $('company-name').oninput = - $('addr-line-1').oninput = $('addr-line-2').oninput = $('city').oninput = - $('state').oninput = $('postal-code').oninput = $('phone').oninput = - $('fax').oninput = $('email').oninput = function(event) { + $('company-name').oninput = $('addr-line-1').oninput = + $('addr-line-2').oninput = $('city').oninput = $('state').oninput = + $('postal-code').oninput = function(event) { self.inputFieldChanged_(); } @@ -105,12 +156,15 @@ cr.define('options', function() { * @private */ inputFieldChanged_: function() { + // Length of lists are tested for <= 1 due to the "add" placeholder item + // in the list. var disabled = - !$('full-name').value && !$('company-name').value && + $('full-name-list').items.length <= 1 && + !$('company-name').value && !$('addr-line-1').value && !$('addr-line-2').value && !$('city').value && !$('state').value && !$('postal-code').value && - !$('country').value && !$('phone').value && !$('fax').value && - !$('email').value; + !$('country').value && $('phone-list').items.length <= 1 && + $('fax-list').items.length <= 1 && $('email-list').items.length <= 1; $('autofill-edit-address-apply-button').disabled = disabled; }, @@ -186,7 +240,7 @@ cr.define('options', function() { * @private */ clearInputFields_: function() { - $('full-name').value = ''; + this.setMultiValueList_('full-name-list', []); $('company-name').value = ''; $('addr-line-1').value = ''; $('addr-line-2').value = ''; @@ -194,9 +248,9 @@ cr.define('options', function() { $('state').value = ''; $('postal-code').value = ''; $('country').value = ''; - $('phone').value = ''; - $('fax').value = ''; - $('email').value = ''; + this.setMultiValueList_('phone-list', []); + this.setMultiValueList_('fax-list', []); + this.setMultiValueList_('email-list', []); this.countryChanged_(); }, @@ -217,7 +271,7 @@ cr.define('options', function() { * @private */ setInputFields_: function(address) { - $('full-name').value = address['fullName']; + this.setMultiValueList_('full-name-list', address['fullName']); $('company-name').value = address['companyName']; $('addr-line-1').value = address['addrLine1']; $('addr-line-2').value = address['addrLine2']; @@ -225,9 +279,9 @@ cr.define('options', function() { $('state').value = address['state']; $('postal-code').value = address['postalCode']; $('country').value = address['country']; - $('phone').value = address['phone']; - $('fax').value = address['fax']; - $('email').value = address['email']; + this.setMultiValueList_('phone-list', address['phone']); + this.setMultiValueList_('fax-list', address['fax']); + this.setMultiValueList_('email-list', address['email']); this.countryChanged_(); }, diff --git a/chrome/browser/resources/options/autofill_options_list.js b/chrome/browser/resources/options/autofill_options_list.js index b90bbb7..8638726 100644 --- a/chrome/browser/resources/options/autofill_options_list.js +++ b/chrome/browser/resources/options/autofill_options_list.js @@ -5,6 +5,8 @@ cr.define('options.autofillOptions', function() { const DeletableItem = options.DeletableItem; const DeletableItemList = options.DeletableItemList; + const InlineEditableItem = options.InlineEditableItem; + const InlineEditableItemList = options.InlineEditableItemList; /** * Creates a new address list item. @@ -75,6 +77,110 @@ cr.define('options.autofillOptions', function() { }; /** + * Creates a new value list item. + * @param {AutofillValuesList} list The parent list of this item. + * @param {String} entry A string value. + * @constructor + * @extends {options.InlineEditableItem} + */ + function ValuesListItem(list, entry) { + var el = cr.doc.createElement('div'); + el.list = list; + el.value = entry; + el.__proto__ = ValuesListItem.prototype; + el.decorate(); + + return el; + } + + ValuesListItem.prototype = { + __proto__: InlineEditableItem.prototype, + + /** @inheritDoc */ + decorate: function() { + InlineEditableItem.prototype.decorate.call(this); + + this.isPlaceholder = !this.value; + + // The stored value. + var cell = this.createEditableTextCell(this.value); + this.contentElement.appendChild(cell); + this.input = cell.querySelector('input'); + + this.addEventListener('commitedit', this.onEditCommitted_); + }, + + /** + * Called when committing an edit. + * @param {Event} e The end event. + * @private + */ + onEditCommitted_: function(e) { + var i = this.list.items.indexOf(this); + if (this.input.value == this.list.dataModel.item(i)) + return; + + if (this.input.value && + this.list.dataModel.indexOf(this.input.value) == -1) { + // Update with new value. + this.list.dataModel.splice(i, 1, this.input.value); + } else { + // Reject empty values and duplicates. + this.list.dataModel.splice(i, 1); + } + }, + }; + + /** + * Creates a new list item for the Add New Item row, which doesn't represent + * an actual entry in the values list but allows the user to add new + * values. + * @param {AutofillValuesList} entry The parent list of this item. + * @constructor + * @extends {cr.ui.ValuesListItem} + */ + function ValuesAddRowListItem(list) { + var el = cr.doc.createElement('div'); + el.list = list; + el.__proto__ = ValuesAddRowListItem.prototype; + el.decorate(); + + return el; + } + + ValuesAddRowListItem.prototype = { + __proto__: ValuesListItem.prototype, + + decorate: function() { + ValuesListItem.prototype.decorate.call(this); + this.input.value = ''; + this.input.placeholder = this.list.getAttribute('placeholder'); + this.deletable = false; + }, + + /** + * Called when committing an edit. Committing a non-empty value adds it + * to the end of the values list, leaving this "AddRow" in place. + * @param {Event} e The end event. + * @extends {options.ValuesListItem} + * @private + */ + onEditCommitted_: function(e) { + var i = this.list.items.indexOf(this); + if (i < 0 || i >= this.list.dataModel.length) + return; + + if (this.input.value && + this.list.dataModel.indexOf(this.input.value) == -1) { + this.list.dataModel.splice(i, 0, this.input.value); + } else { + this.input.value = ''; + this.list.dataModel.updateIndex(i); + } + }, + }; + + /** * Create a new address list. * @constructor * @extends {options.DeletableItemList} @@ -154,10 +260,56 @@ cr.define('options.autofillOptions', function() { }, }; + /** + * Create a new value list. + * @constructor + * @extends {options.InlineEditableItemList} + */ + var AutofillValuesList = cr.ui.define('list'); + + AutofillValuesList.prototype = { + __proto__: InlineEditableItemList.prototype, + + decorate: function() { + InlineEditableItemList.prototype.decorate.call(this); + + var self = this; + function handleBlur(e) { + // When the blur event happens we do not know who is getting focus so we + // delay this a bit until we know if the new focus node is outside the + // list. + var doc = e.target.ownerDocument; + window.setTimeout(function() { + var activeElement = doc.activeElement; + if (!self.contains(activeElement)) + self.selectionModel.unselectAll(); + }, 50); + } + + this.addEventListener('blur', handleBlur, true); + }, + + /** @inheritDoc */ + createItem: function(entry) { + if (entry != null) + return new ValuesListItem(this, entry); + else + return new ValuesAddRowListItem(this); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + this.dataModel.splice(index, 1); + }, + }; + return { AddressListItem: AddressListItem, CreditCardListItem: CreditCardListItem, + ValuesListItem: ValuesListItem, + ValuesAddRowListItem: ValuesAddRowListItem, AutofillAddressList: AutofillAddressList, AutofillCreditCardList: AutofillCreditCardList, + AutofillValuesList: AutofillValuesList, }; }); diff --git a/chrome/browser/resources/options/autofill_overlay.css b/chrome/browser/resources/options/autofill_overlay.css index 32b8e27..c9a707a 100644 --- a/chrome/browser/resources/options/autofill_overlay.css +++ b/chrome/browser/resources/options/autofill_overlay.css @@ -1,4 +1,7 @@ -#autofill-edit-address-overlay, +#autofill-edit-address-overlay { + width: 510px; +} + #autofill-edit-credit-card-overlay { width: 500px; } @@ -9,7 +12,6 @@ div.table { div.cell { display: table-cell; - max-width: 151px; } div.row { @@ -20,11 +22,46 @@ div.input { padding: 2px; } -input.autofill-form { - width: 90%; - max-width: 151px; +/* Pad |city|, |state|, and |postal-code| to line up nicely with |phone-list| + * |fax-list| and |email-list|. */ +#city, #state, #postal-code { + margin: 0; + -webkit-margin-start: 2px; + -webkit-margin-end: 4px; } #country { max-width: 450px; } + +#autofill-edit-address-overlay list { + /* Min height is a multiple of the list item height (32) */ + min-height: 32px; + width: 156px; +} + +#autofill-edit-address-overlay list div.static-text { + -webkit-box-flex: 1; + -webkit-border-radius: 2px; + -webkit-padding-start: 4px; + -webkit-padding-end: 4px; + border: 1px solid darkGray; + width: 141px; +} + +#autofill-edit-address-overlay list#full-name-list { + width: 216px; +} + +#autofill-edit-address-overlay list#full-name-list div.static-text { + width: 201px; +} + +#autofill-edit-address-overlay list#full-name-list input { + width: 211px; +} + +/* Adjust the close button placement. */ +#autofill-edit-address-overlay list .raw-button { + padding: 1px 10px; +} diff --git a/chrome/browser/ui/webui/options/autofill_options_handler.cc b/chrome/browser/ui/webui/options/autofill_options_handler.cc index e991a221..8bf7709 100644 --- a/chrome/browser/ui/webui/options/autofill_options_handler.cc +++ b/chrome/browser/ui/webui/options/autofill_options_handler.cc @@ -68,6 +68,31 @@ DictionaryValue* GetCountryData() { return country_data; } +// Get the multi-valued element for |type| and return it in |ListValue| form. +void GetValueList(const AutofillProfile& profile, + AutofillFieldType type, + scoped_ptr<ListValue>* list) { + std::vector<string16> values; + profile.GetMultiInfo(type, &values); + list->reset(new ListValue); + for (size_t i = 0; i < values.size(); ++i) { + (*list)->Set(i, Value::CreateStringValue(values[i])); + } +} + +// Set the multi-valued element for |type| from input |list| values. +void SetValueList(const ListValue* list, + AutofillFieldType type, + AutofillProfile* profile) { + std::vector<string16> values(list->GetSize()); + for (size_t i = 0; i < list->GetSize(); ++i) { + string16 value; + if (list->GetString(i, &value)) + values[i] = value; + } + profile->SetMultiInfo(type, values); +} + } // namespace AutofillOptionsHandler::AutofillOptionsHandler() @@ -168,6 +193,14 @@ void AutofillOptionsHandler::SetAddressOverlayStrings( l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_FAX)); localized_strings->SetString("emailLabel", l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EMAIL)); + localized_strings->SetString("addNewNamePlaceholder", + l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_NEW_NAME)); + localized_strings->SetString("addNewPhonePlaceholder", + l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_NEW_PHONE)); + localized_strings->SetString("addNewFaxPlaceholder", + l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_NEW_FAX)); + localized_strings->SetString("addNewEmailPlaceholder", + l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_NEW_EMAIL)); std::string app_locale = AutofillCountry::ApplicationLocale(); std::string default_country_code = @@ -266,7 +299,9 @@ void AutofillOptionsHandler::LoadAddressEditor(const ListValue* args) { DictionaryValue address; address.SetString("guid", profile->guid()); - address.SetString("fullName", profile->GetInfo(NAME_FULL)); + scoped_ptr<ListValue> list; + GetValueList(*profile, NAME_FULL, &list); + address.Set("fullName", list.release()); address.SetString("companyName", profile->GetInfo(COMPANY_NAME)); address.SetString("addrLine1", profile->GetInfo(ADDRESS_HOME_LINE1)); address.SetString("addrLine2", profile->GetInfo(ADDRESS_HOME_LINE2)); @@ -274,9 +309,12 @@ void AutofillOptionsHandler::LoadAddressEditor(const ListValue* args) { address.SetString("state", profile->GetInfo(ADDRESS_HOME_STATE)); address.SetString("postalCode", profile->GetInfo(ADDRESS_HOME_ZIP)); address.SetString("country", profile->CountryCode()); - address.SetString("phone", profile->GetInfo(PHONE_HOME_WHOLE_NUMBER)); - address.SetString("fax", profile->GetInfo(PHONE_FAX_WHOLE_NUMBER)); - address.SetString("email", profile->GetInfo(EMAIL_ADDRESS)); + GetValueList(*profile, PHONE_HOME_WHOLE_NUMBER, &list); + address.Set("phone", list.release()); + GetValueList(*profile, PHONE_FAX_WHOLE_NUMBER, &list); + address.Set("fax", list.release()); + GetValueList(*profile, EMAIL_ADDRESS, &list); + address.Set("email", list.release()); web_ui_->CallJavascriptFunction("AutofillOptions.editAddress", address); } @@ -330,8 +368,9 @@ void AutofillOptionsHandler::SetAddress(const ListValue* args) { std::string country_code; string16 value; - if (args->GetString(1, &value)) - profile.SetInfo(NAME_FULL, value); + ListValue* list_value; + if (args->GetList(1, &list_value)) + SetValueList(list_value, NAME_FULL, &profile); if (args->GetString(2, &value)) profile.SetInfo(COMPANY_NAME, value); if (args->GetString(3, &value)) @@ -346,12 +385,12 @@ void AutofillOptionsHandler::SetAddress(const ListValue* args) { profile.SetInfo(ADDRESS_HOME_ZIP, value); if (args->GetString(8, &country_code)) profile.SetCountryCode(country_code); - if (args->GetString(9, &value)) - profile.SetInfo(PHONE_HOME_WHOLE_NUMBER, value); - if (args->GetString(10, &value)) - profile.SetInfo(PHONE_FAX_WHOLE_NUMBER, value); - if (args->GetString(11, &value)) - profile.SetInfo(EMAIL_ADDRESS, value); + if (args->GetList(9, &list_value)) + SetValueList(list_value, PHONE_HOME_WHOLE_NUMBER, &profile); + if (args->GetList(10, &list_value)) + SetValueList(list_value, PHONE_FAX_WHOLE_NUMBER, &profile); + if (args->GetList(11, &list_value)) + SetValueList(list_value, EMAIL_ADDRESS, &profile); if (!guid::IsValidGUID(profile.guid())) { profile.set_guid(guid::GenerateGUID()); |