* @since 1.0
*/
class DateRangePicker extends \kartik\base\InputWidget
{
/**
* @var string the javascript callback to be passed to the plugin constructor.
* Note: a default value is set for this when you set `hideInput` to false, OR
* you set `useWithAddon` to `true`.
*/
public $callback;
/**
* @var boolean whether to hide the input (e.g. when you want to show the date
* range picker as a dropdown). If set to true, the input will be hidden. The plugin
* will be initialized on a container element (default 'div'), using the container template.
* A default `callback` will be setup in this case to display the selected range value within
* the container.
*/
public $hideInput = false;
/**
* @var boolean whether you are using the picker with a input group addon. You can set it
* to `true`, when `hideInput` is false, and you wish to show the picker position more
* correctly at the input-group-addon icon. A default `callback` will be setup in this case
* to generate the selected range value for the input.
*/
public $useWithAddon = false;
/**
* @var initialize all the list values set in `pluginOptions['ranges']`
* and convert all values to yii\web\JsExpression
*/
public $initRangeExpr = true;
/**
* @var boolean show a preset dropdown. If set to true, this will automatically generate
* a preset list of ranges for selection. Setting this to true will also automatically
* set `initRangeExpr` to true.
*/
public $presetDropdown = false;
/**
* @var array the HTML attributes for the container, if hideInput is set
* to true. The following special options are recognized:
* `tag`: string, the HTML tag for rendering the container. Defaults to `div`.
*/
public $containerOptions = ['class' => 'drp-container input-group'];
/**
* @var array the template for rendering the container, when hideInput is set
* to true. The special tag `{input}` will be replaced with the hidden form input.
* In addition, the element with css class `range-value` will be replaced by the
* calculated plugin value. The special tag `{value}` will be replaced with the
* value of the hidden form input during initialization
*/
public $containerTemplate = <<< HTML
{value}
{input}
HTML;
/**
* @inherit doc
*/
protected $_pluginName = 'daterangepicker';
/**
* @var string locale language to be used for the plugin
*/
protected $eLang = '';
/**
* @var string the pluginOptions format for the date time
*/
private $_format;
/**
* @var string the pluginOptions separator
*/
private $_separator;
/**
* Initializes the widget
*
* @throw InvalidConfigException
*/
public function init()
{
parent::init();
$this->_msgCat = 'kvdrp';
$this->initI18N(__DIR__);
$this->initLocale();
if ($this->convertFormat && isset($this->pluginOptions['format'])) {
$this->pluginOptions['format'] = static::convertDateFormat($this->pluginOptions['format']);
}
$this->_format = ArrayHelper::getValue($this->pluginOptions, 'format', 'YYYY-MM-DD');
$this->_separator = ArrayHelper::getValue($this->pluginOptions, 'separator', ' - ');
if (!empty($this->value)) {
$dates = explode($this->_separator, $this->value);
if (count($dates) > 1) {
$this->pluginOptions['startDate'] = $dates[0];
$this->pluginOptions['endDate'] = $dates[1];
}
}
$value = empty($this->value) ? '' : $this->value;
$this->containerTemplate = str_replace('{value}', $value, $this->containerTemplate);
$this->initRange();
$this->containerOptions['id'] = $this->options['id'] . '-container';
$this->registerAssets();
echo $this->renderInput();
}
/**
* Initialize locale settings
*/
protected function initLocale()
{
$this->setLanguage('');
if (empty($this->_langFile)) {
return;
}
$localeSettings = ArrayHelper::getValue($this->pluginOptions, 'locale', []);
$localeSettings += [
'applyLabel' => Yii::t('kvdrp', 'Apply'),
'cancelLabel' => Yii::t('kvdrp', 'Cancel'),
'fromLabel' => Yii::t('kvdrp', 'From'),
'toLabel' => Yii::t('kvdrp', 'To'),
'weekLabel' => Yii::t('kvdrp', 'W'),
'customRangeLabel' => Yii::t('kvdrp', 'Custom Range'),
'daysOfWeek' => new JsExpression('moment.weekdaysMin()'),
'monthNames' => new JsExpression('moment.monthsShort()'),
'firstDay' => new JsExpression('moment.localeData()._week.dow')
];
$this->pluginOptions['locale'] = $localeSettings;
}
/**
* Automatically convert the date format from PHP DateTime to Moment.js DateTime format
* as required by bootstrap-daterangepicker plugin.
*
* @see http://php.net/manual/en/function.date.php
* @see http://momentjs.com/docs/#/parsing/string-format/
*
* @param string $format the PHP date format string
*
* @return string
*/
protected static function convertDateFormat($format)
{
return strtr($format, [
// meridian lowercase remains same
// 'a' => 'a',
// meridian uppercase remains same
// 'A' => 'A',
// second (with leading zeros)
's' => 'ss',
// minute (with leading zeros)
'i' => 'mm',
// hour in 12-hour format (no leading zeros)
'g' => 'h',
// hour in 12-hour format (with leading zeros)
'h' => 'hh',
// hour in 24-hour format (no leading zeros)
'G' => 'H',
// hour in 24-hour format (with leading zeros)
'H' => 'HH',
// day of the week locale
'w' => 'e',
// day of the week ISO
'W' => 'E',
// day of month (no leading zero)
'j' => 'D',
// day of month (two digit)
'd' => 'DD',
// day name short
'D' => 'DDD',
// day name long
'l' => 'DDDD',
// month of year (no leading zero)
'n' => 'M',
// month of year (two digit)
'm' => 'MM',
// month name short
'M' => 'MMM',
// month name long
'F' => 'MMMM',
// year (two digit)
'y' => 'YY',
// year (four digit)
'Y' => 'YYYY',
// unix timestamp
'U' => 'X',
]);
}
/**
* Initializes the pluginOptions range list
*/
protected function initRange()
{
if (isset($dummyValidation)) {
$msg = Yii::t('kvdrp', 'Select Date Range');
}
if ($this->presetDropdown) {
$this->initRangeExpr = true;
$this->pluginOptions['ranges'] = [
Yii::t('kvdrp', "Today") => ["moment().startOf('day')", "moment()"],
Yii::t('kvdrp', "Yesterday") => [
"moment().startOf('day').subtract(1,'days')",
"moment().endOf('day').subtract(1,'days')"
],
Yii::t('kvdrp', "Last {n} Days", ['n' => 7]) => [
"moment().startOf('day').subtract(6, 'days')",
"moment()"
],
Yii::t('kvdrp', "Last {n} Days", ['n' => 30]) => [
"moment().startOf('day').subtract(29, 'days')",
"moment()"
],
Yii::t('kvdrp', "This Month") => ["moment().startOf('month')", "moment().endOf('month')"],
Yii::t('kvdrp', "Last Month") => [
"moment().subtract(1, 'month').startOf('month')",
"moment().subtract(1, 'month').endOf('month')"
],
];
}
if (!$this->initRangeExpr || empty($this->pluginOptions['ranges']) || !is_array($this->pluginOptions['ranges'])) {
return;
}
$range = [];
foreach ($this->pluginOptions['ranges'] as $key => $value) {
if (!is_array($value) || empty($value[0]) || empty($value[1])) {
throw new InvalidConfigException("Invalid settings for pluginOptions['ranges']. Each range value must be a two element array.");
}
$range[$key] = [static::parseJsExpr($value[0]), static::parseJsExpr($value[1])];
}
$this->pluginOptions['ranges'] = $range;
}
/**
* Parses and returns a JsExpression
*
* @param string|JsExpression $value
*
* @return JsExpression
*/
protected static function parseJsExpr($value)
{
return $value instanceof JsExpression ? $value : new JsExpression($value);
}
/**
* Registers the needed client assets
*/
public function registerAssets()
{
$view = $this->getView();
MomentAsset::register($view);
$input = 'jQuery("#' . $this->options['id'] . '")';
$id = $input;
if ($this->hideInput) {
$id = 'jQuery("#' . $this->containerOptions['id'] . '")';
}
if (!empty($this->_langFile)) {
LanguageAsset::register($view)->js[] = $this->_langFile;
}
DateRangePickerAsset::register($view);
if (empty($this->callback)) {
if ($this->hideInput) {
$this->callback = <<< JS
function(start, end) {
var val = start.format('{$this->_format}') + '{$this->_separator}' + end.format('{$this->_format}');
{$id}.find('.range-value').html(val);
{$input}.val(val);
{$input}.trigger('change');
}
JS;
} elseif ($this->useWithAddon) {
$id = "{$input}.closest('.input-group')";
$this->callback = <<< JS
function(start, end) {
var val = start.format('{$this->_format}') + '{$this->_separator}' + end.format('{$this->_format}');
{$input}.val(val);
{$input}.trigger('change');
}
JS;
} else {
$this->registerPlugin($this->_pluginName, $id);
return;
}
}
$this->registerPlugin($this->_pluginName, $id, null, $this->callback);
}
/**
* Renders the input
*
* @return string
*/
protected function renderInput()
{
if (!$this->hideInput) {
Html::addCssClass($this->options, 'form-control');
return $this->getInput('textInput');
}
$tag = ArrayHelper::remove($this->containerOptions, 'tag', 'div');
$content = str_replace('{input}', $this->getInput('hiddenInput'), $this->containerTemplate);
return Html::tag($tag, $content, $this->containerOptions);
}
}