Setup TagField to work within AssetAdmin (Fixes #107)
This commit is contained in:
parent
bc7404005b
commit
068e4c6a26
|
@ -0,0 +1,5 @@
|
||||||
|
SilverStripe\Admin\LeftAndMain:
|
||||||
|
extra_requirements_css:
|
||||||
|
- 'silverstripe/tagfield:client/dist/styles/bundle.css'
|
||||||
|
extra_requirements_javascript:
|
||||||
|
- 'silverstripe/tagfield:client/dist/js/bundle.js'
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import fetch from 'isomorphic-fetch';
|
import fetch from 'isomorphic-fetch';
|
||||||
|
import fieldHolder from 'components/FieldHolder/FieldHolder';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import debounce from 'debounce-promise';
|
import debounce from 'debounce-promise';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
@ -15,6 +16,7 @@ class TagField extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onChange = this.onChange.bind(this);
|
this.onChange = this.onChange.bind(this);
|
||||||
|
this.handleOnBlur = this.handleOnBlur.bind(this);
|
||||||
this.getOptions = this.getOptions.bind(this);
|
this.getOptions = this.getOptions.bind(this);
|
||||||
this.fetchOptions = debounce(this.fetchOptions, 500);
|
this.fetchOptions = debounce(this.fetchOptions, 500);
|
||||||
}
|
}
|
||||||
|
@ -43,6 +45,15 @@ class TagField extends Component {
|
||||||
return this.fetchOptions(input);
|
return this.fetchOptions(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required to prevent TagField being cleared on blur
|
||||||
|
*
|
||||||
|
* @link https://github.com/JedWatson/react-select/issues/805
|
||||||
|
*/
|
||||||
|
handleOnBlur() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
fetchOptions(input) {
|
fetchOptions(input) {
|
||||||
const { optionUrl, labelKey, valueKey } = this.props;
|
const { optionUrl, labelKey, valueKey } = this.props;
|
||||||
const fetchURL = url.parse(optionUrl, true);
|
const fetchURL = url.parse(optionUrl, true);
|
||||||
|
@ -52,8 +63,8 @@ class TagField extends Component {
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((json) => ({
|
.then((json) => ({
|
||||||
options: json.items.map(item => ({
|
options: json.items.map(item => ({
|
||||||
[labelKey]: item.id,
|
[labelKey]: item.Title,
|
||||||
[valueKey]: item.text,
|
[valueKey]: item.Value,
|
||||||
}))
|
}))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -85,6 +96,7 @@ class TagField extends Component {
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
{...passThroughAttributes}
|
{...passThroughAttributes}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
onBlur={this.handleOnBlur}
|
||||||
inputProps={{ className: 'no-change-track' }}
|
inputProps={{ className: 'no-change-track' }}
|
||||||
{...optionAttributes}
|
{...optionAttributes}
|
||||||
/>
|
/>
|
||||||
|
@ -113,4 +125,6 @@ TagField.defaultProps = {
|
||||||
disabled: false
|
disabled: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TagField;
|
export { TagField as Component };
|
||||||
|
|
||||||
|
export default fieldHolder(TagField);
|
||||||
|
|
116
src/TagField.php
116
src/TagField.php
|
@ -6,15 +6,13 @@ use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Control\HTTPResponse;
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Forms\DropdownField;
|
use SilverStripe\Forms\MultiSelectField;
|
||||||
use SilverStripe\Forms\Validator;
|
use SilverStripe\Forms\Validator;
|
||||||
use SilverStripe\ORM\ArrayList;
|
use SilverStripe\ORM\ArrayList;
|
||||||
use SilverStripe\ORM\DataList;
|
use SilverStripe\ORM\DataList;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\ORM\DataObjectInterface;
|
use SilverStripe\ORM\DataObjectInterface;
|
||||||
use SilverStripe\ORM\SS_List;
|
|
||||||
use SilverStripe\View\ArrayData;
|
use SilverStripe\View\ArrayData;
|
||||||
use SilverStripe\View\Requirements;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a tagging interface, storing links between tag DataObjects and a parent DataObject.
|
* Provides a tagging interface, storing links between tag DataObjects and a parent DataObject.
|
||||||
|
@ -22,7 +20,7 @@ use SilverStripe\View\Requirements;
|
||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage fields
|
* @subpackage fields
|
||||||
*/
|
*/
|
||||||
class TagField extends DropdownField
|
class TagField extends MultiSelectField
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
|
@ -61,6 +59,9 @@ class TagField extends DropdownField
|
||||||
*/
|
*/
|
||||||
protected $isMultiple = true;
|
protected $isMultiple = true;
|
||||||
|
|
||||||
|
/** @skipUpgrade */
|
||||||
|
protected $schemaComponent = 'TagField';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string $title
|
* @param string $title
|
||||||
|
@ -236,19 +237,6 @@ class TagField extends DropdownField
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* When not used in a React form factory context, this adds the schema data to SilverStripe template
|
|
||||||
* rendered attributes lists
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getAttributes()
|
|
||||||
{
|
|
||||||
$attributes = parent::getAttributes();
|
|
||||||
$attributes['data-schema'] = json_encode($this->getSchemaData());
|
|
||||||
return $attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -258,11 +246,12 @@ class TagField extends DropdownField
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bool $onlySelected Only return options that are selected
|
|
||||||
* @return ArrayList
|
* @return ArrayList
|
||||||
*/
|
*/
|
||||||
protected function getOptions($onlySelected = false)
|
protected function getOptions()
|
||||||
{
|
{
|
||||||
|
$options = ArrayList::create();
|
||||||
|
|
||||||
$source = $this->getSourceList();
|
$source = $this->getSourceList();
|
||||||
|
|
||||||
if (!$source) {
|
if (!$source) {
|
||||||
|
@ -270,35 +259,28 @@ class TagField extends DropdownField
|
||||||
}
|
}
|
||||||
|
|
||||||
$dataClass = $source->dataClass();
|
$dataClass = $source->dataClass();
|
||||||
$titleField = $this->getTitleField();
|
|
||||||
$values = $this->Value();
|
$values = $this->Value();
|
||||||
|
|
||||||
if ($values) {
|
if (!$values) {
|
||||||
if (is_array($values)) {
|
return $options;
|
||||||
$values = $source->filter($titleField, $values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($onlySelected) {
|
|
||||||
$source = $values;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $source instanceof DataList ? $this->formatOptions($source) : ArrayList::create();
|
if (is_array($values)) {
|
||||||
}
|
$values = DataList::create($dataClass)
|
||||||
|
->filter($this->getTitleField(), $values);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = $values->column($this->getTitleField());
|
||||||
|
|
||||||
/**
|
|
||||||
* @param DataList $source
|
|
||||||
* @return ArrayList
|
|
||||||
*/
|
|
||||||
protected function formatOptions(DataList $source)
|
|
||||||
{
|
|
||||||
$options = ArrayList::create();
|
|
||||||
$titleField = $this->getTitleField();
|
$titleField = $this->getTitleField();
|
||||||
|
|
||||||
foreach ($source as $object) {
|
foreach ($source as $object) {
|
||||||
$options->push(
|
$options->push(
|
||||||
ArrayData::create([
|
ArrayData::create([
|
||||||
'Title' => $object->$titleField,
|
'Title' => $object->$titleField,
|
||||||
'Value' => $object->Title,
|
'Value' => $object->ID,
|
||||||
|
'Selected' => in_array($object->$titleField, $ids),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -315,17 +297,41 @@ class TagField extends DropdownField
|
||||||
$name = $this->getName();
|
$name = $this->getName();
|
||||||
|
|
||||||
if ($source->hasMethod($name)) {
|
if ($source->hasMethod($name)) {
|
||||||
$value = $source->$name()->column($this->getTitleField());
|
$values = [];
|
||||||
|
$titleField = $this->getTitleField();
|
||||||
|
|
||||||
|
foreach ($source->$name() as $tag) {
|
||||||
|
$values[] = [
|
||||||
|
'Title' => $tag->$titleField,
|
||||||
|
'Value' => $tag->ID,
|
||||||
|
'Selected' => true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::setValue($values);
|
||||||
}
|
}
|
||||||
} elseif ($value instanceof SS_List) {
|
|
||||||
$value = $value->column($this->getTitleField());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_array($value)) {
|
if (!is_array($value)) {
|
||||||
return parent::setValue($value);
|
return parent::setValue($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::setValue(array_filter($value));
|
return parent::setValue($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getAttributes()
|
||||||
|
{
|
||||||
|
return array_merge(
|
||||||
|
parent::getAttributes(),
|
||||||
|
[
|
||||||
|
'name' => $this->getName() . '[]',
|
||||||
|
'style' => 'width: 100%',
|
||||||
|
'data-schema' => json_encode($this->getSchemaData()),
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -333,8 +339,6 @@ class TagField extends DropdownField
|
||||||
*/
|
*/
|
||||||
public function saveInto(DataObjectInterface $record)
|
public function saveInto(DataObjectInterface $record)
|
||||||
{
|
{
|
||||||
parent::saveInto($record);
|
|
||||||
|
|
||||||
$name = $this->getName();
|
$name = $this->getName();
|
||||||
$titleField = $this->getTitleField();
|
$titleField = $this->getTitleField();
|
||||||
$values = $this->Value();
|
$values = $this->Value();
|
||||||
|
@ -344,6 +348,7 @@ class TagField extends DropdownField
|
||||||
if (!$values) {
|
if (!$values) {
|
||||||
$values = [];
|
$values = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($record) || empty($titleField)) {
|
if (empty($record) || empty($titleField)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -388,6 +393,11 @@ class TagField extends DropdownField
|
||||||
if ($this->getCanCreate()) {
|
if ($this->getCanCreate()) {
|
||||||
$dataClass = $source->dataClass();
|
$dataClass = $source->dataClass();
|
||||||
$record = Injector::inst()->create($dataClass);
|
$record = Injector::inst()->create($dataClass);
|
||||||
|
|
||||||
|
if (is_array($term)) {
|
||||||
|
$term = $term['Value'];
|
||||||
|
}
|
||||||
|
|
||||||
$record->{$titleField} = $term;
|
$record->{$titleField} = $term;
|
||||||
$record->write();
|
$record->write();
|
||||||
if ($source instanceof SS_List) {
|
if ($source instanceof SS_List) {
|
||||||
|
@ -438,8 +448,8 @@ class TagField extends DropdownField
|
||||||
$titleField = $this->getTitleField();
|
$titleField = $this->getTitleField();
|
||||||
foreach ($query->map('ID', $titleField) as $id => $title) {
|
foreach ($query->map('ID', $titleField) as $id => $title) {
|
||||||
$items[$title] = [
|
$items[$title] = [
|
||||||
'id' => $title,
|
'Title' => $title,
|
||||||
'text' => $title,
|
'Value' => $id,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,4 +490,20 @@ class TagField extends DropdownField
|
||||||
{
|
{
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSchemaStateDefaults()
|
||||||
|
{
|
||||||
|
$data = parent::getSchemaStateDefaults();
|
||||||
|
|
||||||
|
// Add options to 'data'
|
||||||
|
$data['lazyLoad'] = $this->getShouldLazyLoad();
|
||||||
|
$data['multi'] = $this->getIsMultiple();
|
||||||
|
$data['optionUrl'] = $this->getSuggestURL();
|
||||||
|
$data['creatable'] = $this->getCanCreate();
|
||||||
|
$data['value'] = $this->Value();
|
||||||
|
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue