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

Upgrade Eonasdan Date Picker #427

Merged
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
42 changes: 42 additions & 0 deletions UPGRADE-2.0.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,48 @@
UPGRADE 2.0
===========

## Upgrade Date Picker

Since the begining, Sonata was using Eonasdan Bootstrap Datepicker in its version 3.1.3 to provide date picker capabilities.

Starting from 2.0 of `form-extensions`, the date picker library has been updated to latest version of
Eonasdan Tempus Dominus. It is the successor of Bootstrap Datepicker,
and its options are not compatible
with the previous version (but they look similar).

Previously all the options were configured using `dp_` prefix, now they are configured using `datepicker_options`.

This affects all the `Picker` types, including `DatePickerType`, `DateTimePickerType`, `DateRangePickerType` and `DateTimeRangePickerType`.

For example, the following code:

```php
$form->add('date', DatePickerType::class, [
'dp_use_current' => false,
'dp_min_date' => '2017-01-01',
'dp_max_date' => '2017-12-31',
]);
```

Should be replaced by:

```php
$form->add('date', DatePickerType::class, [
'datepicker_options' => [
'useCurrent' => false,
'restrictions' => [
'minDate' => '2017-01-01',
'maxDate' => '2017-12-31',
],
],
]);
```

All the options accepted by Tempus Dominus are available via `datepicker_options` and are well
documented on the official [docs](https://getdatepicker.com/6/options/).

You can also take a look at Sonata documentation for more information.

## Deprecations

All the deprecated code introduced on 1.x is removed on 2.0.
Expand Down
13 changes: 4 additions & 9 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,9 @@
// Any SCSS/CSS you require will output into a single css file (app.css in this case)
import '../scss/app.scss';

import moment from 'moment';
// eslint-disable-next-line import/no-unresolved
import DatePicker from '@symfony/stimulus-bridge/lazy-controller-loader?lazy=true!./controllers/datepicker_controller.js';

// Eonasdan Bootstrap DateTimePicker in its version 3 does not
// provide the scss or plain css, it only provides the less version
// of its source files, that's why it is not included it via npm.
import '../vendor/bootstrap-datetimepicker';
const { sonataApplication } = global;

// Create global moment variable to be used by the locale script.
// It expects moment to be available on the global scope
// in order to define the requested locale translations
global.moment = moment;
sonataApplication.register('datepicker', DatePicker);
141 changes: 141 additions & 0 deletions assets/js/controllers/datepicker_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*!
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { Controller } from '@hotwired/stimulus';
import { TempusDominus, Namespace, loadLocale } from '@eonasdan/tempus-dominus';
import { faFiveIcons } from '@eonasdan/tempus-dominus/dist/plugins/fa-five';

export default class extends Controller {
static outlets = ['datepicker'];

static values = {
options: Object,
};

#allowedLocales = [
jordisala1991 marked this conversation as resolved.
Show resolved Hide resolved
'ar',
'ar-SA',
'de',
'es',
'fi',
'fr',
'it',
'nl',
'pl',
'ro',
'ru',
'sl',
'tr',
];

datePicker = null;

connect() {
const options = this.processOptions();
const locale = this.processLocale(options);

this.dispatchEvent('pre-connect', { options, locale });

this.datePicker = new TempusDominus(this.element, options);

if (locale !== null) {
import(`@eonasdan/tempus-dominus/dist/locales/${locale}`).then((data) => {
loadLocale(data);

this.datePicker.locale(data.name);
this.datePicker.updateOptions(options);

this.dispatchEvent('post-connect-changed-locale');
});
}

this.dispatchEvent('connect', { datePicker: this.datePicker });
}

datepickerOutletConnected(outlet, element) {
this.element.addEventListener(Namespace.events.change, (event) => {
outlet.datePicker.updateOptions({
restrictions: {
minDate: event.detail.date,
},
});
});

element.addEventListener(Namespace.events.change, (event) => {
this.datePicker.updateOptions({
restrictions: {
maxDate: event.detail.date,
},
});
});
}

processOptions() {
const options = this.optionsValue;
const { restrictions = {}, display = {} } = options;

if (options?.defaultDate) {
options.defaultDate = new Date(options.defaultDate);
}

if (options?.viewDate) {
options.viewDate = new Date(options.viewDate);
}

if (restrictions?.minDate) {
restrictions.minDate = new Date(restrictions.minDate);
}

if (restrictions?.maxDate) {
restrictions.maxDate = new Date(restrictions.maxDate);
}

if (restrictions?.disabledDates) {
restrictions.disabledDates = restrictions.disabledDates.map((date) => new Date(date));
}

if (restrictions?.enabledDates) {
restrictions.enabledDates = restrictions.enabledDates.map((date) => new Date(date));
}

if (!display?.icons) {
display.icons = faFiveIcons;
}

return options;
}

processLocale(options) {
const { localization: { locale } = {} } = options;

if (!locale) {
return null;
}

if (this.#allowedLocales.includes(locale)) {
return locale;
}

if (!locale.includes('-')) {
return null;
}

const localeCode = locale.split('-')[0];

if (this.#allowedLocales.includes(localeCode)) {
return localeCode;
}

return null;
}

dispatchEvent(name, payload) {
this.dispatch(name, { detail: payload, prefix: 'datepicker' });
}
}
Loading