Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #19

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public static Node buildCountryCodeVisibleSample() {
return buildSample(title, description, field);
}

private static Node buildSample(String title, String description, PhoneNumberField field) {
public static Node buildSample(String title, String description, PhoneNumberField field) {
Label titleLabel = new Label(title);
titleLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 1.4em;");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.dlsc.phonenumberfx.demo;

import com.dlsc.phonenumberfx.PhoneNumberField;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

Expand All @@ -14,11 +18,30 @@ public class SinglePhoneNumberFieldApp extends Application {
public void start(Stage stage) {
VBox vBox = new VBox(20);
vBox.setPadding(new Insets(20));
vBox.setAlignment(Pos.CENTER);
vBox.setAlignment(Pos.CENTER_LEFT);

PhoneNumberField field = new PhoneNumberField();
vBox.getChildren().addAll(
PhoneNumberFieldSamples.buildCountryCodeVisibleSample()
PhoneNumberFieldSamples.buildSample("Phone Number Field", "A configurable field for entering international phone numbers.", field)
);

Button clearButton = new Button("Clear");
clearButton.setOnAction(evt -> field.clear());

CheckBox showExampleBox = new CheckBox("Show example number for selected country");
showExampleBox.selectedProperty().bindBidirectional(field.showExampleNumbersProperty());

CheckBox countryCodeVisibleBox = new CheckBox("Show country code as part of number");
countryCodeVisibleBox.selectedProperty().bindBidirectional(field.countryCodeVisibleProperty());

CheckBox disableCountryCodeBox = new CheckBox("Disable country dropdown");
disableCountryCodeBox.selectedProperty().bindBidirectional(field.disableCountryDropdownProperty());

CheckBox editableBox = new CheckBox("Editable");
editableBox.selectedProperty().bindBidirectional(field.editableProperty());

vBox.getChildren().addAll(new Separator(), clearButton, showExampleBox, countryCodeVisibleBox, disableCountryCodeBox, editableBox);

ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(vBox);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
Expand Down Expand Up @@ -133,7 +134,7 @@ protected void layoutChildren(double x, double y, double w, double h) {
comboBox.setMaxWidth(Double.MAX_VALUE);
comboBox.setMaxHeight(Double.MAX_VALUE);
comboBox.setFocusTraversable(false);
comboBox.disableProperty().bind(disableCountryDropdownProperty());
comboBox.disableProperty().bind(disableCountryDropdownProperty().or(editableProperty().not()));
comboBox.valueProperty().bindBidirectional(selectedCountryProperty());

ButtonCell buttonCell = new ButtonCell();
Expand All @@ -146,6 +147,7 @@ protected void layoutChildren(double x, double y, double w, double h) {
formatter = new PhoneNumberFormatter();

InvalidationListener updateSampleListener = it -> updatePromptTextWithExampleNumber();
showExampleNumbersProperty().addListener(updateSampleListener);
expectedPhoneNumberTypeProperty().addListener(updateSampleListener);
selectedCountryProperty().addListener(updateSampleListener);

Expand All @@ -161,7 +163,9 @@ protected void layoutChildren(double x, double y, double w, double h) {
}
});

rawPhoneNumberProperty().addListener((obs, oldV, newV) -> Platform.runLater(() -> formatter.setFormattedPhoneNumber(getRawPhoneNumber())));
ChangeListener<Object> updateFormattedPhoneNumberListener = (obs, oldV, newV) -> Platform.runLater(() -> formatter.setFormattedPhoneNumber(getRawPhoneNumber()));
rawPhoneNumberProperty().addListener(updateFormattedPhoneNumberListener);
countryCodeVisibleProperty().addListener(updateFormattedPhoneNumberListener);
validProperty().addListener((obs, oldV, newV) -> pseudoClassStateChanged(INVALID_PSEUDO_CLASS, !newV));

countryCellFactory.addListener((obs, oldValue, newValue) -> {
Expand Down Expand Up @@ -208,16 +212,22 @@ protected void layoutChildren(double x, double y, double w, double h) {
}

private void updatePromptTextWithExampleNumber() {
if (getSelectedCountry() == null) {
setPromptText(null);
} else {
Phonenumber.PhoneNumber sampleNumber;
if (getExpectedPhoneNumberType() == null) {
sampleNumber = phoneNumberUtil.getExampleNumber(getSelectedCountry().iso2Code());
if (isShowExampleNumbers()) {
if (getSelectedCountry() == null) {
setPromptText(null);
} else {
sampleNumber = phoneNumberUtil.getExampleNumberForType(getSelectedCountry().iso2Code(), getExpectedPhoneNumberType());
Phonenumber.PhoneNumber sampleNumber;
if (getExpectedPhoneNumberType() == null) {
sampleNumber = phoneNumberUtil.getExampleNumber(getSelectedCountry().iso2Code());
} else {
sampleNumber = phoneNumberUtil.getExampleNumberForType(getSelectedCountry().iso2Code(), getExpectedPhoneNumberType());
}
setPromptText(formatter.doFormat(phoneNumberUtil.format(sampleNumber, PhoneNumberUtil.PhoneNumberFormat.E164), getSelectedCountry()));
}
} else {
if (!promptTextProperty().isBound()) {
setPromptText(null);
}
setPromptText(formatter.doFormat(phoneNumberUtil.format(sampleNumber, PhoneNumberUtil.PhoneNumberFormat.E164), getSelectedCountry()));
}
}

Expand Down Expand Up @@ -295,6 +305,20 @@ public void set(String newRawPhoneNumber) {
}
};

private final BooleanProperty showExampleNumbers = new SimpleBooleanProperty(this, "showExampleNumbers", true);

public final boolean isShowExampleNumbers() {
return showExampleNumbers.get();
}

public final BooleanProperty showExampleNumbersProperty() {
return showExampleNumbers;
}

public final void setShowExampleNumbers(boolean showExampleNumbers) {
this.showExampleNumbers.set(showExampleNumbers);
}

/**
* @return The raw phone number corresponding exactly to what the user typed in, including the (+) sign appended at the
* beginning. This value can be a valid E164 formatted number.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import com.dlsc.phonenumberfx.PhoneNumberField.Country;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.NumberParseException.ErrorType;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import javafx.beans.property.*;
import javafx.css.PseudoClass;
import javafx.scene.control.*;
import javafx.util.StringConverter;

import java.util.Locale;
import java.util.Objects;
Expand Down Expand Up @@ -41,6 +43,7 @@ public PhoneNumberLabel() {
validProperty().addListener((obs, oldV, newV) -> pseudoClassStateChanged(INVALID_PSEUDO_CLASS, !newV));

rawPhoneNumber.addListener((obs, oldRawPhoneNumber, newRawPhoneNumber) -> {
errorType.set(null);
try {
Phonenumber.PhoneNumber phoneNumber;

Expand All @@ -51,7 +54,7 @@ public PhoneNumberLabel() {
nationalPhoneNumber.set(phoneNumberUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.NATIONAL));

switch (getStrategy()) {
case NATIONAL_FOR_DEFAULT_COUNTRY_ONLY:
case NATIONAL_FOR_OWN_COUNTRY_ONLY:
if (phoneNumber.getCountryCode() == getCountry().countryCode()) {
setText(getNationalPhoneNumber());
} else {
Expand All @@ -74,11 +77,19 @@ public PhoneNumberLabel() {

setTooltip(new Tooltip(phoneNumberUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)));
setValid(phoneNumberUtil.isValidNumber(phoneNumber));

} catch (NumberParseException e) {
ErrorType errorType = e.getErrorType();
if (errorType != null) {
this.errorType.set(errorType);
setTooltip(new Tooltip(getConverter().toString(errorType)));
} else {
setTooltip(new Tooltip(newRawPhoneNumber));
}

if (newRawPhoneNumber != null && newRawPhoneNumber.startsWith(getCountry().countryCodePrefix())) {
newRawPhoneNumber = newRawPhoneNumber.substring(getCountry().countryCodePrefix().length());
}

e164PhoneNumber.set("");
nationalPhoneNumber.set("");
setText(newRawPhoneNumber);
Expand All @@ -94,18 +105,113 @@ private void updateValidPseudoState() {
pseudoClassStateChanged(INVALID_PSEUDO_CLASS, !isValid());
}

// STRING CONVERTER

private final ObjectProperty<StringConverter<ErrorType>> converter = new SimpleObjectProperty<>(this, "converter", new StringConverter<>() {

@Override
public String toString(ErrorType errorType) {
if (errorType != null) {
switch (errorType) {
case INVALID_COUNTRY_CODE:
return "Invalid country code";
case NOT_A_NUMBER:
return "Invalid: not a number";
case TOO_SHORT_AFTER_IDD:
case TOO_SHORT_NSN:
return "Invalid: too short / not enough digits";
case TOO_LONG:
return "Invalid: too long / too many digits";
}
}
return null;
}

@Override
public ErrorType fromString(String string) {
return null;
}
});

public StringConverter<ErrorType> getConverter() {
return converter.get();
}

/**
* Returns the property that represents the converter for the error type.
* The converter is used to convert the error type to a string for display purposes.
*
* @return the property that represents the converter for the error type
*/
public ObjectProperty<StringConverter<ErrorType>> converterProperty() {
return converter;
}

public void setConverter(StringConverter<ErrorType> converter) {
this.converter.set(converter);
}

// ERROR TYPE

private final ObjectProperty<ErrorType> errorType = new SimpleObjectProperty<>(this, "errorType");

public final ErrorType getErrorType() {
return errorType.get();
}

/**
* Returns the error type property of the phone number field. The error type
* represents the type of error that occurred during the parsing of the phone
* number.
*
* @return the error type property
*/
public final ObjectProperty<ErrorType> errorTypeProperty() {
return errorType;
}

public final void setErrorType(ErrorType errorType) {
this.errorType.set(errorType);
}

/**
* Enumeration representing different strategies for displaying phone numbers.
*
* @see #setStrategy(Strategy)
*/
public enum Strategy {
NATIONAL_FOR_DEFAULT_COUNTRY_ONLY,

/**
* This value causes the label to show the phone number in a format appropriate
* for the country where the application is being used, while numbers from other
* countries will contain the countries prefix.
*/
NATIONAL_FOR_OWN_COUNTRY_ONLY,

/**
* This value causes the label to always show the phone number in international
* format including the country code prefix.
*/
ALWAYS_DISPLAY_INTERNATIONAL,
ALWAYS_DISPLAY_NATIONAL,

/**
* This value causes the label to always show the phone number in the national
* format, without the country code prefix.
*/
ALWAYS_DISPLAY_NATIONAL
}

private final ObjectProperty<Strategy> strategy = new SimpleObjectProperty<>(this, "strategy", Strategy.NATIONAL_FOR_DEFAULT_COUNTRY_ONLY);
// STRATEGY

private final ObjectProperty<Strategy> strategy = new SimpleObjectProperty<>(this, "strategy", Strategy.NATIONAL_FOR_OWN_COUNTRY_ONLY);

public final Strategy getStrategy() {
return strategy.get();
}

/**
* A property used to determine how the label will display the given phone number.
*/
public final ObjectProperty<Strategy> strategyProperty() {
return strategy;
}
Expand All @@ -114,6 +220,8 @@ public final void setStrategy(Strategy strategy) {
this.strategy.set(strategy);
}

// RAW PHONE NUMBER

private final StringProperty rawPhoneNumber = new SimpleStringProperty(this, "rawPhoneNumber");

/**
Expand All @@ -132,12 +240,18 @@ public final void setRawPhoneNumber(String rawPhoneNumber) {
rawPhoneNumberProperty().set(rawPhoneNumber);
}

// COUNTRY

private final ObjectProperty<Country> country = new SimpleObjectProperty<>(this, "country", Country.ofISO2(Locale.getDefault().getCountry()));

public final Country getCountry() {
return country.get();
}

/**
* The country where the field gets used. This property will be initially populated via
* a lookup of the default locale.
*/
public final ObjectProperty<Country> countryProperty() {
return country;
}
Expand All @@ -146,8 +260,13 @@ public final void setCountry(Country country) {
this.country.set(country);
}

// NATIONAL PHONE NUMBER

private final ReadOnlyStringWrapper nationalPhoneNumber = new ReadOnlyStringWrapper(this, "nationalPhoneNumber");

/**
* A representation of the phone number in the national format of the specified country.
*/
public final ReadOnlyStringProperty nationalPhoneNumberProperty() {
return nationalPhoneNumber.getReadOnlyProperty();
}
Expand All @@ -160,8 +279,13 @@ private void setNationalPhoneNumber(String nationalPhoneNumber) {
this.nationalPhoneNumber.set(nationalPhoneNumber);
}

// E164 PHONE NUMBER

private final ReadOnlyStringWrapper e164PhoneNumber = new ReadOnlyStringWrapper(this, "e164PhoneNumber");

/**
* A representation of the phone number in the international standard format E164.
*/
public final ReadOnlyStringProperty e164PhoneNumberProperty() {
return e164PhoneNumber.getReadOnlyProperty();
}
Expand All @@ -174,6 +298,8 @@ private void setE164PhoneNumber(String e164PhoneNumber) {
this.e164PhoneNumber.set(e164PhoneNumber);
}

// VALIDITY FLAG

private final ReadOnlyBooleanWrapper valid = new ReadOnlyBooleanWrapper(this, "valid");

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
}

.phone-number-field:invalid {
-fx-text-fill: red;
}

.phone-number-field > .left-pane > .combo-box {
Expand Down
Loading