Skip to content
This repository has been archived by the owner on Oct 8, 2020. It is now read-only.

Commit

Permalink
Add localization support
Browse files Browse the repository at this point in the history
Support 159 locales from Java with Arabic numerals and Eastern Arabic
numerals. Could add support for numerals in other scripts later on.

Next steps are to improve the step API for Java side, and add more tests
for entering time with keyboard input.

Fixes #2
  • Loading branch information
pleku committed Nov 1, 2018
2 parents 779e4e8 + d2d7d3d commit ef3b25a
Show file tree
Hide file tree
Showing 7 changed files with 759 additions and 39 deletions.
56 changes: 45 additions & 11 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.vaadin</groupId>
<artifactId>flow-component-base</artifactId>
<version>1.1-SNAPSHOT</version>
<version>1.2-SNAPSHOT</version>
</parent>

<artifactId>vaadin-time-picker-flow</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<flow.version>1.1-SNAPSHOT</flow.version>
<flow.version>1.2-SNAPSHOT</flow.version>
</properties>

<repositories>
Expand All @@ -23,7 +24,9 @@
</repository>
<repository>
<id>vaadin-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
<url>
https://oss.sonatype.org/content/repositories/vaadin-snapshots/
</url>
</repository>
</repositories>

Expand Down Expand Up @@ -78,11 +81,6 @@
<groupId>org.webjars.bowergithub.vaadin</groupId>
<artifactId>vaadin-development-mode-detector</artifactId>
</dependency>

