Setup TagField to work within AssetAdmin (Fixes #107)

This commit is contained in:
Will Rossiter 2018-10-10 15:33:28 +13:00 committed by Guy Marriott
parent bc7404005b
commit 068e4c6a26
No known key found for this signature in database
GPG Key ID: A80F9ACCB86D3DA7
3 changed files with 93 additions and 48 deletions

5
_config/admin.yml Normal file
View File

@ -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'

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import Select from 'react-select';
import fetch from 'isomorphic-fetch';
import fieldHolder from 'components/FieldHolder/FieldHolder';
import url from 'url';
import debounce from 'debounce-promise';
import PropTypes from 'prop-types';
@ -15,6 +16,7 @@ class TagField extends Component {
};
this.onChange = this.onChange.bind(this);
this.handleOnBlur = this.handleOnBlur.bind(this);
this.getOptions = this.getOptions.bind(this);
this.fetchOptions = debounce(this.fetchOptions, 500);
}
@ -43,6 +45,15 @@ class TagField extends Component {
return this.fetchOptions(input);
}
/**
* Required to prevent TagField being cleared on blur
*
* @link https://github.com/JedWatson/react-select/issues/805
*/
handleOnBlur() {
}
fetchOptions(input) {
const { optionUrl, labelKey, valueKey } = this.props;
const fetchURL = url.parse(optionUrl, true);
@ -52,8 +63,8 @@ class TagField extends Component {
.then((response) => response.json())
.then((json) => ({
options: json.items.map(item => ({
[labelKey]: item.id,
[valueKey]: item.text,
[labelKey]: item.Title,
[valueKey]: item.Value,
}))
}));
}
@ -85,6 +96,7 @@ class TagField extends Component {
<SelectComponent
{...passThroughAttributes}
onChange={this.onChange}
onBlur={this.handleOnBlur}
inputProps={{ className: 'no-change-track' }}
{...optionAttributes}
/>
@ -113,4 +125,6 @@ TagField.defaultProps = {
disabled: false
};
export default TagField;
export { TagField as Component };
export default fieldHolder(TagField);

View File

@ -6,15 +6,13 @@ use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\MultiSelectField;
use SilverStripe\Forms\Validator;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\SS_List;
use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
/**
* Provides a tagging interface, storing links between tag DataObjects and a parent DataObject.
@ -22,7 +20,7 @@ use SilverStripe\View\Requirements;
* @package forms
* @subpackage fields
*/
class TagField extends DropdownField
class TagField extends MultiSelectField
{
/**
* @var array
@ -61,6 +59,9 @@ class TagField extends DropdownField
*/
protected $isMultiple = true;
/** @skipUpgrade */
protected $schemaComponent = 'TagField';
/**
* @param string $name
* @param string $title
@ -236,19 +237,6 @@ class TagField extends DropdownField
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
*/
@ -258,11 +246,12 @@ class TagField extends DropdownField
}
/**
* @param bool $onlySelected Only return options that are selected
* @return ArrayList
*/
protected function getOptions($onlySelected = false)
protected function getOptions()
{
$options = ArrayList::create();
$source = $this->getSourceList();
if (!$source) {
@ -270,35 +259,28 @@ class TagField extends DropdownField
}
$dataClass = $source->dataClass();
$titleField = $this->getTitleField();
$values = $this->Value();
if ($values) {
if (is_array($values)) {
$values = $source->filter($titleField, $values);
}
}
if ($onlySelected) {
$source = $values;
if (!$values) {
return $options;
}
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();
foreach ($source as $object) {
$options->push(
ArrayData::create([
'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();
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)) {
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)
{
parent::saveInto($record);
$name = $this->getName();
$titleField = $this->getTitleField();
$values = $this->Value();
@ -344,6 +348,7 @@ class TagField extends DropdownField
if (!$values) {
$values = [];
}
if (empty($record) || empty($titleField)) {
return;
}
@ -388,6 +393,11 @@ class TagField extends DropdownField
if ($this->getCanCreate()) {
$dataClass = $source->dataClass();
$record = Injector::inst()->create($dataClass);
if (is_array($term)) {
$term = $term['Value'];
}
$record->{$titleField} = $term;
$record->write();
if ($source instanceof SS_List) {
@ -438,8 +448,8 @@ class TagField extends DropdownField
$titleField = $this->getTitleField();
foreach ($query->map('ID', $titleField) as $id => $title) {
$items[$title] = [
'id' => $title,
'text' => $title,
'Title' => $title,
'Value' => $id,
];
}
@ -480,4 +490,20 @@ class TagField extends DropdownField
{
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;
}
}