mirror of
https://github.com/silverstripe/silverstripe-tagfield
synced 2024-07-01 00:49:19 +02:00
Compare commits
No commits in common. "2" and "2.4.0" have entirely different histories.
|
@ -13,7 +13,6 @@ trim_trailing_whitespace = true
|
|||
[{*.yml,package.json,*.js,*.scss}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
quote_type = single
|
||||
|
||||
# The indent size used in the package.json file cannot be changed:
|
||||
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
|
||||
|
|
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
|
@ -1,11 +0,0 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: CI
|
||||
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
|
16
.github/workflows/dispatch-ci.yml
vendored
16
.github/workflows/dispatch-ci.yml
vendored
|
@ -1,16 +0,0 @@
|
|||
name: Dispatch CI
|
||||
|
||||
on:
|
||||
# At 12:00 PM UTC, only on Saturday and Sunday
|
||||
schedule:
|
||||
- cron: '0 12 * * 6,0'
|
||||
|
||||
jobs:
|
||||
dispatch-ci:
|
||||
name: Dispatch CI
|
||||
# Only run cron on the silverstripe account
|
||||
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Dispatch CI
|
||||
uses: silverstripe/gha-dispatch-ci@v1
|
17
.github/workflows/keepalive.yml
vendored
17
.github/workflows/keepalive.yml
vendored
|
@ -1,17 +0,0 @@
|
|||
name: Keepalive
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# The 4th of every month at 10:50am UTC
|
||||
schedule:
|
||||
- cron: '50 10 4 * *'
|
||||
|
||||
jobs:
|
||||
keepalive:
|
||||
name: Keepalive
|
||||
# Only run cron on the silverstripe account
|
||||
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Keepalive
|
||||
uses: silverstripe/gha-keepalive@v1
|
17
.github/workflows/update-js.yml
vendored
17
.github/workflows/update-js.yml
vendored
|
@ -1,17 +0,0 @@
|
|||
name: Update JS
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# Run on a schedule of once per quarter
|
||||
schedule:
|
||||
- cron: '0 0 1 */3 *'
|
||||
|
||||
jobs:
|
||||
update-js:
|
||||
name: Update JS
|
||||
# Only run cron on the silverstripe account
|
||||
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update JS
|
||||
uses: silverstripe/gha-update-js@v1
|
12
.scrutinizer.yml
Normal file
12
.scrutinizer.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
checks:
|
||||
php: true
|
||||
|
||||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
tests:
|
||||
override: [php-scrutinizer-run]
|
||||
|
||||
filter:
|
||||
paths: ["src/*", "tests/*"]
|
||||
|
43
.travis.yml
Normal file
43
.travis.yml
Normal file
|
@ -0,0 +1,43 @@
|
|||
language: php
|
||||
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
global:
|
||||
- COMPOSER_ROOT_VERSION=2.0.x-dev
|
||||
- TRAVIS_NODE_VERSION="6"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.6
|
||||
env: DB=MYSQL PHPCS_TEST=1 PHPUNIT_TEST=1
|
||||
- php: 7.0
|
||||
env: DB=PGSQL PHPUNIT_TEST=1
|
||||
- php: 7.1
|
||||
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
|
||||
- php: 7.1
|
||||
env: DB=MYSQL NPM_TEST=1
|
||||
|
||||
before_script:
|
||||
- phpenv rehash
|
||||
- phpenv config-rm xdebug.ini
|
||||
|
||||
- composer validate
|
||||
- composer require --no-update silverstripe/recipe-cms:4.3.x-dev silverstripe/admin:1.3.x-dev
|
||||
- if [[ $DB == PGSQL ]]; then composer require --no-update silverstripe/postgresql:2.1.x-dev; fi
|
||||
- composer install --prefer-source --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
|
||||
|
||||
# Install NPM dependencies
|
||||
- if [[ $NPM_TEST ]]; then nvm install $TRAVIS_NODE_VERSION && nvm use $TRAVIS_NODE_VERSION && npm install -g yarn && yarn install --network-concurrency 1 && (cd vendor/silverstripe/admin && yarn install --network-concurrency 1) && yarn run build; fi
|
||||
|
||||
script:
|
||||
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
|
||||
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
|
||||
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs src/ tests/ *.php; fi
|
||||
- if [[ $NPM_TEST ]]; then git diff-files --quiet -w --relative=client; fi
|
||||
- if [[ $NPM_TEST ]]; then git diff --name-status --relative=client; fi
|
||||
- if [[ $NPM_TEST ]]; then yarn run test; fi
|
||||
- if [[ $NPM_TEST ]]; then yarn run lint; fi
|
||||
|
||||
after_success:
|
||||
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
|
166
README.md
166
README.md
|
@ -1,166 +0,0 @@
|
|||
# Tag Field
|
||||
|
||||
[![CI](https://github.com/silverstripe/silverstripe-tagfield/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-tagfield/actions/workflows/ci.yml)
|
||||
[![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
|
||||
|
||||
Custom tag input field, for SilverStripe.
|
||||
|
||||
## Overview
|
||||
|
||||
![Screenshot](docs/en/screenshot.png)
|
||||
|
||||
Allows storing tags as a relationship, or comma-delimited strings.
|
||||
Supports autocompletion with lazy-loading.
|
||||
|
||||
Note: The field is optimised for usage in the Silverstripe CMS UI.
|
||||
The form field class itself can be used outside of the CMS,
|
||||
but you'll need to build your own frontend to interpret the raw field data (`data-schema` attribute).
|
||||
|
||||
## Requirements
|
||||
|
||||
* Silverstripe 4.0
|
||||
|
||||
## Installing
|
||||
|
||||
```sh
|
||||
$ composer require silverstripe/tagfield
|
||||
```
|
||||
|
||||
## Using
|
||||
|
||||
### Relational Tags
|
||||
|
||||
```php
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class BlogPost extends DataObject
|
||||
{
|
||||
private static $many_many = [
|
||||
'BlogTags' => BlogTag::class
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class BlogTag extends DataObject
|
||||
{
|
||||
private static $db = [
|
||||
'Title' => 'Varchar(200)',
|
||||
];
|
||||
|
||||
private static $belongs_many_many = [
|
||||
'BlogPosts' => BlogPost::class
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
$field = TagField::create(
|
||||
'BlogTags',
|
||||
'Blog Tags',
|
||||
BlogTag::get(),
|
||||
$this->BlogTags()
|
||||
)
|
||||
->setShouldLazyLoad(true) // tags should be lazy loaded
|
||||
->setCanCreate(true); // new tag DataObjects can be created
|
||||
```
|
||||
**Note:** This assumes you have imported the namespaces class, e.g. use
|
||||
SilverStripe\TagField\TagField;
|
||||
|
||||
#### Has-One Relations
|
||||
|
||||
You can also use the TagField to select values for `has_one` relations.
|
||||
Let's assume, that a `BlogPost` *has one* `BlogCategory`.
|
||||
|
||||
```php
|
||||
class BlogCategory extends DataObject
|
||||
{
|
||||
private static $db = [
|
||||
'Title' => 'Varchar(200)',
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class BlogPost extends DataObject
|
||||
{
|
||||
private static $has_one = [
|
||||
'BlogCategory' => BlogCategory::class
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
$field = TagField::create(
|
||||
'BlogCategoryID',
|
||||
$this->fieldLabel('BlogCategory'),
|
||||
BlogCategory::get()
|
||||
)
|
||||
->setIsMultiple(false)
|
||||
->setCanCreate(true);
|
||||
```
|
||||
|
||||
**Note:** We're using the `ID` suffix for the field-name (eg. `BlogCategoryID` instead of `BlogCategory`) and
|
||||
only allow one value by setting `->setIsMultiple(false)`
|
||||
|
||||
### String Tags
|
||||
|
||||
```php
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class BlogPost extends DataObject
|
||||
{
|
||||
private static $db = [
|
||||
'Tags' => 'Text',
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
$field = StringTagField::create(
|
||||
'Tags',
|
||||
'Tags',
|
||||
['one', 'two'],
|
||||
explode(',', $this->Tags)
|
||||
);
|
||||
|
||||
$field->setShouldLazyLoad(true); // tags should be lazy loaded
|
||||
```
|
||||
|
||||
You can find more in-depth documentation in [docs/en](docs/en/introduction.md).
|
||||
|
||||
## Using TagField with silverstripe-taxonomy
|
||||
|
||||
TagField assumes a `Title` field on objects. For classes without a `Title` field
|
||||
use `setTitleField` to modify accordingly.
|
||||
|
||||
```php
|
||||
$field = TagField::create(
|
||||
'Tags',
|
||||
'Blog Tags',
|
||||
TaxonomyTerm::get(),
|
||||
)
|
||||
->setTitleField('Name');
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
This library follows [Semver](http://semver.org). According to Semver, you will
|
||||
be able to upgrade to any minor or patch version of this library without any
|
||||
breaking changes to the public API. Semver also requires that we clearly define
|
||||
the public API for this library.
|
||||
|
||||
All methods, with `public` visibility, are part of the public API. All other
|
||||
methods are not part of the public API. Where possible, we'll try to keep
|
||||
`protected` methods backwards-compatible in minor/patch versions, but if you're
|
||||
overriding methods then please test your work before upgrading.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Please [create an
|
||||
issue](http://github.com/silverstripe/silverstripe-tagfield/issues) for any bugs
|
||||
you've found, or features you're missing.
|
2
client/dist/js/bundle.js
vendored
2
client/dist/js/bundle.js
vendored
|
@ -1 +1 @@
|
|||
!function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle.js")}({"./client/src/boot/index.js":function(e,t,n){"use strict";var o=n("./client/src/boot/registerComponents.js"),r=function(e){return e&&e.__esModule?e:{default:e}}(o);window.document.addEventListener("DOMContentLoaded",function(){(0,r.default)()})},"./client/src/boot/registerComponents.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),s=o(r),i=n("./client/src/components/TagField.js"),a=o(i);t.default=function(){s.default.component.registerMany({TagField:a.default})}},"./client/src/bundles/bundle.js":function(e,t,n){"use strict";n("./client/src/legacy/entwine/TagField.js"),n("./client/src/boot/index.js")},"./client/src/components/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n={};for(var o in e)t.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(n[o]=e[o]);return n}function s(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.Component=void 0;var l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},c=function(){function e(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(t,n,o){return n&&e(t.prototype,n),o&&e(t,o),t}}(),f=n(1),p=o(f),d=n(5),m=o(d),v=n(3),y=o(v),b=n(2),g=o(b),j=n("./node_modules/url/url.js"),O=o(j),w=n("./node_modules/debounce-promise/dist/index.js"),C=o(w),x=n("./node_modules/prop-types/index.js"),_=o(x),S=function(e){function t(e){i(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.selectComponentRef=null,n.setSelectComponentRef=function(e){n.selectComponentRef=e},n.state={initalState:e.value?e.value:[],hasChanges:!1},n.isControlled()||(n.state=h({},n.state,{value:e.value})),n.handleChange=n.handleChange.bind(n),n.handleOnBlur=n.handleOnBlur.bind(n),n.getOptions=n.getOptions.bind(n),n.fetchOptions=(0,C.default)(n.fetchOptions,500),n}return u(t,e),c(t,[{key:"componentDidUpdate",value:function(e,t){if(t.hasChanges!==this.state.hasChanges){var n=this.selectComponentRef.select.wrapper,o=new Event("change",{bubbles:!0});n.dispatchEvent(o)}}},{key:"getOptions",value:function(e){var t=this.props,n=t.lazyLoad,o=t.options;return n?e?this.fetchOptions(e):Promise.resolve({options:[]}):Promise.resolve({options:o})}},{key:"handleChange",value:function(e){if(this.setState({hasChanges:!1}),JSON.stringify(this.state.initalState)!==JSON.stringify(e)&&this.setState({hasChanges:!0}),this.isControlled())return void this.props.onChange(e);this.setState({value:e})}},{key:"isControlled",value:function(){return"function"==typeof this.props.onChange}},{key:"handleOnBlur",value:function(){}},{key:"fetchOptions",value:function(e){var t=this.props,n=t.optionUrl,o=t.labelKey,r=t.valueKey,i=O.default.parse(n,!0);return i.query.term=e,(0,y.default)(O.default.format(i),{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return{options:e.items.map(function(e){var t;return t={},s(t,o,e.Title),s(t,r,e.Value),s(t,"Selected",e.Selected),t})}})}},{key:"render",value:function(){var e=this.props,t=e.lazyLoad,n=e.options,o=e.creatable,s=r(e,["lazyLoad","options","creatable"]),i=t?{loadOptions:this.getOptions}:{options:n},a=m.default;if(t&&o?a=m.default.AsyncCreatable:t?a=m.default.Async:o&&(a=m.default.Creatable),this.isControlled()||(s.value=this.state.value),!s.multi&&s.value&&Object.keys(s.value).length>0){var u=s.value[Object.keys(s.value)[0]];"object"===(void 0===u?"undefined":l(u))&&(s.value=u)}var c=this.state.hasChanges?"":"no-change-track";return p.default.createElement(a,h({},s,{onChange:this.handleChange,onBlur:this.handleOnBlur},i,{className:c,ref:this.setSelectComponentRef}))}}]),t}(f.Component);S.propTypes={name:_.default.string.isRequired,labelKey:_.default.string.isRequired,valueKey:_.default.string.isRequired,lazyLoad:_.default.bool,creatable:_.default.bool,multi:_.default.bool,disabled:_.default.bool,options:_.default.arrayOf(_.default.object),optionUrl:_.default.string,value:_.default.any,onChange:_.default.func,onBlur:_.default.func},S.defaultProps={labelKey:"Title",valueKey:"Value",disabled:!1,lazyLoad:!1,creatable:!1,multi:!1},t.Component=S,t.default=(0,g.default)(S)},"./client/src/legacy/entwine/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s=n(1),i=o(s),a=n(4),u=o(a),l=n(0);window.jQuery.entwine("ss",function(e){e(".js-injector-boot .ss-tag-field.entwine").entwine({onmatch:function(){var e=this,t=this.closest(".cms-content").attr("id"),n=t?{context:t}:{},o=(0,l.loadComponent)("TagField",n),s=r({},this.data("schema"),{onBlur:function(){e.parents(".cms-edit-form:first").trigger("change")}});u.default.render(i.default.createElement(o,r({noHolder:!0},s)),this[0])},onunmatch:function(){u.default.unmountComponentAtNode(this[0])}}),e(".cms-edit-form").entwine({getChangeTrackerOptions:function(){var t=void 0===this.entwineData("ChangeTrackerOptions"),n=this._super();return t&&(n=e.extend({},n),n.ignoreFieldSelector+=", .ss-tag-field .no-change-track :input",this.setChangeTrackerOptions(n)),n}})})},"./node_modules/debounce-promise/dist/index.js":function(e,t,n){"use strict";function o(e){return"function"==typeof e?e():e}function r(){var e={};return e.promise=new Promise(function(t,n){e.resolve=t,e.reject=n}),e}e.exports=function(e){function t(){var t=a;clearTimeout(u),Promise.resolve(s.accumulate?e.call(this,l):e.apply(this,l[l.length-1])).then(t.resolve,t.reject),l=[],a=null}var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=void 0,a=void 0,u=void 0,l=[];return function(){var h=o(n),c=(new Date).getTime(),f=!i||c-i>h;i=c;for(var p=arguments.length,d=Array(p),m=0;m<p;m++)d[m]=arguments[m];if(f&&s.leading)return s.accumulate?Promise.resolve(e.call(this,[d])).then(function(e){return e[0]}):Promise.resolve(e.call.apply(e,[this].concat(d)));if(a?clearTimeout(u):a=r(),l.push(d),u=setTimeout(t.bind(this),h),s.accumulate){var v=l.length-1;return a.promise.then(function(e){return e[v]})}return a.promise}}},"./node_modules/node-libs-browser/node_modules/punycode/punycode.js":function(e,t,n){(function(e,o){var r;!function(o){function s(e){throw new RangeError(R[e])}function i(e,t){for(var n=e.length,o=[];n--;)o[n]=t(e[n]);return o}function a(e,t){var n=e.split("@"),o="";return n.length>1&&(o=n[0]+"@",e=n[1]),e=e.replace(k,"."),o+i(e.split("."),t).join(".")}function u(e){for(var t,n,o=[],r=0,s=e.length;r<s;)t=e.charCodeAt(r++),t>=55296&&t<=56319&&r<s?(n=e.charCodeAt(r++),56320==(64512&n)?o.push(((1023&t)<<10)+(1023&n)+65536):(o.push(t),r--)):o.push(t);return o}function l(e){return i(e,function(e){var t="";return e>65535&&(e-=65536,t+=I(e>>>10&1023|55296),e=56320|1023&e),t+=I(e)}).join("")}function h(e){return e-48<10?e-22:e-65<26?e-65:e-97<26?e-97:g}function c(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function f(e,t,n){var o=0;for(e=n?A(e/C):e>>1,e+=A(e/t);e>q*O>>1;o+=g)e=A(e/q);return A(o+(q+1)*e/(e+w))}function p(e){var t,n,o,r,i,a,u,c,p,d,m=[],v=e.length,y=0,w=_,C=x;for(n=e.lastIndexOf(S),n<0&&(n=0),o=0;o<n;++o)e.charCodeAt(o)>=128&&s("not-basic"),m.push(e.charCodeAt(o));for(r=n>0?n+1:0;r<v;){for(i=y,a=1,u=g;r>=v&&s("invalid-input"),c=h(e.charCodeAt(r++)),(c>=g||c>A((b-y)/a))&&s("overflow"),y+=c*a,p=u<=C?j:u>=C+O?O:u-C,!(c<p);u+=g)d=g-p,a>A(b/d)&&s("overflow"),a*=d;t=m.length+1,C=f(y-i,t,0==i),A(y/t)>b-w&&s("overflow"),w+=A(y/t),y%=t,m.splice(y++,0,w)}return l(m)}function d(e){var t,n,o,r,i,a,l,h,p,d,m,v,y,w,C,T=[];for(e=u(e),v=e.length,t=_,n=0,i=x,a=0;a<v;++a)(m=e[a])<128&&T.push(I(m));for(o=r=T.length,r&&T.push(S);o<v;){for(l=b,a=0;a<v;++a)(m=e[a])>=t&&m<l&&(l=m);for(y=o+1,l-t>A((b-n)/y)&&s("overflow"),n+=(l-t)*y,t=l,a=0;a<v;++a)if(m=e[a],m<t&&++n>b&&s("overflow"),m==t){for(h=n,p=g;d=p<=i?j:p>=i+O?O:p-i,!(h<d);p+=g)C=h-d,w=g-d,T.push(I(c(d+C%w,0))),h=A(C/w);T.push(I(c(h,0))),i=f(n,y,o==r),n=0,++o}++n,++t}return T.join("")}function m(e){return a(e,function(e){return T.test(e)?p(e.slice(4).toLowerCase()):e})}function v(e){return a(e,function(e){return P.test(e)?"xn--"+d(e):e})}var y,b=("object"==typeof t&&t&&t.nodeType,"object"==typeof e&&e&&e.nodeType,2147483647),g=36,j=1,O=26,w=38,C=700,x=72,_=128,S="-",T=/^xn--/,P=/[^\x20-\x7E]/,k=/[\x2E\u3002\uFF0E\uFF61]/g,R={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},q=g-j,A=Math.floor,I=String.fromCharCode;y={version:"1.4.1",ucs2:{decode:u,encode:l},decode:p,encode:d,toASCII:v,toUnicode:m},void 0!==(r=function(){return y}.call(t,n,t,e))&&(e.exports=r)}()}).call(t,n("./node_modules/webpack/buildin/module.js")(e),n("./node_modules/webpack/buildin/global.js"))},"./node_modules/prop-types/factoryWithThrowingShims.js":function(e,t,n){"use strict";function o(){}function r(){}var s=n("./node_modules/prop-types/lib/ReactPropTypesSecret.js");r.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,r,i){if(i!==s){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:r,resetWarningCache:o};return n.PropTypes=n,n}},"./node_modules/prop-types/index.js":function(e,t,n){e.exports=n("./node_modules/prop-types/factoryWithThrowingShims.js")()},"./node_modules/prop-types/lib/ReactPropTypesSecret.js":function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},"./node_modules/querystring-es3/decode.js":function(e,t,n){"use strict";function o(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,s){t=t||"&",n=n||"=";var i={};if("string"!=typeof e||0===e.length)return i;var a=/\+/g;e=e.split(t);var u=1e3;s&&"number"==typeof s.maxKeys&&(u=s.maxKeys);var l=e.length;u>0&&l>u&&(l=u);for(var h=0;h<l;++h){var c,f,p,d,m=e[h].replace(a,"%20"),v=m.indexOf(n);v>=0?(c=m.substr(0,v),f=m.substr(v+1)):(c=m,f=""),p=decodeURIComponent(c),d=decodeURIComponent(f),o(i,p)?r(i[p])?i[p].push(d):i[p]=[i[p],d]:i[p]=d}return i};var r=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},"./node_modules/querystring-es3/encode.js":function(e,t,n){"use strict";function o(e,t){if(e.map)return e.map(t);for(var n=[],o=0;o<e.length;o++)n.push(t(e[o],o));return n}var r=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,a){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?o(i(e),function(i){var a=encodeURIComponent(r(i))+n;return s(e[i])?o(e[i],function(e){return a+encodeURIComponent(r(e))}).join(t):a+encodeURIComponent(r(e[i]))}).join(t):a?encodeURIComponent(r(a))+n+encodeURIComponent(r(e)):""};var s=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},"./node_modules/querystring-es3/index.js":function(e,t,n){"use strict";t.decode=t.parse=n("./node_modules/querystring-es3/decode.js"),t.encode=t.stringify=n("./node_modules/querystring-es3/encode.js")},"./node_modules/url/url.js":function(e,t,n){"use strict";function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function r(e,t,n){if(e&&l.isObject(e)&&e instanceof o)return e;var r=new o;return r.parse(e,t,n),r}function s(e){return l.isString(e)&&(e=r(e)),e instanceof o?e.format():o.prototype.format.call(e)}function i(e,t){return r(e,!1,!0).resolve(t)}function a(e,t){return e?r(e,!1,!0).resolveObject(t):t}var u=n("./node_modules/node-libs-browser/node_modules/punycode/punycode.js"),l=n("./node_modules/url/util.js");t.parse=r,t.resolve=i,t.resolveObject=a,t.format=s,t.Url=o;var h=/^([a-z0-9.+-]+:)/i,c=/:[0-9]*$/,f=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,p=["<",">",'"',"`"," ","\r","\n","\t"],d=["{","}","|","\\","^","`"].concat(p),m=["'"].concat(d),v=["%","/","?",";","#"].concat(m),y=["/","?","#"],b=/^[+a-z0-9A-Z_-]{0,63}$/,g=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,j={javascript:!0,"javascript:":!0},O={javascript:!0,"javascript:":!0},w={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},C=n("./node_modules/querystring-es3/index.js");o.prototype.parse=function(e,t,n){if(!l.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),r=-1!==o&&o<e.indexOf("#")?"?":"#",s=e.split(r),i=/\\/g;s[0]=s[0].replace(i,"/"),e=s.join(r);var a=e;if(a=a.trim(),!n&&1===e.split("#").length){var c=f.exec(a);if(c)return this.path=a,this.href=a,this.pathname=c[1],c[2]?(this.search=c[2],this.query=t?C.parse(this.search.substr(1)):this.search.substr(1)):t&&(this.search="",this.query={}),this}var p=h.exec(a);if(p){p=p[0];var d=p.toLowerCase();this.protocol=d,a=a.substr(p.length)}if(n||p||a.match(/^\/\/[^@\/]+@[^@\/]+/)){var x="//"===a.substr(0,2);!x||p&&O[p]||(a=a.substr(2),this.slashes=!0)}if(!O[p]&&(x||p&&!w[p])){for(var _=-1,S=0;S<y.length;S++){var T=a.indexOf(y[S]);-1!==T&&(-1===_||T<_)&&(_=T)}var P,k;k=-1===_?a.lastIndexOf("@"):a.lastIndexOf("@",_),-1!==k&&(P=a.slice(0,k),a=a.slice(k+1),this.auth=decodeURIComponent(P)),_=-1;for(var S=0;S<v.length;S++){var T=a.indexOf(v[S]);-1!==T&&(-1===_||T<_)&&(_=T)}-1===_&&(_=a.length),this.host=a.slice(0,_),a=a.slice(_),this.parseHost(),this.hostname=this.hostname||"";var R="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!R)for(var q=this.hostname.split(/\./),S=0,A=q.length;S<A;S++){var I=q[S];if(I&&!I.match(b)){for(var U="",E=0,F=I.length;E<F;E++)I.charCodeAt(E)>127?U+="x":U+=I[E];if(!U.match(b)){var L=q.slice(0,S),N=q.slice(S+1),M=I.match(g);M&&(L.push(M[1]),N.unshift(M[2])),N.length&&(a="/"+N.join(".")+a),this.hostname=L.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),R||(this.hostname=u.toASCII(this.hostname));var z=this.port?":"+this.port:"",B=this.hostname||"";this.host=B+z,this.href+=this.host,R&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==a[0]&&(a="/"+a))}if(!j[d])for(var S=0,A=m.length;S<A;S++){var K=m[S];if(-1!==a.indexOf(K)){var D=encodeURIComponent(K);D===K&&(D=escape(K)),a=a.split(K).join(D)}}var H=a.indexOf("#");-1!==H&&(this.hash=a.substr(H),a=a.slice(0,H));var W=a.indexOf("?");if(-1!==W?(this.search=a.substr(W),this.query=a.substr(W+1),t&&(this.query=C.parse(this.query)),a=a.slice(0,W)):t&&(this.search="",this.query={}),a&&(this.pathname=a),w[d]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var z=this.pathname||"",$=this.search||"";this.path=z+$}return this.href=this.format(),this},o.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",o=this.hash||"",r=!1,s="";this.host?r=e+this.host:this.hostname&&(r=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(r+=":"+this.port)),this.query&&l.isObject(this.query)&&Object.keys(this.query).length&&(s=C.stringify(this.query));var i=this.search||s&&"?"+s||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||w[t])&&!1!==r?(r="//"+(r||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):r||(r=""),o&&"#"!==o.charAt(0)&&(o="#"+o),i&&"?"!==i.charAt(0)&&(i="?"+i),n=n.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),i=i.replace("#","%23"),t+r+n+i+o},o.prototype.resolve=function(e){return this.resolveObject(r(e,!1,!0)).format()},o.prototype.resolveObject=function(e){if(l.isString(e)){var t=new o;t.parse(e,!1,!0),e=t}for(var n=new o,r=Object.keys(this),s=0;s<r.length;s++){var i=r[s];n[i]=this[i]}if(n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol){for(var a=Object.keys(e),u=0;u<a.length;u++){var h=a[u];"protocol"!==h&&(n[h]=e[h])}return w[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n}if(e.protocol&&e.protocol!==n.protocol){if(!w[e.protocol]){for(var c=Object.keys(e),f=0;f<c.length;f++){var p=c[f];n[p]=e[p]}return n.href=n.format(),n}if(n.protocol=e.protocol,e.host||O[e.protocol])n.pathname=e.pathname;else{for(var d=(e.pathname||"").split("/");d.length&&!(e.host=d.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==d[0]&&d.unshift(""),d.length<2&&d.unshift(""),n.pathname=d.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var m=n.pathname||"",v=n.search||"";n.path=m+v}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var y=n.pathname&&"/"===n.pathname.charAt(0),b=e.host||e.pathname&&"/"===e.pathname.charAt(0),g=b||y||n.host&&e.pathname,j=g,C=n.pathname&&n.pathname.split("/")||[],d=e.pathname&&e.pathname.split("/")||[],x=n.protocol&&!w[n.protocol];if(x&&(n.hostname="",n.port=null,n.host&&(""===C[0]?C[0]=n.host:C.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===d[0]?d[0]=e.host:d.unshift(e.host)),e.host=null),g=g&&(""===d[0]||""===C[0])),b)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,C=d;else if(d.length)C||(C=[]),C.pop(),C=C.concat(d),n.search=e.search,n.query=e.query;else if(!l.isNullOrUndefined(e.search)){if(x){n.hostname=n.host=C.shift();var _=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");_&&(n.auth=_.shift(),n.host=n.hostname=_.shift())}return n.search=e.search,n.query=e.query,l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!C.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var S=C.slice(-1)[0],T=(n.host||e.host||C.length>1)&&("."===S||".."===S)||""===S,P=0,k=C.length;k>=0;k--)S=C[k],"."===S?C.splice(k,1):".."===S?(C.splice(k,1),P++):P&&(C.splice(k,1),P--);if(!g&&!j)for(;P--;P)C.unshift("..");!g||""===C[0]||C[0]&&"/"===C[0].charAt(0)||C.unshift(""),T&&"/"!==C.join("/").substr(-1)&&C.push("");var R=""===C[0]||C[0]&&"/"===C[0].charAt(0);if(x){n.hostname=n.host=R?"":C.length?C.shift():"";var _=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");_&&(n.auth=_.shift(),n.host=n.hostname=_.shift())}return g=g||n.host&&C.length,g&&!R&&C.unshift(""),C.length?n.pathname=C.join("/"):(n.pathname=null,n.path=null),l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},o.prototype.parseHost=function(){var e=this.host,t=c.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},"./node_modules/url/util.js":function(e,t,n){"use strict";e.exports={isString:function(e){return"string"==typeof e},isObject:function(e){return"object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},"./node_modules/webpack/buildin/global.js":function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},"./node_modules/webpack/buildin/module.js":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},0:function(e,t){e.exports=Injector},1:function(e,t){e.exports=React},2:function(e,t){e.exports=FieldHolder},3:function(e,t){e.exports=IsomorphicFetch},4:function(e,t){e.exports=ReactDom},5:function(e,t){e.exports=ReactSelect}});
|
||||
!function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle.js")}({"./client/src/boot/index.js":function(e,t,n){"use strict";var o=n("./client/src/boot/registerComponents.js"),r=function(e){return e&&e.__esModule?e:{default:e}}(o);window.document.addEventListener("DOMContentLoaded",function(){(0,r.default)()})},"./client/src/boot/registerComponents.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),s=o(r),i=n("./client/src/components/TagField.js"),a=o(i);t.default=function(){s.default.component.registerMany({TagField:a.default})}},"./client/src/bundles/bundle.js":function(e,t,n){"use strict";n("./client/src/legacy/entwine/TagField.js"),n("./client/src/boot/index.js")},"./client/src/components/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n={};for(var o in e)t.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(n[o]=e[o]);return n}function s(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.Component=void 0;var l=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},h=function(){function e(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(t,n,o){return n&&e(t.prototype,n),o&&e(t,o),t}}(),c=n(1),f=o(c),p=n(5),d=o(p),m=n(3),v=o(m),y=n(2),b=o(y),g=n("./node_modules/url/url.js"),j=o(g),O=n("./node_modules/debounce-promise/dist/index.js"),w=o(O),x=n("./node_modules/prop-types/index.js"),_=o(x),C=function(e){function t(e){i(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.isControlled()||(n.state={value:e.value}),n.handleChange=n.handleChange.bind(n),n.handleOnBlur=n.handleOnBlur.bind(n),n.getOptions=n.getOptions.bind(n),n.fetchOptions=(0,w.default)(n.fetchOptions,500),n}return u(t,e),h(t,[{key:"getOptions",value:function(e){var t=this.props,n=t.lazyLoad,o=t.options;return n?e?this.fetchOptions(e):Promise.resolve({options:[]}):Promise.resolve({options:o})}},{key:"handleChange",value:function(e){if(this.isControlled())return void this.props.onChange(e);this.setState({value:e})}},{key:"isControlled",value:function(){return"function"==typeof this.props.onChange}},{key:"handleOnBlur",value:function(){}},{key:"fetchOptions",value:function(e){var t=this.props,n=t.optionUrl,o=t.labelKey,r=t.valueKey,i=j.default.parse(n,!0);return i.query.term=e,(0,v.default)(j.default.format(i),{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return{options:e.items.map(function(e){var t;return t={},s(t,o,e.Title),s(t,r,e.Value),t})}})}},{key:"render",value:function(){var e=this.props,t=e.lazyLoad,n=e.options,o=e.creatable,s=r(e,["lazyLoad","options","creatable"]),i=t?{loadOptions:this.getOptions}:{options:n},a=d.default;return t&&o?a=d.default.AsyncCreatable:t?a=d.default.Async:o&&(a=d.default.Creatable),this.isControlled()||(s.value=this.state.value),f.default.createElement(a,l({},s,{onChange:this.handleChange,onBlur:this.handleOnBlur,inputProps:{className:"no-change-track"}},i))}}]),t}(c.Component);C.propTypes={name:_.default.string.isRequired,labelKey:_.default.string.isRequired,valueKey:_.default.string.isRequired,lazyLoad:_.default.bool,creatable:_.default.bool,multi:_.default.bool,disabled:_.default.bool,options:_.default.arrayOf(_.default.object),optionUrl:_.default.string,value:_.default.any,onChange:_.default.func,onBlur:_.default.func},C.defaultProps={labelKey:"Title",valueKey:"Value",disabled:!1,lazyLoad:!1,creatable:!1,multi:!1},t.Component=C,t.default=(0,b.default)(C)},"./client/src/legacy/entwine/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s=n(1),i=o(s),a=n(4),u=o(a),l=n(0);window.jQuery.entwine("ss",function(e){e(".js-injector-boot .ss-tag-field.entwine").entwine({onmatch:function(){var e=this,t=this.closest(".cms-content").attr("id"),n=t?{context:t}:{},o=(0,l.loadComponent)("TagField",n),s=r({},this.data("schema"),{onBlur:function(){e.parents(".cms-edit-form:first").trigger("change")}});u.default.render(i.default.createElement(o,r({noHolder:!0},s)),this[0])},onunmatch:function(){u.default.unmountComponentAtNode(this[0])}})})},"./node_modules/debounce-promise/dist/index.js":function(e,t,n){"use strict";function o(e){return"function"==typeof e?e():e}function r(){var e={};return e.promise=new Promise(function(t,n){e.resolve=t,e.reject=n}),e}e.exports=function(e){function t(){var t=a;clearTimeout(u),Promise.resolve(s.accumulate?e.call(this,l):e.apply(this,l[l.length-1])).then(t.resolve,t.reject),l=[],a=null}var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=void 0,a=void 0,u=void 0,l=[];return function(){var h=o(n),c=(new Date).getTime(),f=!i||c-i>h;i=c;for(var p=arguments.length,d=Array(p),m=0;m<p;m++)d[m]=arguments[m];if(f&&s.leading)return s.accumulate?Promise.resolve(e.call(this,[d])).then(function(e){return e[0]}):Promise.resolve(e.call.apply(e,[this].concat(d)));if(a?clearTimeout(u):a=r(),l.push(d),u=setTimeout(t.bind(this),h),s.accumulate){var v=l.length-1;return a.promise.then(function(e){return e[v]})}return a.promise}}},"./node_modules/node-libs-browser/node_modules/punycode/punycode.js":function(e,t,n){(function(e,o){var r;!function(o){function s(e){throw new RangeError(I[e])}function i(e,t){for(var n=e.length,o=[];n--;)o[n]=t(e[n]);return o}function a(e,t){var n=e.split("@"),o="";return n.length>1&&(o=n[0]+"@",e=n[1]),e=e.replace(A,"."),o+i(e.split("."),t).join(".")}function u(e){for(var t,n,o=[],r=0,s=e.length;r<s;)t=e.charCodeAt(r++),t>=55296&&t<=56319&&r<s?(n=e.charCodeAt(r++),56320==(64512&n)?o.push(((1023&t)<<10)+(1023&n)+65536):(o.push(t),r--)):o.push(t);return o}function l(e){return i(e,function(e){var t="";return e>65535&&(e-=65536,t+=S(e>>>10&1023|55296),e=56320|1023&e),t+=S(e)}).join("")}function h(e){return e-48<10?e-22:e-65<26?e-65:e-97<26?e-97:g}function c(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function f(e,t,n){var o=0;for(e=n?k(e/x):e>>1,e+=k(e/t);e>R*O>>1;o+=g)e=k(e/R);return k(o+(R+1)*e/(e+w))}function p(e){var t,n,o,r,i,a,u,c,p,d,m=[],v=e.length,y=0,w=C,x=_;for(n=e.lastIndexOf(P),n<0&&(n=0),o=0;o<n;++o)e.charCodeAt(o)>=128&&s("not-basic"),m.push(e.charCodeAt(o));for(r=n>0?n+1:0;r<v;){for(i=y,a=1,u=g;r>=v&&s("invalid-input"),c=h(e.charCodeAt(r++)),(c>=g||c>k((b-y)/a))&&s("overflow"),y+=c*a,p=u<=x?j:u>=x+O?O:u-x,!(c<p);u+=g)d=g-p,a>k(b/d)&&s("overflow"),a*=d;t=m.length+1,x=f(y-i,t,0==i),k(y/t)>b-w&&s("overflow"),w+=k(y/t),y%=t,m.splice(y++,0,w)}return l(m)}function d(e){var t,n,o,r,i,a,l,h,p,d,m,v,y,w,x,T=[];for(e=u(e),v=e.length,t=C,n=0,i=_,a=0;a<v;++a)(m=e[a])<128&&T.push(S(m));for(o=r=T.length,r&&T.push(P);o<v;){for(l=b,a=0;a<v;++a)(m=e[a])>=t&&m<l&&(l=m);for(y=o+1,l-t>k((b-n)/y)&&s("overflow"),n+=(l-t)*y,t=l,a=0;a<v;++a)if(m=e[a],m<t&&++n>b&&s("overflow"),m==t){for(h=n,p=g;d=p<=i?j:p>=i+O?O:p-i,!(h<d);p+=g)x=h-d,w=g-d,T.push(S(c(d+x%w,0))),h=k(x/w);T.push(S(c(h,0))),i=f(n,y,o==r),n=0,++o}++n,++t}return T.join("")}function m(e){return a(e,function(e){return T.test(e)?p(e.slice(4).toLowerCase()):e})}function v(e){return a(e,function(e){return q.test(e)?"xn--"+d(e):e})}var y,b=("object"==typeof t&&t&&t.nodeType,"object"==typeof e&&e&&e.nodeType,2147483647),g=36,j=1,O=26,w=38,x=700,_=72,C=128,P="-",T=/^xn--/,q=/[^\x20-\x7E]/,A=/[\x2E\u3002\uFF0E\uFF61]/g,I={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},R=g-j,k=Math.floor,S=String.fromCharCode;y={version:"1.4.1",ucs2:{decode:u,encode:l},decode:p,encode:d,toASCII:v,toUnicode:m},void 0!==(r=function(){return y}.call(t,n,t,e))&&(e.exports=r)}()}).call(t,n("./node_modules/webpack/buildin/module.js")(e),n("./node_modules/webpack/buildin/global.js"))},"./node_modules/prop-types/factoryWithThrowingShims.js":function(e,t,n){"use strict";function o(){}function r(){}var s=n("./node_modules/prop-types/lib/ReactPropTypesSecret.js");r.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,r,i){if(i!==s){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:r,resetWarningCache:o};return n.PropTypes=n,n}},"./node_modules/prop-types/index.js":function(e,t,n){e.exports=n("./node_modules/prop-types/factoryWithThrowingShims.js")()},"./node_modules/prop-types/lib/ReactPropTypesSecret.js":function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},"./node_modules/querystring-es3/decode.js":function(e,t,n){"use strict";function o(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,s){t=t||"&",n=n||"=";var i={};if("string"!=typeof e||0===e.length)return i;var a=/\+/g;e=e.split(t);var u=1e3;s&&"number"==typeof s.maxKeys&&(u=s.maxKeys);var l=e.length;u>0&&l>u&&(l=u);for(var h=0;h<l;++h){var c,f,p,d,m=e[h].replace(a,"%20"),v=m.indexOf(n);v>=0?(c=m.substr(0,v),f=m.substr(v+1)):(c=m,f=""),p=decodeURIComponent(c),d=decodeURIComponent(f),o(i,p)?r(i[p])?i[p].push(d):i[p]=[i[p],d]:i[p]=d}return i};var r=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},"./node_modules/querystring-es3/encode.js":function(e,t,n){"use strict";function o(e,t){if(e.map)return e.map(t);for(var n=[],o=0;o<e.length;o++)n.push(t(e[o],o));return n}var r=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,a){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?o(i(e),function(i){var a=encodeURIComponent(r(i))+n;return s(e[i])?o(e[i],function(e){return a+encodeURIComponent(r(e))}).join(t):a+encodeURIComponent(r(e[i]))}).join(t):a?encodeURIComponent(r(a))+n+encodeURIComponent(r(e)):""};var s=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},"./node_modules/querystring-es3/index.js":function(e,t,n){"use strict";t.decode=t.parse=n("./node_modules/querystring-es3/decode.js"),t.encode=t.stringify=n("./node_modules/querystring-es3/encode.js")},"./node_modules/url/url.js":function(e,t,n){"use strict";function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function r(e,t,n){if(e&&l.isObject(e)&&e instanceof o)return e;var r=new o;return r.parse(e,t,n),r}function s(e){return l.isString(e)&&(e=r(e)),e instanceof o?e.format():o.prototype.format.call(e)}function i(e,t){return r(e,!1,!0).resolve(t)}function a(e,t){return e?r(e,!1,!0).resolveObject(t):t}var u=n("./node_modules/node-libs-browser/node_modules/punycode/punycode.js"),l=n("./node_modules/url/util.js");t.parse=r,t.resolve=i,t.resolveObject=a,t.format=s,t.Url=o;var h=/^([a-z0-9.+-]+:)/i,c=/:[0-9]*$/,f=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,p=["<",">",'"',"`"," ","\r","\n","\t"],d=["{","}","|","\\","^","`"].concat(p),m=["'"].concat(d),v=["%","/","?",";","#"].concat(m),y=["/","?","#"],b=/^[+a-z0-9A-Z_-]{0,63}$/,g=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,j={javascript:!0,"javascript:":!0},O={javascript:!0,"javascript:":!0},w={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},x=n("./node_modules/querystring-es3/index.js");o.prototype.parse=function(e,t,n){if(!l.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),r=-1!==o&&o<e.indexOf("#")?"?":"#",s=e.split(r),i=/\\/g;s[0]=s[0].replace(i,"/"),e=s.join(r);var a=e;if(a=a.trim(),!n&&1===e.split("#").length){var c=f.exec(a);if(c)return this.path=a,this.href=a,this.pathname=c[1],c[2]?(this.search=c[2],this.query=t?x.parse(this.search.substr(1)):this.search.substr(1)):t&&(this.search="",this.query={}),this}var p=h.exec(a);if(p){p=p[0];var d=p.toLowerCase();this.protocol=d,a=a.substr(p.length)}if(n||p||a.match(/^\/\/[^@\/]+@[^@\/]+/)){var _="//"===a.substr(0,2);!_||p&&O[p]||(a=a.substr(2),this.slashes=!0)}if(!O[p]&&(_||p&&!w[p])){for(var C=-1,P=0;P<y.length;P++){var T=a.indexOf(y[P]);-1!==T&&(-1===C||T<C)&&(C=T)}var q,A;A=-1===C?a.lastIndexOf("@"):a.lastIndexOf("@",C),-1!==A&&(q=a.slice(0,A),a=a.slice(A+1),this.auth=decodeURIComponent(q)),C=-1;for(var P=0;P<v.length;P++){var T=a.indexOf(v[P]);-1!==T&&(-1===C||T<C)&&(C=T)}-1===C&&(C=a.length),this.host=a.slice(0,C),a=a.slice(C),this.parseHost(),this.hostname=this.hostname||"";var I="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!I)for(var R=this.hostname.split(/\./),P=0,k=R.length;P<k;P++){var S=R[P];if(S&&!S.match(b)){for(var U="",E=0,F=S.length;E<F;E++)S.charCodeAt(E)>127?U+="x":U+=S[E];if(!U.match(b)){var L=R.slice(0,P),M=R.slice(P+1),N=S.match(g);N&&(L.push(N[1]),M.unshift(N[2])),M.length&&(a="/"+M.join(".")+a),this.hostname=L.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),I||(this.hostname=u.toASCII(this.hostname));var z=this.port?":"+this.port:"",B=this.hostname||"";this.host=B+z,this.href+=this.host,I&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==a[0]&&(a="/"+a))}if(!j[d])for(var P=0,k=m.length;P<k;P++){var K=m[P];if(-1!==a.indexOf(K)){var D=encodeURIComponent(K);D===K&&(D=escape(K)),a=a.split(K).join(D)}}var H=a.indexOf("#");-1!==H&&(this.hash=a.substr(H),a=a.slice(0,H));var W=a.indexOf("?");if(-1!==W?(this.search=a.substr(W),this.query=a.substr(W+1),t&&(this.query=x.parse(this.query)),a=a.slice(0,W)):t&&(this.search="",this.query={}),a&&(this.pathname=a),w[d]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var z=this.pathname||"",$=this.search||"";this.path=z+$}return this.href=this.format(),this},o.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",o=this.hash||"",r=!1,s="";this.host?r=e+this.host:this.hostname&&(r=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(r+=":"+this.port)),this.query&&l.isObject(this.query)&&Object.keys(this.query).length&&(s=x.stringify(this.query));var i=this.search||s&&"?"+s||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||w[t])&&!1!==r?(r="//"+(r||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):r||(r=""),o&&"#"!==o.charAt(0)&&(o="#"+o),i&&"?"!==i.charAt(0)&&(i="?"+i),n=n.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),i=i.replace("#","%23"),t+r+n+i+o},o.prototype.resolve=function(e){return this.resolveObject(r(e,!1,!0)).format()},o.prototype.resolveObject=function(e){if(l.isString(e)){var t=new o;t.parse(e,!1,!0),e=t}for(var n=new o,r=Object.keys(this),s=0;s<r.length;s++){var i=r[s];n[i]=this[i]}if(n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol){for(var a=Object.keys(e),u=0;u<a.length;u++){var h=a[u];"protocol"!==h&&(n[h]=e[h])}return w[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n}if(e.protocol&&e.protocol!==n.protocol){if(!w[e.protocol]){for(var c=Object.keys(e),f=0;f<c.length;f++){var p=c[f];n[p]=e[p]}return n.href=n.format(),n}if(n.protocol=e.protocol,e.host||O[e.protocol])n.pathname=e.pathname;else{for(var d=(e.pathname||"").split("/");d.length&&!(e.host=d.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==d[0]&&d.unshift(""),d.length<2&&d.unshift(""),n.pathname=d.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var m=n.pathname||"",v=n.search||"";n.path=m+v}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var y=n.pathname&&"/"===n.pathname.charAt(0),b=e.host||e.pathname&&"/"===e.pathname.charAt(0),g=b||y||n.host&&e.pathname,j=g,x=n.pathname&&n.pathname.split("/")||[],d=e.pathname&&e.pathname.split("/")||[],_=n.protocol&&!w[n.protocol];if(_&&(n.hostname="",n.port=null,n.host&&(""===x[0]?x[0]=n.host:x.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===d[0]?d[0]=e.host:d.unshift(e.host)),e.host=null),g=g&&(""===d[0]||""===x[0])),b)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,x=d;else if(d.length)x||(x=[]),x.pop(),x=x.concat(d),n.search=e.search,n.query=e.query;else if(!l.isNullOrUndefined(e.search)){if(_){n.hostname=n.host=x.shift();var C=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");C&&(n.auth=C.shift(),n.host=n.hostname=C.shift())}return n.search=e.search,n.query=e.query,l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!x.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var P=x.slice(-1)[0],T=(n.host||e.host||x.length>1)&&("."===P||".."===P)||""===P,q=0,A=x.length;A>=0;A--)P=x[A],"."===P?x.splice(A,1):".."===P?(x.splice(A,1),q++):q&&(x.splice(A,1),q--);if(!g&&!j)for(;q--;q)x.unshift("..");!g||""===x[0]||x[0]&&"/"===x[0].charAt(0)||x.unshift(""),T&&"/"!==x.join("/").substr(-1)&&x.push("");var I=""===x[0]||x[0]&&"/"===x[0].charAt(0);if(_){n.hostname=n.host=I?"":x.length?x.shift():"";var C=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");C&&(n.auth=C.shift(),n.host=n.hostname=C.shift())}return g=g||n.host&&x.length,g&&!I&&x.unshift(""),x.length?n.pathname=x.join("/"):(n.pathname=null,n.path=null),l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},o.prototype.parseHost=function(){var e=this.host,t=c.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},"./node_modules/url/util.js":function(e,t,n){"use strict";e.exports={isString:function(e){return"string"==typeof e},isObject:function(e){return"object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},"./node_modules/webpack/buildin/global.js":function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},"./node_modules/webpack/buildin/module.js":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},0:function(e,t){e.exports=Injector},1:function(e,t){e.exports=React},2:function(e,t){e.exports=FieldHolder},3:function(e,t){e.exports=IsomorphicFetch},4:function(e,t){e.exports=ReactDom},5:function(e,t){e.exports=ReactSelect}});
|
2
client/dist/styles/bundle.css
vendored
2
client/dist/styles/bundle.css
vendored
|
@ -1 +1 @@
|
|||
.ss-tag-field .Select--multi .Select-value{margin-top:3px}
|
||||
@-o-keyframes Select-animation-spin{to{-o-transform:rotate(1turn);transform:rotate(1turn)}}.Select{position:relative}.Select input::-webkit-contacts-auto-fill-button,.Select input::-webkit-credentials-auto-fill-button{display:none!important}.Select input::-ms-clear,.Select input::-ms-reveal{display:none!important}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{-webkit-box-shadow:none;box-shadow:none}.Select.is-searchable.is-focused:not(.is-open)>.Select-control,.Select.is-searchable.is-open>.Select-control{cursor:text}.Select.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#b3b3b3 #ccc #d9d9d9}.Select.is-open>.Select-control .Select-arrow{top:-2px;border-color:transparent transparent #999;border-width:0 5px 5px}.Select.is-focused>.Select-control{background:#fff}.Select.is-focused:not(.is-open)>.Select-control{border-color:#08c #0099e6 #0099e6;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1),0 0 5px -1px fade(#08c,50%);box-shadow:inset 0 1px 2px rgba(0,0,0,.1),0 0 5px -1px fade(#08c,50%)}.Select.has-value.is-clearable.Select--single>.Select-control .Select-value{padding-right:42px}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label,.Select.has-value.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover{color:#08c;outline:none;text-decoration:underline}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus{background:#fff}.Select.has-value.is-pseudo-focused .Select-input{opacity:0}.Select.is-open .Select-arrow,.Select .Select-arrow-zone:hover>.Select-arrow{border-top-color:#666}.Select.Select--rtl{direction:rtl;text-align:right}.Select-control{background-color:#fff;border-color:#d9d9d9 #ccc #b3b3b3;border-radius:4px;border:1px solid #ccc;color:#333;cursor:default;display:table;border-spacing:0;border-collapse:separate;height:36px;outline:none;overflow:hidden;position:relative;width:100%}.Select-control:hover{-webkit-box-shadow:0 1px 0 rgba(0,0,0,.06);box-shadow:0 1px 0 rgba(0,0,0,.06)}.Select-control .Select-input:focus{outline:none;background:#fff}.Select--single>.Select-control .Select-value,.Select-placeholder{bottom:0;color:#aaa;left:0;line-height:34px;padding-left:10px;padding-right:10px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.Select-input{height:34px;padding-left:10px;padding-right:10px;vertical-align:middle}.Select-input>input{width:100%;background:none transparent;border:0 none;-webkit-box-shadow:none;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:none;line-height:17px;padding:8px 0 12px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.Select-control:not(.is-searchable)>.Select-input{outline:none}.Select-loading-zone{cursor:pointer;display:table-cell;text-align:center}.Select-loading,.Select-loading-zone{position:relative;vertical-align:middle;width:16px}.Select-loading{-webkit-animation:Select-animation-spin .4s infinite linear;-o-animation:Select-animation-spin .4s infinite linear;animation:Select-animation-spin .4s infinite linear;height:16px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:50%;border:2px solid #ccc;border-right-color:#333;display:inline-block}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn .2s;-o-animation:Select-animation-fadeIn .2s;animation:Select-animation-fadeIn .2s;color:#999;cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:17px}.Select-clear-zone:hover{color:#d0021b}.Select-clear{display:inline-block;font-size:18px;line-height:1}.Select--multi .Select-clear-zone{width:17px}.Select--multi .Select-multi-value-wrapper{display:inline-block}.Select .Select-aria-only{position:absolute;display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0,0,0,0);overflow:hidden;float:left}.Select-arrow-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:25px;padding-right:5px}.Select--rtl .Select-arrow-zone{padding-right:0;padding-left:5px}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;display:inline-block;height:0;width:0;position:relative}@-webkit-keyframes Select-animation-fadeIn{0%{opacity:0}to{opacity:1}}@-o-keyframes Select-animation-fadeIn{0%{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{0%{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#fff;border:1px solid #ccc;border-top-color:#e6e6e6;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.06);box-shadow:0 1px 0 rgba(0,0,0,.06);-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;left:0;top:100%;width:100%;z-index:1000;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option{-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#fff;color:#666;cursor:pointer;display:block;padding:8px 10px}.Select-option:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.Select-option.is-selected{background-color:#f5faff;color:#333}.Select-option.is-focused{background-color:#f2f9fc;color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{-webkit-box-sizing:border-box;box-sizing:border-box;color:#999;cursor:default;display:block;padding:8px 10px}.Select--multi .Select-input{vertical-align:middle;margin-left:10px;padding:0}.Select--multi.Select--rtl .Select-input{margin-left:0;margin-right:10px}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#f2f9fc;border-radius:2px;border:1px solid #c9e6f2;color:#08c;display:inline-block;font-size:.9em;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:2px;border-top-right-radius:2px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#08c;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:2px;border-top-left-radius:2px;border-right:1px solid #c9e6f2;padding:1px 5px 3px}.Select--multi .Select-value-icon:focus,.Select--multi .Select-value-icon:hover{background-color:#ddeff7;color:#0077b3}.Select--multi .Select-value-icon:active{background-color:#c9e6f2}.Select--multi.Select--rtl .Select-value{margin-left:0;margin-right:5px}.Select--multi.Select--rtl .Select-value-icon{border-right:none;border-left:1px solid #c9e6f2}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:active,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:hover{background-color:#fcfcfc}@keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn);-o-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}.ss-tag-field .Select--multi .Select-value{margin-top:3px}
|
|
@ -1,2 +1,2 @@
|
|||
require('legacy/entwine/TagField');
|
||||
require('legacy/entwine/TagField.js');
|
||||
require('boot');
|
||||
|
|
|
@ -10,20 +10,8 @@ class TagField extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.selectComponentRef = null;
|
||||
|
||||
this.setSelectComponentRef = ref => {
|
||||
this.selectComponentRef = ref;
|
||||
};
|
||||
|
||||
this.state = {
|
||||
initalState: props.value ? props.value : [],
|
||||
hasChanges: false,
|
||||
};
|
||||
|
||||
if (!this.isControlled()) {
|
||||
this.state = {
|
||||
...this.state,
|
||||
value: props.value,
|
||||
};
|
||||
}
|
||||
|
@ -34,14 +22,6 @@ class TagField extends Component {
|
|||
this.fetchOptions = debounce(this.fetchOptions, 500);
|
||||
}
|
||||
|
||||
componentDidUpdate(previousProps, previousState) {
|
||||
if (previousState.hasChanges !== this.state.hasChanges) {
|
||||
const element = this.selectComponentRef.select.wrapper;
|
||||
const event = new Event('change', { bubbles: true });
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options that should be shown to the user for this tagfield, optionally filtering by the
|
||||
* given string input
|
||||
|
@ -70,23 +50,13 @@ class TagField extends Component {
|
|||
* @param {string} value
|
||||
*/
|
||||
handleChange(value) {
|
||||
this.setState({
|
||||
hasChanges: false
|
||||
});
|
||||
|
||||
if (JSON.stringify(this.state.initalState) !== JSON.stringify(value)) {
|
||||
this.setState({
|
||||
hasChanges: true
|
||||
});
|
||||
}
|
||||
|
||||
if (this.isControlled()) {
|
||||
this.props.onChange(value);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value,
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -106,7 +76,9 @@ class TagField extends Component {
|
|||
*
|
||||
* @link https://github.com/JedWatson/react-select/issues/805
|
||||
*/
|
||||
handleOnBlur() {}
|
||||
handleOnBlur() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a request to fetch options, optionally using the given string as a filter.
|
||||
|
@ -122,17 +94,20 @@ class TagField extends Component {
|
|||
return fetch(url.format(fetchURL), { credentials: 'same-origin' })
|
||||
.then((response) => response.json())
|
||||
.then((json) => ({
|
||||
options: json.items.map((item) => ({
|
||||
options: json.items.map(item => ({
|
||||
[labelKey]: item.Title,
|
||||
[valueKey]: item.Value,
|
||||
Selected: item.Selected,
|
||||
})),
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { lazyLoad, options, creatable, ...passThroughAttributes } =
|
||||
this.props;
|
||||
const {
|
||||
lazyLoad,
|
||||
options,
|
||||
creatable,
|
||||
...passThroughAttributes
|
||||
} = this.props;
|
||||
|
||||
const optionAttributes = lazyLoad
|
||||
? { loadOptions: this.getOptions }
|
||||
|
@ -153,30 +128,13 @@ class TagField extends Component {
|
|||
passThroughAttributes.value = this.state.value;
|
||||
}
|
||||
|
||||
// if this is a single select then we just need the first value
|
||||
if (!passThroughAttributes.multi && passThroughAttributes.value) {
|
||||
if (Object.keys(passThroughAttributes.value).length > 0) {
|
||||
const value =
|
||||
passThroughAttributes.value[
|
||||
Object.keys(passThroughAttributes.value)[0]
|
||||
];
|
||||
|
||||
if (typeof value === 'object') {
|
||||
passThroughAttributes.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const changedClassName = this.state.hasChanges ? '' : 'no-change-track';
|
||||
|
||||
return (
|
||||
<SelectComponent
|
||||
{...passThroughAttributes}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleOnBlur}
|
||||
inputProps={{ className: 'no-change-track' }}
|
||||
{...optionAttributes}
|
||||
className={changedClassName}
|
||||
ref={this.setSelectComponentRef}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@import "~react-select/scss/default";
|
||||
|
||||
.ss-tag-field .Select--multi .Select-value {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
|
|
@ -31,26 +31,4 @@ window.jQuery.entwine('ss', ($) => {
|
|||
ReactDOM.unmountComponentAtNode(this[0]);
|
||||
}
|
||||
});
|
||||
|
||||
$('.cms-edit-form').entwine({
|
||||
getChangeTrackerOptions() {
|
||||
// Figure out if we're still returning the default value
|
||||
const isDefault = (this.entwineData('ChangeTrackerOptions') === undefined);
|
||||
// Get the current options
|
||||
let opts = this._super();
|
||||
|
||||
if (isDefault) {
|
||||
// If it is the default then...
|
||||
// clone the object (so we don't modify the original),
|
||||
opts = $.extend({}, opts);
|
||||
// modify it,
|
||||
opts.ignoreFieldSelector += ', .ss-tag-field .no-change-track :input';
|
||||
// then set the clone as the value on this element
|
||||
// (so next call to this method gets this same clone)
|
||||
this.setChangeTrackerOptions(opts);
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,12 +19,11 @@
|
|||
"issues": "http://github.com/silverstripe/silverstripe-tagfield/issues"
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0",
|
||||
"silverstripe/framework": "^4.10",
|
||||
"silverstripe/framework": "^4.3",
|
||||
"silverstripe/versioned": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"squizlabs/php_codesniffer": "^3.0"
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -40,4 +39,4 @@
|
|||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,17 +68,3 @@ class BlogPost extends DataObject
|
|||
In the above code example, the options available (whether lazy loaded or not) would be "one" and "two", and the
|
||||
user would be able to create new options. Whichever tags are chosen would be stored in the BlogPost's `Tags` field
|
||||
as a comma-delimited string.
|
||||
|
||||
## Using TagField with silverstripe-taxonomy
|
||||
|
||||
TagField assumes a `Title` field on objects. For classes without a `Title` field
|
||||
use `setTitleField` to modify accordingly.
|
||||
|
||||
```php
|
||||
$field = TagField::create(
|
||||
'Tags',
|
||||
'Blog Tags',
|
||||
TaxonomyTerm::get(),
|
||||
)
|
||||
->setTitleField('Name');
|
||||
```
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@silverstripe/eslint-config": "^0.0.6",
|
||||
"@silverstripe/eslint-config": "^0.0.5",
|
||||
"@silverstripe/webpack-config": "^0.10.0",
|
||||
"@storybook/react": "^3.2.19",
|
||||
"babel-jest": "^23.6.0",
|
||||
|
@ -74,7 +74,7 @@
|
|||
"classnames": "^2.2.5",
|
||||
"debounce-promise": "^3.1.0",
|
||||
"isomorphic-fetch": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
|
||||
"jquery": "^3.5.0",
|
||||
"jquery": "^3.1.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "15.3.1",
|
||||
"react-dom": "15.3.1",
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
<ruleset name="SilverStripe">
|
||||
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
|
||||
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
|
||||
<!-- base rules are PSR-2 -->
|
||||
<rule ref="PSR2" >
|
||||
<!-- Current exclusions -->
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Default">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true">
|
||||
<testsuite name="Default">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/</directory>
|
||||
|
|
109
readme.md
Normal file
109
readme.md
Normal file
|
@ -0,0 +1,109 @@
|
|||
# Tag Field
|
||||
|
||||
Custom tag input field, for SilverStripe.
|
||||
|
||||
[![Build Status](https://travis-ci.org/silverstripe/silverstripe-tagfield.svg?branch=master)](https://travis-ci.org/silverstripe/silverstripe-tagfield)
|
||||
[![Code Quality](http://img.shields.io/scrutinizer/g/silverstripe/silverstripe-tagfield.svg?style=flat)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-tagfield)
|
||||
[![Code coverage](https://codecov.io/gh/silverstripe/silverstripe-tagfield/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-tagfield)
|
||||
[![Version](http://img.shields.io/packagist/v/silverstripe/tagfield.svg?style=flat)](https://packagist.org/packages/silverstripe/tagfield)
|
||||
[![License](http://img.shields.io/packagist/l/silverstripe/tagfield.svg?style=flat)](license.md)
|
||||
[![SilverStripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
|
||||
|
||||
## Overview
|
||||
|
||||
![Screenshot](docs/en/screenshot.png)
|
||||
|
||||
Allows storing tags as a relationship, or comma-delimited strings.
|
||||
Supports autocompletion with lazy-loading.
|
||||
|
||||
Note: The field is optimised for usage in the SilverStripe CMS UI.
|
||||
The form field class itself can be used outside of the CMS,
|
||||
but you'll need to build your own frontend to interpret the raw field data (`data-schema` attribute).
|
||||
|
||||
## Requirements
|
||||
|
||||
* SilverStripe 4.0
|
||||
|
||||
## Installing
|
||||
|
||||
```sh
|
||||
$ composer require silverstripe/tagfield
|
||||
```
|
||||
|
||||
## Using
|
||||
|
||||
### Relational Tags
|
||||
|
||||
```php
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class BlogPost extends DataObject
|
||||
{
|
||||
private static $many_many = [
|
||||
'BlogTags' => BlogTag::class
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class BlogTag extends DataObject
|
||||
{
|
||||
private static $db = [
|
||||
'Title' => 'Varchar(200)',
|
||||
];
|
||||
|
||||
private static $belongs_many_many = [
|
||||
'BlogPosts' => BlogPost::class
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
$field = TagField::create(
|
||||
'BlogTags',
|
||||
'Blog Tags',
|
||||
BlogTag::get(),
|
||||
$this->BlogTags()
|
||||
)
|
||||
->setShouldLazyLoad(true) // tags should be lazy loaded
|
||||
->setCanCreate(true); // new tag DataObjects can be created
|
||||
```
|
||||
**Note:** This assumes you have imported the namespaces class, e.g. use SilverStripe\TagField\TagField;
|
||||
|
||||
### String Tags
|
||||
|
||||
```php
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
class BlogPost extends DataObject
|
||||
{
|
||||
private static $db = [
|
||||
'Tags' => 'Text',
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
$field = StringTagField::create(
|
||||
'Tags',
|
||||
'Tags',
|
||||
['one', 'two'],
|
||||
explode(',', $this->Tags)
|
||||
);
|
||||
|
||||
$field->setShouldLazyLoad(true); // tags should be lazy loaded
|
||||
```
|
||||
|
||||
You can find more in-depth documentation in [docs/en](docs/en/introduction.md).
|
||||
|
||||
## Versioning
|
||||
|
||||
This library follows [Semver](http://semver.org). According to Semver, you will be able to upgrade to any minor or patch version of this library without any breaking changes to the public API. Semver also requires that we clearly define the public API for this library.
|
||||
|
||||
All methods, with `public` visibility, are part of the public API. All other methods are not part of the public API. Where possible, we'll try to keep `protected` methods backwards-compatible in minor/patch versions, but if you're overriding methods then please test your work before upgrading.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Please [create an issue](http://github.com/silverstripe/silverstripe-tagfield/issues) for any bugs you've found, or features you're missing.
|
|
@ -6,7 +6,6 @@ use Iterator;
|
|||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\Validator;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
|
@ -34,13 +33,6 @@ class StringTagField extends DropdownField
|
|||
'suggest',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var bool Triggers a write call within the saveInto function if enabled
|
||||
*
|
||||
* @deprecated 3.0.0 Will be removed without equivalent functionality to replace it
|
||||
*/
|
||||
private static $immediate_write_enabled = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
@ -66,9 +58,6 @@ class StringTagField extends DropdownField
|
|||
*/
|
||||
protected $isMultiple = true;
|
||||
|
||||
/** @skipUpgrade */
|
||||
protected $schemaComponent = 'TagField';
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -155,7 +144,7 @@ class StringTagField extends DropdownField
|
|||
|
||||
public function Field($properties = [])
|
||||
{
|
||||
$this->addExtraClass('ss-tag-field entwine');
|
||||
$this->addExtraClass('ss-tag-field');
|
||||
|
||||
return $this
|
||||
->customise($properties)
|
||||
|
@ -255,12 +244,12 @@ class StringTagField extends DropdownField
|
|||
public function setValue($value, $source = null)
|
||||
{
|
||||
if (is_string($value)) {
|
||||
$value = explode(',', $value ?? '');
|
||||
$value = explode(',', $value);
|
||||
}
|
||||
|
||||
if ($source instanceof DataObject) {
|
||||
$name = $this->getName();
|
||||
$value = explode(',', $source->$name ?? '');
|
||||
$value = explode(',', $source->$name);
|
||||
}
|
||||
|
||||
if ($source instanceof SS_List) {
|
||||
|
@ -271,7 +260,7 @@ class StringTagField extends DropdownField
|
|||
$value = [];
|
||||
}
|
||||
|
||||
return parent::setValue(array_filter($value ?? []));
|
||||
return parent::setValue(array_filter($value));
|
||||
}
|
||||
|
||||
public function saveInto(DataObjectInterface $record)
|
||||
|
@ -281,13 +270,7 @@ class StringTagField extends DropdownField
|
|||
$name = $this->getName();
|
||||
|
||||
$record->$name = $this->dataValue();
|
||||
|
||||
$immediate_write_enabled = Deprecation::withNoReplacement(function () {
|
||||
return self::config()->get('immediate_write_enabled');
|
||||
});
|
||||
if ($immediate_write_enabled) {
|
||||
$record->write();
|
||||
}
|
||||
$record->write();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -319,49 +302,6 @@ class StringTagField extends DropdownField
|
|||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create tag with the given value
|
||||
*
|
||||
* @param string $term
|
||||
* @return DataObject|bool
|
||||
*/
|
||||
protected function getOrCreateTag($term)
|
||||
{
|
||||
// Check if existing record can be found
|
||||
$source = $this->getSourceList();
|
||||
if (!$source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$titleField = $this->getTitleField();
|
||||
$record = $source
|
||||
->filter($titleField, $term)
|
||||
->first();
|
||||
if ($record) {
|
||||
return $record;
|
||||
}
|
||||
|
||||
// Create new instance if not yet saved
|
||||
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) {
|
||||
$source->add($record);
|
||||
}
|
||||
return $record;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns array of arrays representing tags that partially match the given search term
|
||||
*
|
||||
|
@ -375,7 +315,7 @@ class StringTagField extends DropdownField
|
|||
/** @var ArrayData $tag */
|
||||
$tagValue = $tag->Value;
|
||||
// Map into a distinct list (prevent duplicates)
|
||||
if (stripos($tagValue ?? '', $term ?? '') !== false && !array_key_exists($tagValue, $items ?? [])) {
|
||||
if (stripos($tagValue, $term) !== false && !array_key_exists($tagValue, $items)) {
|
||||
$items[$tagValue] = [
|
||||
'id' => $tag->Title,
|
||||
'text' => $tag->Value,
|
||||
|
@ -383,7 +323,7 @@ class StringTagField extends DropdownField
|
|||
}
|
||||
}
|
||||
// @todo do we actually need lazy loading limits for StringTagField?
|
||||
return array_slice(array_values($items ?? []), 0, $this->getLazyLoadItemLimit());
|
||||
return array_slice(array_values($items), 0, $this->getLazyLoadItemLimit());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -416,11 +356,4 @@ class StringTagField extends DropdownField
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function performReadonlyTransformation()
|
||||
{
|
||||
$field = parent::performReadonlyTransformation();
|
||||
$field->setValue(implode(', ', $this->Value()));
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
|
|
238
src/TagField.php
238
src/TagField.php
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace SilverStripe\TagField;
|
||||
|
||||
use Exception;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Forms\MultiSelectField;
|
||||
use SilverStripe\Forms\Validator;
|
||||
|
@ -13,9 +13,6 @@ use SilverStripe\ORM\ArrayList;
|
|||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DataObjectInterface;
|
||||
use SilverStripe\ORM\FieldType\DBMultiEnum;
|
||||
use SilverStripe\ORM\Relation;
|
||||
use SilverStripe\ORM\SS_List;
|
||||
use SilverStripe\View\ArrayData;
|
||||
|
||||
/**
|
||||
|
@ -69,12 +66,13 @@ class TagField extends MultiSelectField
|
|||
/**
|
||||
* @param string $name
|
||||
* @param string $title
|
||||
* @param null|DataList|array $source
|
||||
* @param null|DataList $source
|
||||
* @param null|DataList $value
|
||||
* @param string $titleField
|
||||
*/
|
||||
public function __construct($name, $title = '', $source = [], $value = null, $titleField = 'Title')
|
||||
{
|
||||
$this->setSourceList($source);
|
||||
$this->setTitleField($titleField);
|
||||
parent::__construct($name, $title, $source, $value);
|
||||
|
||||
|
@ -182,9 +180,7 @@ class TagField extends MultiSelectField
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the DataList source. The 4.x upgrade for SelectField::setSource starts to convert this to an array.
|
||||
* If empty use getSource() for array version
|
||||
*
|
||||
* Get the DataList source. The 4.x upgrade for SelectField::setSource starts to convert this to an array
|
||||
* @return DataList
|
||||
*/
|
||||
public function getSourceList()
|
||||
|
@ -194,8 +190,7 @@ class TagField extends MultiSelectField
|
|||
|
||||
/**
|
||||
* Set the model class name for tags
|
||||
*
|
||||
* @param DataList $sourceList
|
||||
* @param DataList $className
|
||||
* @return self
|
||||
*/
|
||||
public function setSourceList($sourceList)
|
||||
|
@ -222,16 +217,10 @@ class TagField extends MultiSelectField
|
|||
public function getSchemaDataDefaults()
|
||||
{
|
||||
$options = $this->getOptions(true);
|
||||
$name = $this->getName();
|
||||
|
||||
if ($this->getIsMultiple() && strpos($name, '[') === false) {
|
||||
$name .= '[]';
|
||||
}
|
||||
|
||||
$schema = array_merge(
|
||||
parent::getSchemaDataDefaults(),
|
||||
[
|
||||
'name' => $name,
|
||||
'name' => $this->getName() . '[]',
|
||||
'lazyLoad' => $this->getShouldLazyLoad(),
|
||||
'creatable' => $this->getCanCreate(),
|
||||
'multi' => $this->getIsMultiple(),
|
||||
|
@ -241,7 +230,7 @@ class TagField extends MultiSelectField
|
|||
);
|
||||
|
||||
if (!$this->getShouldLazyLoad()) {
|
||||
$schema['options'] = array_values($this->getOptions()->toNestedArray() ?? []);
|
||||
$schema['options'] = array_values($this->getOptions()->toNestedArray());
|
||||
} else {
|
||||
$schema['optionUrl'] = $this->getSuggestURL();
|
||||
}
|
||||
|
@ -272,49 +261,29 @@ class TagField extends MultiSelectField
|
|||
|
||||
$dataClass = $source->dataClass();
|
||||
|
||||
$values = $this->getValueArray();
|
||||
$values = $this->Value();
|
||||
|
||||
// If we have no values and we only want selected options we can bail here
|
||||
if (empty($values) && $onlySelected) {
|
||||
return ArrayList::create();
|
||||
}
|
||||
|
||||
$titleField = $this->getTitleField();
|
||||
|
||||
// Convert an array of values into a datalist of options
|
||||
if (!$values instanceof SS_List) {
|
||||
if (is_array($values) && !empty($values)) {
|
||||
// if values is an array of Ids then we should look up via
|
||||
// ID.
|
||||
if (array_filter($values, 'is_int')) {
|
||||
$queryField = 'ID';
|
||||
} else {
|
||||
$queryField = $titleField;
|
||||
}
|
||||
|
||||
if (is_a($source, DataList::class)) {
|
||||
$values = $source->filterAny([
|
||||
$queryField => $values
|
||||
]);
|
||||
} else {
|
||||
$values = DataList::create($dataClass)
|
||||
->filterAny([
|
||||
$queryField => $values
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$values = ArrayList::create();
|
||||
}
|
||||
if (is_array($values) && !empty($values)) {
|
||||
$values = DataList::create($dataClass)
|
||||
->filter($this->getTitleField(), $values);
|
||||
} else {
|
||||
$values = ArrayList::create();
|
||||
}
|
||||
|
||||
// Prep a function to parse a dataobject into an option
|
||||
$addOption = function (DataObject $item) use ($options, $values, $titleField) {
|
||||
$addOption = function (DataObject $item) use ($options, $values) {
|
||||
$titleField = $this->getTitleField();
|
||||
$option = $item->$titleField;
|
||||
|
||||
$options->push(ArrayData::create([
|
||||
'Title' => $option,
|
||||
'Value' => $option,
|
||||
'Selected' => (bool) $values->find($titleField, $option)
|
||||
'Selected' => (bool) $values->find('ID', $item->ID)
|
||||
]));
|
||||
};
|
||||
|
||||
|
@ -325,173 +294,95 @@ class TagField extends MultiSelectField
|
|||
}
|
||||
|
||||
$source->each($addOption);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the source array if required
|
||||
*
|
||||
* Note: this is expensive for a SS_List
|
||||
*
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSource()
|
||||
public function setValue($value, $source = null)
|
||||
{
|
||||
if (is_null($this->source)) {
|
||||
$this->source = $this->getListMap($this->getSourceList());
|
||||
if ($source instanceof DataObject) {
|
||||
$name = $this->getName();
|
||||
|
||||
if ($source->hasMethod($name)) {
|
||||
$value = $source->$name()->column($this->getTitleField());
|
||||
}
|
||||
}
|
||||
return $this->source;
|
||||
|
||||
if (!is_array($value)) {
|
||||
return parent::setValue($value);
|
||||
}
|
||||
|
||||
return parent::setValue(array_filter($value));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Intercept DataList source
|
||||
*
|
||||
* @param mixed $source
|
||||
* @return $this
|
||||
*/
|
||||
public function setSource($source)
|
||||
{
|
||||
// When setting a datalist force internal list to null
|
||||
if ($source instanceof DataList) {
|
||||
$this->source = null;
|
||||
$this->setSourceList($source);
|
||||
} else {
|
||||
parent::setSource($source);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param DataObject|DataObjectInterface $record DataObject to save data into
|
||||
* @throws Exception
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
$name = $this->getName();
|
||||
|
||||
if ($this->getIsMultiple() && strpos($name, '[') === false) {
|
||||
$name .= '[]';
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
parent::getAttributes(),
|
||||
[
|
||||
'name' => $name,
|
||||
'name' => $this->getName() . '[]',
|
||||
'style' => 'width: 100%',
|
||||
'data-schema' => json_encode($this->getSchemaData()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
protected function getListValues($values): array
|
||||
{
|
||||
if (empty($values)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (is_array($values)) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ($values instanceof SS_List) {
|
||||
return $values->column($this->getTitleField());
|
||||
}
|
||||
|
||||
if ($values instanceof DataObject && $values->exists()) {
|
||||
return [$values->{$this->getTitleField()} ?? $values->ID];
|
||||
}
|
||||
|
||||
if (is_int($values)) {
|
||||
return [$values];
|
||||
}
|
||||
|
||||
return [trim((string) $values)];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveInto(DataObjectInterface $record)
|
||||
{
|
||||
$name = $this->getName();
|
||||
$values = $this->getValueArray();
|
||||
|
||||
$titleField = $this->getTitleField();
|
||||
$values = $this->Value();
|
||||
$relation = $record->$name();
|
||||
$ids = [];
|
||||
|
||||
if (!$values) {
|
||||
$values = [];
|
||||
}
|
||||
|
||||
if (empty($record)) {
|
||||
if (empty($record) || empty($titleField)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Relation $relation */
|
||||
$relation = $record->hasMethod($name) ? $record->$name() : null;
|
||||
if (!$record->hasMethod($name)) {
|
||||
throw new Exception(
|
||||
sprintf("%s does not have a %s method", get_class($record), $name)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
$tag = $this->getOrCreateTag($value);
|
||||
|
||||
if ($tag) {
|
||||
$ids[] = $tag->ID;
|
||||
$values[$key] = $tag->Title;
|
||||
// Get or create record
|
||||
$record = $this->getOrCreateTag($value);
|
||||
if ($record) {
|
||||
$ids[] = $record->ID;
|
||||
$values[$key] = $record->Title;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($relation instanceof Relation) {
|
||||
// Save ids into relation
|
||||
$relation->setByIDList(array_filter($ids ?? []));
|
||||
} elseif ($record->hasField($name)) {
|
||||
if ($this->getIsMultiple()) {
|
||||
if ($record->obj($name) instanceof DBMultiEnum) {
|
||||
// Save dataValue into field... a CSV for DBMultiEnum
|
||||
$record->$name = $this->csvEncode(array_filter(array_values($values)));
|
||||
} else {
|
||||
// ... JSON-encoded string for other fields
|
||||
$record->$name = $this->stringEncode(array_filter(array_values($values)));
|
||||
}
|
||||
} else {
|
||||
if (isset($tag) && $tag->ID) {
|
||||
$record->$name = $tag->ID;
|
||||
} else {
|
||||
$record->$name = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
$relation->setByIDList(array_filter($ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create tag with the given value
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @param string $term
|
||||
* @return DataObject|bool
|
||||
*/
|
||||
protected function getOrCreateTag($value)
|
||||
protected function getOrCreateTag($term)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$value = $value['Value'] ?? '';
|
||||
}
|
||||
|
||||
// Check if existing record can be found
|
||||
$source = $this->getSourceList();
|
||||
$titleField = $this->getTitleField();
|
||||
|
||||
if (!$source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$record = $source
|
||||
->filter($titleField, $value)
|
||||
->filter($titleField, $term)
|
||||
->first();
|
||||
|
||||
if ($record) {
|
||||
return $record;
|
||||
}
|
||||
|
@ -500,13 +391,16 @@ class TagField extends MultiSelectField
|
|||
if ($this->getCanCreate()) {
|
||||
$dataClass = $source->dataClass();
|
||||
$record = Injector::inst()->create($dataClass);
|
||||
$record->{$titleField} = $value;
|
||||
$record->write();
|
||||
|
||||
if (is_array($term)) {
|
||||
$term = $term['Value'];
|
||||
}
|
||||
|
||||
$record->{$titleField} = $term;
|
||||
$record->write();
|
||||
if ($source instanceof SS_List) {
|
||||
$source->add($record);
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
|
@ -539,9 +433,6 @@ class TagField extends MultiSelectField
|
|||
protected function getTags($term)
|
||||
{
|
||||
$source = $this->getSourceList();
|
||||
if (!$source) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$titleField = $this->getTitleField();
|
||||
|
||||
|
@ -553,15 +444,14 @@ class TagField extends MultiSelectField
|
|||
// Map into a distinct list
|
||||
$items = [];
|
||||
$titleField = $this->getTitleField();
|
||||
|
||||
foreach ($query->map('ID', $titleField)->values() as $title) {
|
||||
foreach ($query->map('ID', $titleField) as $id => $title) {
|
||||
$items[$title] = [
|
||||
'Title' => $title,
|
||||
'Value' => $title,
|
||||
];
|
||||
}
|
||||
|
||||
return array_values($items ?? []);
|
||||
return array_values($items);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -586,8 +476,6 @@ class TagField extends MultiSelectField
|
|||
/** @var ReadonlyTagField $copy */
|
||||
$copy = $this->castedCopy(ReadonlyTagField::class);
|
||||
$copy->setSourceList($this->getSourceList());
|
||||
$copy->setTitleField($this->getTitleField());
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
|
@ -615,14 +503,4 @@ class TagField extends MultiSelectField
|
|||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function getSchemaDataType(): string
|
||||
{
|
||||
if ($this->getIsMultiple()) {
|
||||
return self::SCHEMA_DATA_TYPE_MULTISELECT;
|
||||
}
|
||||
|
||||
return self::SCHEMA_DATA_TYPE_SINGLESELECT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
|
||||
namespace SilverStripe\TagField\Tests;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\TagField\StringTagField;
|
||||
use SilverStripe\TagField\Tests\Stub\StringTagFieldTestBlogPost;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
class StringTagFieldTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
|
@ -62,44 +65,6 @@ class StringTagFieldTest extends SapphireTest
|
|||
$this->assertEquals('Tag1,Tag2', $record->Tags);
|
||||
}
|
||||
|
||||
public function testImmediateWriteEnabled()
|
||||
{
|
||||
if (Deprecation::isEnabled()) {
|
||||
$this->markTestSkipped('Test calls deprecated code');
|
||||
}
|
||||
$record = $this->getNewStringTagFieldTestBlogPost('BlogPost1');
|
||||
$record->write();
|
||||
|
||||
StringTagField::config()->set('immediate_write_enabled', true);
|
||||
|
||||
$field = new StringTagField('Tags');
|
||||
$field->setValue(['Tag1', 'Tag2']);
|
||||
$field->saveInto($record);
|
||||
|
||||
$this->assertEquals('Tag1,Tag2', StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
|
||||
}
|
||||
|
||||
public function testImmediateWriteDisabled()
|
||||
{
|
||||
if (Deprecation::isEnabled()) {
|
||||
$this->markTestSkipped('Test calls deprecated code');
|
||||
}
|
||||
$record = $this->getNewStringTagFieldTestBlogPost('BlogPost1');
|
||||
$record->write();
|
||||
|
||||
StringTagField::config()->set('immediate_write_enabled', false);
|
||||
|
||||
$field = new StringTagField('Tags');
|
||||
$field->setValue(['Tag1', 'Tag2']);
|
||||
$field->saveInto($record);
|
||||
|
||||
$this->assertNull(StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
|
||||
|
||||
$record->write();
|
||||
|
||||
$this->assertEquals('Tag1,Tag2', StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
|
||||
}
|
||||
|
||||
public function testItSuggestsTags()
|
||||
{
|
||||
$field = new StringTagField('SomeField', 'Some field', ['Tag1', 'Tag2'], []);
|
||||
|
@ -168,7 +133,7 @@ class StringTagFieldTest extends SapphireTest
|
|||
$schema = $field->getSchemaDataDefaults();
|
||||
$this->assertTrue($schema['lazyLoad']);
|
||||
$this->assertTrue($schema['creatable']);
|
||||
$this->assertStringContainsString('suggest', $schema['optionUrl']);
|
||||
$this->assertContains('suggest', $schema['optionUrl']);
|
||||
}
|
||||
|
||||
public function testSchemaIsAddedToAttributes()
|
||||
|
@ -178,28 +143,6 @@ class StringTagFieldTest extends SapphireTest
|
|||
$this->assertNotEmpty($attributes['data-schema']);
|
||||
}
|
||||
|
||||
public function testPerformReadonlyTransformation()
|
||||
{
|
||||
$field = new StringTagField('Tags');
|
||||
$field->setSource(['Test1', 'Test2', 'Test3']);
|
||||
|
||||
// Ensure a single value can be rendered
|
||||
$field->setValue(['Test2']);
|
||||
$field_readonly = $field->performReadonlyTransformation();
|
||||
$this->assertEquals('Test2', $field_readonly->Value());
|
||||
|
||||
// Ensure multiple valid values are rendered
|
||||
$field->setValue(['Test1', 'Test2']);
|
||||
$field_readonly = $field->performReadonlyTransformation();
|
||||
$this->assertEquals('Test1, Test2', $field_readonly->Value());
|
||||
|
||||
// Ensure an value not in the source array is still rendered
|
||||
// (because e.g. in history view it must have been a valid value when it was set)
|
||||
$field->setValue(['Test', 'Test1']);
|
||||
$field_readonly = $field->performReadonlyTransformation();
|
||||
$this->assertEquals('Test, Test1', $field_readonly->Value());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
* @return HTTPRequest
|
||||
|
|
|
@ -18,8 +18,4 @@ class TagFieldTestBlogPost extends DataObject implements TestOnly
|
|||
private static $many_many = [
|
||||
'Tags' => TagFieldTestBlogTag::class
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'PrimaryTag' => TagFieldTestBlogTag::class
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace SilverStripe\TagField\Tests;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
|
@ -14,6 +15,9 @@ use SilverStripe\TagField\Tests\Stub\TagFieldTestBlogPost;
|
|||
use SilverStripe\TagField\Tests\Stub\TagFieldTestBlogTag;
|
||||
use SilverStripe\TagField\Tests\Stub\TagFieldTestController;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
class TagFieldTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
|
@ -36,40 +40,12 @@ class TagFieldTest extends SapphireTest
|
|||
$field->setValue(['Tag3', 'Tag4']);
|
||||
$field->saveInto($record);
|
||||
$record->write();
|
||||
|
||||
$this->compareExpectedAndActualTags(
|
||||
['Tag3', 'Tag4'],
|
||||
$record
|
||||
);
|
||||
}
|
||||
|
||||
public function testItSavesToHasOne()
|
||||
{
|
||||
$record = $this->getNewTagFieldTestBlogPost('BlogPost1');
|
||||
$tag = new TagFieldTestBlogTag();
|
||||
$tag->Title = 'Foobar';
|
||||
$tag->write();
|
||||
|
||||
$field = new TagField('PrimaryTagID', '', new DataList(TagFieldTestBlogTag::class));
|
||||
$field->setIsMultiple(false);
|
||||
|
||||
$field->setValue('Foobar');
|
||||
$field->saveInto($record);
|
||||
$record->write();
|
||||
|
||||
$this->assertEquals($tag->ID, $record->PrimaryTagID, 'The tag is saved to a has_one');
|
||||
|
||||
$tag = new TagFieldTestBlogTag();
|
||||
$tag->Title = 'Foobarbaz';
|
||||
$tag->write();
|
||||
|
||||
$field->setValue(['Foobarbaz']);
|
||||
$field->saveInto($record);
|
||||
$record->write();
|
||||
|
||||
$this->assertEquals($tag->ID, $record->PrimaryTagID, 'The tag is saved to a has_one');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
|
@ -100,7 +76,7 @@ class TagFieldTest extends SapphireTest
|
|||
*/
|
||||
protected function compareTagLists(array $expected, DataList $actualSource)
|
||||
{
|
||||
$actual = array_values($actualSource->map('ID', 'Title')->toArray() ?? []);
|
||||
$actual = array_values($actualSource->map('ID', 'Title')->toArray());
|
||||
sort($expected);
|
||||
sort($actual);
|
||||
|
||||
|
@ -156,30 +132,6 @@ class TagFieldTest extends SapphireTest
|
|||
);
|
||||
}
|
||||
|
||||
public function testSavesReactTags()
|
||||
{
|
||||
$record = $this->getNewTagFieldTestBlogPost('BlogPost1');
|
||||
$record->write();
|
||||
|
||||
$field = new TagField('Tags', '', new DataList(TagFieldTestBlogTag::class));
|
||||
$field->setValue([
|
||||
[
|
||||
'Title' => 'Tag1',
|
||||
'Value' => 'Tag1',
|
||||
],
|
||||
[
|
||||
'Title' => 'Tag2',
|
||||
'Value' => 'Tag2',
|
||||
],
|
||||
]);
|
||||
$field->saveInto($record);
|
||||
|
||||
$this->compareExpectedAndActualTags(
|
||||
['Tag1', 'Tag2'],
|
||||
$record
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that {@see TagField::saveInto} respects existing tags
|
||||
*/
|
||||
|
@ -335,7 +287,7 @@ class TagFieldTest extends SapphireTest
|
|||
$this->objFromFixture(TagFieldTestBlogPost::class, 'BlogPost2')
|
||||
);
|
||||
|
||||
$ids = TagFieldTestBlogTag::get()->column('ID');
|
||||
$ids = TagFieldTestBlogTag::get()->column('Title');
|
||||
|
||||
$this->assertEquals($field->Value(), $ids);
|
||||
}
|
||||
|
@ -385,9 +337,7 @@ class TagFieldTest extends SapphireTest
|
|||
$tag = TagFieldTestBlogTag::get()->first();
|
||||
$this->assertNotEmpty($tag);
|
||||
$this->assertEquals('New Tag', $tag->Title);
|
||||
|
||||
$record = TagFieldTestBlogPost::get()->byID($record->ID);
|
||||
|
||||
$this->assertEquals(
|
||||
$tag->ID,
|
||||
$record->Tags()->first()->ID
|
||||
|
@ -402,31 +352,6 @@ class TagFieldTest extends SapphireTest
|
|||
$field = new TagField('Tags', '', TagFieldTestBlogTag::get());
|
||||
$readOnlyField = $field->performReadonlyTransformation();
|
||||
$this->assertInstanceOf(ReadonlyTagField::class, $readOnlyField);
|
||||
|
||||
// Custom title field
|
||||
$field = new TagField('Tags', '', TagFieldTestBlogTag::get());
|
||||
$field->setTitleField('Name');
|
||||
$readOnlyField = $field->performReadonlyTransformation();
|
||||
$this->assertEquals('Name', $readOnlyField->getTitleField());
|
||||
}
|
||||
|
||||
public function testItDisplaysWithSelectedValuesFromDataList()
|
||||
{
|
||||
$source = TagFieldTestBlogTag::get();
|
||||
$selectedTag = $source->First();
|
||||
$unselectedTag = $source->Last();
|
||||
$value = $source->filter('ID', $selectedTag->ID); // arbitrary subset
|
||||
|
||||
$field = new TagField('TestField', null, $source, $value);
|
||||
|
||||
// Not the cleanest way to assert this, but getOptions() is protected
|
||||
$schema = $field->getSchemaDataDefaults();
|
||||
$this->assertTrue(
|
||||
$this->getFromOptionsByTitle($schema['options'], $selectedTag->Title)['Selected']
|
||||
);
|
||||
$this->assertFalse(
|
||||
$this->getFromOptionsByTitle($schema['options'], $unselectedTag->Title)['Selected']
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetSchemaDataDefaults()
|
||||
|
@ -463,7 +388,7 @@ class TagFieldTest extends SapphireTest
|
|||
$schema = $field->getSchemaDataDefaults();
|
||||
$this->assertTrue($schema['lazyLoad']);
|
||||
$this->assertTrue($schema['creatable']);
|
||||
$this->assertStringContainsString('suggest', $schema['optionUrl']);
|
||||
$this->assertContains('suggest', $schema['optionUrl']);
|
||||
}
|
||||
|
||||
public function testSchemaIsAddedToAttributes()
|
||||
|
@ -472,20 +397,4 @@ class TagFieldTest extends SapphireTest
|
|||
$attributes = $field->getAttributes();
|
||||
$this->assertNotEmpty($attributes['data-schema']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @param string $title
|
||||
* @return array|null
|
||||
*/
|
||||
protected function getFromOptionsByTitle(array $options, $title)
|
||||
{
|
||||
foreach ($options as $option) {
|
||||
if ($option['Title'] == $title) {
|
||||
return $option;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user