<dependency>
<groupId>org.webjars.bowergithub.polymerelements</groupId>
<artifactId>iron-test-helpers</artifactId>
</dependency>
<dependency>
<groupId>org.webjars.bowergithub.polymerelements</groupId>
<artifactId>iron-icon</artifactId>
Expand Down Expand Up @@ -141,7 +139,7 @@
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-component-demo-helpers</artifactId>
Expand All @@ -154,5 +152,41 @@
<version>${flow.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-combo-box-flow</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-data</artifactId>
<version>${flow.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-components-testbench</artifactId>
<version>LATEST</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty.version}</version>
<configuration>
<webApp>
<resourceBases>
<resourceBase>
${project.basedir}/src/main/resources/META-INF/resources
</resourceBase>
</resourceBases>
</webApp>
</configuration>
</plugin>
</plugins>
</build>
</project>
130 changes: 116 additions & 14 deletions src/main/java/com/vaadin/flow/component/timepicker/TimePicker.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,24 @@
package com.vaadin.flow.component.timepicker;

import java.time.LocalTime;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Stream;

import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.HasEnabled;
import com.vaadin.flow.component.HasSize;
import com.vaadin.flow.component.HasValidation;
import com.vaadin.flow.component.HasValue;
import com.vaadin.flow.component.*;
import com.vaadin.flow.component.dependency.JavaScript;
import com.vaadin.flow.function.SerializableConsumer;
import com.vaadin.flow.function.SerializableFunction;
import com.vaadin.flow.shared.Registration;

/**
* Server-side component for the <code>vaadin-time-picker</code> element.
* An input component for selecting time of day, based on
* {@code vaadin-time-picker} web component.
*
* @author Vaadin Ltd
*/
public class TimePicker
extends GeneratedVaadinTimePicker<TimePicker, LocalTime>
@JavaScript("frontend://timepickerConnector.js")
public class TimePicker extends GeneratedVaadinTimePicker<TimePicker, LocalTime>
implements HasSize, HasValidation, HasEnabled {

private static final SerializableFunction<String, LocalTime> PARSER = s -> {
Expand All @@ -42,6 +44,8 @@ public class TimePicker
return d == null ? "" : d.toString();
};

private Locale locale;

/**
* Default constructor.
*/
Expand All @@ -51,7 +55,7 @@ public TimePicker() {

/**
* Convenience constructor to create a time picker with a pre-selected time.
*
*
* @param time
* the pre-selected time in the picker
*/
Expand All @@ -61,7 +65,7 @@ public TimePicker(LocalTime time) {

/**
* Convenience constructor to create a time picker with a label.
*
*
* @param label
* the label describing the time picker
* @see #setLabel(String)
Expand All @@ -74,7 +78,7 @@ public TimePicker(String label) {
/**
* Convenience constructor to create a time picker with a pre-selected time
* and a label.
*
*
* @param label
* the label describing the time picker
* @param time
Expand Down Expand Up @@ -197,7 +201,7 @@ public boolean isRequired() {
* <p>
* If the step is less than 900 seconds, the dropdown is hidden.
* </p>
*
*
* @param step
* the step to set, unit seconds
*/
Expand All @@ -208,11 +212,11 @@ public void setStep(double step) {

/**
* Gets the step of the time picker.
*
*
* <p>
* This property is not synchronized automatically from the client side, so
* the returned value may not be the same as in client side.
*
*
* @return the {@code step} property from the picker, unit seconds
*/
public double getStep() {
Expand All @@ -224,4 +228,102 @@ public Registration addInvalidChangeListener(
ComponentEventListener<InvalidChangeEvent<TimePicker>> listener) {
return super.addInvalidChangeListener(listener);
}

@Override
protected void onAttach(AttachEvent attachEvent) {
super.onAttach(attachEvent);
if (getLocale() == null) {
setLocale(attachEvent.getUI().getLocale());
}
initConnector();
}

private void initConnector() {
// can't run this with getElement().executeJavaScript(...) since then
// setLocale might be called before this causing client side error
runBeforeClientResponse(ui -> ui.getPage().executeJavaScript(
"window.Vaadin.Flow.timepickerConnector.initLazy($0)",
getElement()));
}

/**
* Set the Locale for the Time Picker. The displayed time will be formatted
* by the browser using the given locale.
* <p>
* By default, the locale is {@code null} until the component is attached to
* an UI, and then locale is set to {@link UI#getLocale()}, unless a locale
* has been explicitly set before that.
* <p>
* The time formatting is done in the browser using the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString">Date.toLocaleTimeString()</a>
* function.
* <p>
* If for some reason the browser doesn't support the given locale, the
* en-US locale is used.
* <p>
* <em>NOTE: only the language + country/region codes are used</em>. This
* means that the script and variant information is not used and supported.
* <em>NOTE: timezone related data is not supported.</em>
*
* @param locale
* the locale set to the time picker, cannot be [@code null}
*/
public void setLocale(Locale locale) {
Objects.requireNonNull(locale, "Locale must not be null.");
if (locale.getLanguage().isEmpty()) {
throw new UnsupportedOperationException("Given Locale "
+ locale.getDisplayName()
+ " is not supported by time picker because it is missing the language information.");
}

this.locale = locale;
// we could support script & variant, but that requires more work on
// client side to detect the different
// number characters for other scripts (current only Arabic there)
StringBuilder bcp47LanguageTag = new StringBuilder(
locale.getLanguage());
if (!locale.getCountry().isEmpty()) {
bcp47LanguageTag.append("-").append(locale.getCountry());
}
runBeforeClientResponse(ui -> getElement().callFunction(
"$connector.setLocale", bcp47LanguageTag.toString()));
}

/**
* Gets the Locale for this date picker.
* <p>
* By default, the locale is {@code null} until the component is attached to
* an UI, and then locale is set to {@link UI#getLocale()}, unless
* {@link #setLocale(Locale)} has been explicitly called before that.
*
* @return the locale used for this picker
*/
@Override
public Locale getLocale() {
return locale;
}

private void runBeforeClientResponse(SerializableConsumer<UI> command) {
getElement().getNode().runWhenAttached(ui -> ui
.beforeClientResponse(this, context -> command.accept(ui)));
}

/**
* Returns a stream of all the available locales that are supported by the
* time picker component.
* <p>
* This is a shorthand for {@link Locale#getAvailableLocales()} where all
* locales without the {@link Locale#getLanguage()} have been filtered out,
* as the browser cannot localize the time for those.
*
* @return a stream of the available locales that are supported by the time
* picker component
* @see #setLocale(Locale)
* @see Locale#getAvailableLocales()
* @see Locale#getLanguage()
*/
public static Stream<Locale> getSupportedAvailableLocales() {
return Stream.of(Locale.getAvailableLocales())
.filter(locale -> !locale.getLanguage().isEmpty());
}
}

0 comments on commit ef3b25a

Please sign in to comment.