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 #27

Merged
merged 13 commits into from
Dec 12, 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
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@

[![Maven Central](https://img.shields.io/maven-central/v/com.dlsc.phonenumberfx/phonenumberfx)](https://search.maven.org/search?q=g:com.dlsc.phonenumberfx%20AND%20a:phonenumberfx)

This repository contains a single control that is being used for entering valid phone numbers
This repository contains a text field control that is being used for entering valid phone numbers
for any country in the world. The library has a dependency to [Google's _libphonenumber_ library](https://github.com/google/libphonenumber/),
which is rather big in size, hence we decided to distribute this control via its own project on
GitHub (as opposed to adding it to the GemsFX project.)
which is rather big, hence we decided to distribute this control via its own project on GitHub
(as opposed to adding it to the GemsFX project). The text field can be configured to format
the number on a "value commit" event (focus lost, enter pressed) or constantly while the user is
typing.

The demo website of Google's library [can be found here](https://libphonenumber.appspot.com).
A great tutorial for this control [can be found here](https://coderscratchpad.com/javafx-phone-number-input-field/).

The second control in this project is a specialized label that properly formats phone numbers set on
it via its `valueProperty()`. The label supports a "home country" so that phone numbers of the home country
will be shown in their national format, while phone numbers from other countries will be shown including
their country code prefix.

A great tutorial for this control [can be found here](https://coderscratchpad.com/javafx-phone-number-input-field/).
The demo website of Google's library [can be found here](https://libphonenumber.appspot.com).
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,14 @@ public void start(Stage stage) {
new Separator(),
PhoneNumberFieldSamples.buildDisabledCountrySelectorSample(),
new Separator(),
PhoneNumberFieldSamples.buildExpectedPhoneNumberTypeSample(),
new Separator(),
PhoneNumberFieldSamples.buildCountryCodeVisibleSample()
PhoneNumberFieldSamples.buildExpectedPhoneNumberTypeSample()
);

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

stage.setTitle("PhoneNumberField");
stage.setScene(new Scene(scrollPane, 900, 800));
stage.setScene(new Scene(scrollPane));
stage.sizeToScene();
stage.centerOnScreen();
stage.show();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.dlsc.phonenumberfx.demo;

import com.dlsc.phonenumberfx.PhoneNumberField;
import com.dlsc.phonenumberfx.PhoneNumberField.Country;
import com.dlsc.phonenumberfx.PhoneNumberLabel;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
Expand All @@ -22,7 +24,7 @@ public final class PhoneNumberFieldSamples {
if (c == null) {
return null;
}
PhoneNumberField.Country code = (PhoneNumberField.Country) c;
Country code = (Country) c;
return "(" + code.phonePrefix() + ")" + code;
};

Expand All @@ -41,7 +43,7 @@ public static Node buildDefaultEmptySample() {

public static Node buildDefaultPrefilledSample() {
PhoneNumberField field = new PhoneNumberField();
field.setRawPhoneNumber("+573003767182");
field.setText("+573003767182");

String title = "Initial Value";
String description = "A control with default settings and a value set through code.";
Expand All @@ -52,11 +54,11 @@ public static Node buildDefaultPrefilledSample() {
public static Node buildCustomAvailableCountriesSample() {
PhoneNumberField field = new PhoneNumberField();
field.getAvailableCountries().setAll(
PhoneNumberField.Country.COLOMBIA,
PhoneNumberField.Country.GERMANY,
PhoneNumberField.Country.UNITED_STATES,
PhoneNumberField.Country.UNITED_KINGDOM,
PhoneNumberField.Country.SWITZERLAND);
Country.COLOMBIA,
Country.GERMANY,
Country.UNITED_STATES,
Country.UNITED_KINGDOM,
Country.SWITZERLAND);

String title = "Available Countries (Customized)";
String description = "A control with modified list of available countries.";
Expand All @@ -68,9 +70,9 @@ public static Node buildPreferredCountriesSample() {
PhoneNumberField field = new PhoneNumberField();

field.getPreferredCountries().setAll(
PhoneNumberField.Country.SWITZERLAND,
PhoneNumberField.Country.GERMANY,
PhoneNumberField.Country.UNITED_KINGDOM);
Country.SWITZERLAND,
Country.GERMANY,
Country.UNITED_KINGDOM);

String title = "Preferred Countries";
String description = "Preferred countries all shown at the top of the list always.";
Expand All @@ -80,7 +82,7 @@ public static Node buildPreferredCountriesSample() {

public static Node buildDisabledCountrySelectorSample() {
PhoneNumberField field = new PhoneNumberField();
field.setSelectedCountry(PhoneNumberField.Country.GERMANY);
field.setSelectedCountry(Country.GERMANY);
field.setDisableCountryDropdown(true);
field.setExpectedPhoneNumberType(PhoneNumberUtil.PhoneNumberType.PERSONAL_NUMBER);

Expand All @@ -93,24 +95,14 @@ public static Node buildDisabledCountrySelectorSample() {
public static Node buildExpectedPhoneNumberTypeSample() {
PhoneNumberField field = new PhoneNumberField();
field.setExpectedPhoneNumberType(PhoneNumberUtil.PhoneNumberType.MOBILE);
field.setSelectedCountry(PhoneNumberField.Country.COLOMBIA);
field.setSelectedCountry(Country.COLOMBIA);

String title = "Fixed Phone Number Type (MOBILE)";
String description = "Establish an expected phone number type, performs validations against the type and shows an example of the phone number.";

return buildSample(title, description, field);
}

public static Node buildCountryCodeVisibleSample() {
PhoneNumberField field = new PhoneNumberField();
field.setCountryCodeVisible(true);

String title = "Country Code Visible";
String description = "Makes the country code always visible in the text field.";

return buildSample(title, description, 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 All @@ -136,13 +128,23 @@ public static Node buildSample(String title, String description, PhoneNumberFiel
rightBox.setPrefWidth(400);

PhoneNumberLabel phoneNumberLabel = new PhoneNumberLabel();
phoneNumberLabel.rawPhoneNumberProperty().bind(field.rawPhoneNumberProperty());
phoneNumberLabel.valueProperty().bind(field.e164PhoneNumberProperty());

ComboBox<Country> countryBox = new ComboBox<>();
countryBox.getItems().setAll(Country.values());
countryBox.getSelectionModel().select(field.getSelectedCountry());
countryBox.valueProperty().bindBidirectional(phoneNumberLabel.countryProperty());

VBox labelBox = new VBox(10, phoneNumberLabel, countryBox);

addField(rightBox, "Text", field.textProperty());
addField(rightBox, "Value", field.valueProperty());
addField(rightBox, "Country Code", field.selectedCountryProperty(), COUNTRY_CODE_CONVERTER);
addField(rightBox, "Raw Number", field.rawPhoneNumberProperty());
addField(rightBox, "E164 Format", field.e164PhoneNumberProperty());
addField(rightBox, "National Format", field.nationalPhoneNumberProperty());
addField(rightBox, "PhoneNumberLabel", phoneNumberLabel);
addField(rightBox, "International Format", field.internationalPhoneNumberProperty());
addField(rightBox, "PhoneNumberLabel", labelBox);
addField(rightBox, "Error Type", field.parsingErrorTypeProperty());
addField(rightBox, "Valid", field.validProperty());

HBox hBox = new HBox(30);
Expand Down Expand Up @@ -170,11 +172,15 @@ private static void addField(GridPane pane, String name, ObservableValue<?> valu
valueLbl.setStyle("-fx-font-family: monospace; -fx-font-size: 1.2em; -fx-font-weight: bold; -fx-padding: 0 0 0 10;");
}

private static void addField(GridPane pane, String name, Label valueLbl) {
valueLbl.setStyle("-fx-font-family: monospace; -fx-font-size: 1.2em; -fx-font-weight: bold; -fx-padding: 0 0 0 10;");
private static void addField(GridPane pane, String name, Node node) {
if (node instanceof VBox) {
node.setStyle("-fx-border-color: black; -fx-border-width: 1px; -fx-font-family: monospace; -fx-font-size: 1.2em; -fx-font-weight: bold; -fx-padding: 0 0 0 10;");
} else {
node.setStyle("-fx-font-family: monospace; -fx-font-size: 1.2em; -fx-font-weight: bold; -fx-padding: 0 0 0 10;");
}
int row = pane.getRowCount();
pane.add(new Label(name + ":"), 0, row);
pane.add(valueLbl, 1, row);
pane.add(node, 1, row);
}

}
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package com.dlsc.phonenumberfx.demo;

import com.dlsc.phonenumberfx.PhoneNumberField;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
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.ComboBox;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.DateTimeStringConverter;
import org.scenicview.ScenicView;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SinglePhoneNumberFieldApp extends Application {

Expand All @@ -24,7 +28,7 @@ public void start(Stage stage) {

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

Button clearButton = new Button("Clear");
Expand All @@ -33,7 +37,7 @@ public void start(Stage stage) {
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");
CheckBox countryCodeVisibleBox = new CheckBox("Show country code");
countryCodeVisibleBox.selectedProperty().bindBidirectional(field.countryCodeVisibleProperty());

CheckBox disableCountryCodeBox = new CheckBox("Disable country dropdown");
Expand All @@ -45,6 +49,9 @@ public void start(Stage stage) {
CheckBox editableBox = new CheckBox("Editable");
editableBox.selectedProperty().bindBidirectional(field.editableProperty());

CheckBox liveFormatting = new CheckBox("Live Formatting");
liveFormatting.selectedProperty().bindBidirectional(field.liveFormattingProperty());

ComboBox<PhoneNumberUtil.PhoneNumberType> expectedTypeBox = new ComboBox<>();
expectedTypeBox.getItems().setAll(PhoneNumberUtil.PhoneNumberType.values());
expectedTypeBox.valueProperty().bindBidirectional(field.expectedPhoneNumberTypeProperty());
Expand All @@ -53,13 +60,28 @@ public void start(Stage stage) {
countryBox.getItems().setAll(PhoneNumberField.Country.values());
countryBox.valueProperty().bindBidirectional(field.selectedCountryProperty());

vBox.getChildren().addAll(new Separator(), clearButton, countryBox, expectedTypeBox, showExampleBox, countryCodeVisibleBox, showCountryCodeBox, disableCountryCodeBox, editableBox);
Button loadSwissNumber = new Button("Swiss number: +41798002320");
loadSwissNumber.setOnAction(evt -> field.setValue("+410798002320"));

Button loadUSNumber1 = new Button("Canadian number: +15872223333");
loadUSNumber1.setOnAction(evt -> field.setValue("+15871234567"));

Button loadUSNumber2 = new Button("White house: +12024561111");
loadUSNumber2.setOnAction(evt -> field.setValue("+12024561111"));

Button loadBadly = new Button("Bad input: 2024561111");
loadBadly.setOnAction(evt -> field.setValue("2024561111"));

HBox loaderBox = new HBox(10, loadSwissNumber, loadUSNumber1, loadUSNumber2, loadBadly);
vBox.getChildren().addAll(new Separator(), clearButton, countryBox, expectedTypeBox, liveFormatting, showExampleBox, countryCodeVisibleBox, showCountryCodeBox, disableCountryCodeBox, editableBox, loaderBox);

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

Scene scene = new Scene(scrollPane);

stage.setTitle("PhoneNumberField");
stage.setScene(new Scene(scrollPane, 900, 400));
stage.setScene(scene);
stage.sizeToScene();
stage.centerOnScreen();
stage.show();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.dlsc.phonenumberfx;

import com.google.i18n.phonenumbers.NumberParseException;
import javafx.util.StringConverter;

/**
* Converts parsing error types to human-readable text.
*/
public class ErrorTypeConverter extends StringConverter<NumberParseException.ErrorType> {

@Override
public String toString(NumberParseException.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 NumberParseException.ErrorType fromString(String string) {
return null;
}
}
Loading
Loading