Compare commits

...

36 Commits
2 ... 3.1.0-rc1

Author SHA1 Message Date
Guy Sartorelli a7023742a8
Merge branch '3.0' into 3 2023-08-30 11:59:46 +12:00
Guy Sartorelli c0c4278592
Merge pull request #259 from creative-commoners/pulls/3.0/module-standardiser-1693279054
MNT Run module-standardiser
2023-08-29 17:12:15 +12:00
Steve Boyd 6dfb322d05 MNT Run module-standardiser 2023-08-29 15:17:34 +12:00
github-actions[bot] d1c02ca800
DEP Update JS dependencies (#258)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-08-24 11:44:38 +12:00
Steve Boyd 91fcd0de13 Merge branch '3.0' into 3 2023-08-22 15:35:15 +12:00
Guy Sartorelli 02aa14ac2d
Merge pull request #256 from blueo/pulls/add-onBlur-handler
Add onBlur handler back
2023-08-21 13:31:34 +12:00
Bernie Hamlin 92823c790a Adds back onBlur handler
removed in 249842fab8
2023-08-18 16:15:11 +12:00
Guy Sartorelli 8aad88b562
Merge pull request #253 from creative-commoners/pulls/3/module-standardiser-1691550433
MNT Run module-standardiser
2023-08-15 12:27:06 +12:00
Steve Boyd 48bf401342 MNT Run module-standardiser 2023-08-14 15:49:30 +12:00
Guy Sartorelli cc2016a8e5
Merge pull request #251 from creative-commoners/pulls/3.0/fix-changetracker-trigger
FIX TagField triggers edit form change without changes
2023-07-21 10:08:10 +12:00
Sabina Talipova efeb8b224d FIX TagField triggers edit form change without changes 2023-07-19 14:07:43 +12:00
Sabina Talipova 61cf3751ab
DEP Update eslint module (#250) 2023-07-12 11:05:12 +12:00
Sabina Talipova 57af611181
MNT ESLint issues (#248) 2023-07-10 09:53:48 +12:00
Sabina Talipova af27f42f1d
Merge pull request #245 from creative-commoners/pulls/3/storybook-docs
ENH Storybook documentation
2023-06-20 10:18:47 +12:00
Sabina Talipova f667020678 ENH Storybook Doc Blocks 2023-06-14 09:07:25 +12:00
Sabina Talipova a2b1a82734 FIX Replace 4 space indent with 2 spaces 2023-06-12 15:35:04 +12:00
Sabina Talipova 47f86a6c25
MNT Support Storybook v7 (#244) 2023-05-31 10:47:42 +12:00
Steve Boyd 2906a86064 Merge branch '3.0' into 3 2023-05-04 13:27:18 +12:00
Guy Sartorelli f1a2e30a26
Merge pull request #240 from creative-commoners/pulls/3.0/react-testing-library
MNT Use React Testing Library
2023-05-01 17:23:05 +12:00
Steve Boyd f2990cb03a MNT Use React Testing Library 2023-05-01 12:53:44 +12:00
Guy Sartorelli 1b97237abb
Merge branch '2' into 3.0 2023-04-27 14:56:05 +12:00
Sabina Talipova 315096c64d
Merge pull request #239 from creative-commoners/pulls/3.0/cms5-readme
DOC Update README.md for CMS 5
2023-04-26 08:21:57 +12:00
Guy Sartorelli 242f6bbb57
DOC Update README.md for CMS 5 2023-04-24 16:29:40 +12:00
Maxime Rainville 09212e406a
Merge pull request #238 from creative-commoners/pulls/3.0/devjs
MNT Update dev JS
2023-04-03 15:32:38 +12:00
Steve Boyd 69d64602dc MNT Update dev JS 2023-04-03 14:38:53 +12:00
Steve Boyd a885be8c13 Merge branch '3.0' into 3 2023-03-30 12:56:00 +13:00
Steve Boyd b80f94f85e Merge branch '2' into 3.0 2023-03-30 12:55:51 +13:00
Steve Boyd dbd7b76c39 Merge branch '3.0' into 3 2023-03-02 17:04:05 +13:00
Steve Boyd 04234de836 Merge branch '2' into 3.0 2023-03-02 17:03:34 +13:00
Guy Sartorelli dd7481a2bc
Merge pull request #234 from kinglozzer/formfield-validation-extensions
NEW: Add extension hooks for field-specific validation
2023-02-23 09:47:41 +13:00
Loz Calver 8b8bd13097 NEW: Add extension hooks for field-specific validation 2023-02-22 16:07:46 +00:00
Guy Sartorelli 249842fab8
DEP Upgrade frontend build stack (#229) 2023-02-02 10:13:18 +13:00
Sabina Talipova 6f203a36af
API Remove deprecated code (#228) 2023-01-25 13:54:29 +13:00
Maxime Rainville 5b26bd0739
Merge pull request #230 from creative-commoners/pulls/3/remove-legacy-upgrader
MNT Remove legacy upgrader config
2023-01-23 10:09:24 +13:00
Steve Boyd 28c41961a4 MNT Remove legacy upgrader config 2023-01-20 16:24:54 +13:00
Sabina Talipova 08d1646e4c
DEP PHP Support in CMS5 (#224) 2022-12-19 11:37:06 +13:00
23 changed files with 6465 additions and 10408 deletions

View File

@ -1,10 +1,10 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
# At 9:45 PM UTC, on day 26 of the month
schedule:
- cron: '50 10 4 * *'
- cron: '45 21 26 * *'
workflow_dispatch:
jobs:
keepalive:

17
.github/workflows/merge-up.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Merge-up
on:
# At 12:00 PM UTC, only on Wednesday
schedule:
- cron: '0 12 * * 3'
workflow_dispatch:
jobs:
merge-up:
name: Merge-up
# 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: Merge-up
uses: silverstripe/gha-merge-up@v1

View File

@ -4,7 +4,7 @@ on:
workflow_dispatch:
# Run on a schedule of once per quarter
schedule:
- cron: '0 0 1 */3 *'
- cron: '45 21 1 */3 *'
jobs:
update-js:

2
.nvmrc
View File

@ -1 +1 @@
10
18

View File

@ -1,4 +0,0 @@
mappings:
StringTagField: SilverStripe\TagField\StringTagField
TagField: SilverStripe\TagField\TagField
TagField_Readonly: SilverStripe\TagField\TagField\Readonly

View File

@ -1,4 +1,4 @@
Copyright (c) 2016, SilverStripe Limited
Copyright (c) 2016, Silverstripe Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

View File

@ -5,6 +5,12 @@
Custom tag input field, for SilverStripe.
## Installation
```sh
composer require silverstripe/tagfield
```
## Overview
![Screenshot](docs/en/screenshot.png)
@ -16,16 +22,6 @@ 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

6
babel.config.json Normal file
View File

@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}

View File

@ -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=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"==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},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),y=n(3),v=o(y),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"),x=o(w),_=n("./node_modules/prop-types/index.js"),C=o(_),P=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,x.default)(n.fetchOptions,500),n}return u(t,e),c(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=O.default.parse(n,!0);return i.query.term=e,(0,v.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":h(u))&&(s.value=u)}return p.default.createElement(a,l({},s,{onChange:this.handleChange,onBlur:this.handleOnBlur,inputProps:{className:"no-change-track"}},i))}}]),t}(f.Component);P.propTypes={name:C.default.string.isRequired,labelKey:C.default.string.isRequired,valueKey:C.default.string.isRequired,lazyLoad:C.default.bool,creatable:C.default.bool,multi:C.default.bool,disabled:C.default.bool,options:C.default.arrayOf(C.default.object),optionUrl:C.default.string,value:C.default.any,onChange:C.default.func,onBlur:C.default.func},P.defaultProps={labelKey:"Title",valueKey:"Value",disabled:!1,lazyLoad:!1,creatable:!1,multi:!1},t.Component=P,t.default=(0,g.default)(P)},"./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 .Select :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 y=l.length-1;return a.promise.then(function(e){return e[y]})}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(A[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+=R(e>>>10&1023|55296),e=56320|1023&e),t+=R(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?I(e/x):e>>1,e+=I(e/t);e>S*O>>1;o+=g)e=I(e/S);return I(o+(S+1)*e/(e+w))}function p(e){var t,n,o,r,i,a,u,c,p,d,m=[],y=e.length,v=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<y;){for(i=v,a=1,u=g;r>=y&&s("invalid-input"),c=h(e.charCodeAt(r++)),(c>=g||c>I((b-v)/a))&&s("overflow"),v+=c*a,p=u<=x?j:u>=x+O?O:u-x,!(c<p);u+=g)d=g-p,a>I(b/d)&&s("overflow"),a*=d;t=m.length+1,x=f(v-i,t,0==i),I(v/t)>b-w&&s("overflow"),w+=I(v/t),v%=t,m.splice(v++,0,w)}return l(m)}function d(e){var t,n,o,r,i,a,l,h,p,d,m,y,v,w,x,T=[];for(e=u(e),y=e.length,t=C,n=0,i=_,a=0;a<y;++a)(m=e[a])<128&&T.push(R(m));for(o=r=T.length,r&&T.push(P);o<y;){for(l=b,a=0;a<y;++a)(m=e[a])>=t&&m<l&&(l=m);for(v=o+1,l-t>I((b-n)/v)&&s("overflow"),n+=(l-t)*v,t=l,a=0;a<y;++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(R(c(d+x%w,0))),h=I(x/w);T.push(R(c(h,0))),i=f(n,v,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 y(e){return a(e,function(e){return q.test(e)?"xn--"+d(e):e})}var v,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]/,k=/[\x2E\u3002\uFF0E\uFF61]/g,A={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},S=g-j,I=Math.floor,R=String.fromCharCode;v={version:"1.4.1",ucs2:{decode:u,encode:l},decode:p,encode:d,toASCII:y,toUnicode:m},void 0!==(r=function(){return v}.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"),y=m.indexOf(n);y>=0?(c=m.substr(0,y),f=m.substr(y+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),y=["%","/","?",";","#"].concat(m),v=["/","?","#"],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<v.length;P++){var T=a.indexOf(v[P]);-1!==T&&(-1===C||T<C)&&(C=T)}var q,k;k=-1===C?a.lastIndexOf("@"):a.lastIndexOf("@",C),-1!==k&&(q=a.slice(0,k),a=a.slice(k+1),this.auth=decodeURIComponent(q)),C=-1;for(var P=0;P<y.length;P++){var T=a.indexOf(y[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 A="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!A)for(var S=this.hostname.split(/\./),P=0,I=S.length;P<I;P++){var R=S[P];if(R&&!R.match(b)){for(var U="",E=0,F=R.length;E<F;E++)R.charCodeAt(E)>127?U+="x":U+=R[E];if(!U.match(b)){var L=S.slice(0,P),M=S.slice(P+1),N=R.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(),A||(this.hostname=u.toASCII(this.hostname));var z=this.port?":"+this.port:"",B=this.hostname||"";this.host=B+z,this.href+=this.host,A&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==a[0]&&(a="/"+a))}if(!j[d])for(var P=0,I=m.length;P<I;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||"",y=n.search||"";n.path=m+y}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var v=n.pathname&&"/"===n.pathname.charAt(0),b=e.host||e.pathname&&"/"===e.pathname.charAt(0),g=b||v||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,k=x.length;k>=0;k--)P=x[k],"."===P?x.splice(k,1):".."===P?(x.splice(k,1),q++):q&&(x.splice(k,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 A=""===x[0]||x[0]&&"/"===x[0].charAt(0);if(_){n.hostname=n.host=A?"":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&&!A&&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}});
!function(){"use strict";var e={274:function(e,t,n){var o,a=(o=n(521))&&o.__esModule?o:{default:o};window.document.addEventListener("DOMContentLoaded",(()=>{(0,a.default)()}))},521:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o=i(n(648)),a=i(n(157));function i(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{o.default.component.registerMany({TagField:a.default})}},157:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var o=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=v(t);if(n&&n.has(e))return n.get(e);var o={},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=a?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}o.default=e,n&&n.set(e,o);return o}(n(363)),a=g(n(967)),i=g(n(493)),s=g(n(198)),r=g(n(453)),l=g(n(78)),u=g(n(754)),c=g(n(875)),f=g(n(42)),d=g(n(872)),p=g(n(496)),h=g(n(86));function g(e){return e&&e.__esModule?e:{default:e}}function v(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(v=function(e){return e?n:t})(e)}function m(){return m=Object.assign?Object.assign.bind():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},m.apply(this,arguments)}class y extends o.Component{constructor(e){super(e),this.selectComponentRef=o.default.createRef(),this.state={initalState:e.value?e.value:[],hasChanges:!1},this.isControlled()||(this.state={...this.state,value:e.value}),this.handleChange=this.handleChange.bind(this),this.handleOnBlur=this.handleOnBlur.bind(this),this.isValidNewOption=this.isValidNewOption.bind(this),this.getOptions=this.getOptions.bind(this),this.fetchOptions=(0,p.default)(this.fetchOptions,500)}componentDidUpdate(e,t){if(t.hasChanges!==this.state.hasChanges){const e=this.selectComponentRef.current.inputRef,t=new Event("change",{bubbles:!0});e.dispatchEvent(t)}}getOptions(e){const{lazyLoad:t,options:n}=this.props;return t?e?this.fetchOptions(e):Promise.resolve([]):Promise.resolve(n)}handleChange(e){this.setState({hasChanges:!1}),JSON.stringify(this.state.initalState)!==JSON.stringify(e)&&this.setState({hasChanges:!0}),this.isControlled()?this.props.onChange(e):this.setState({value:e})}isControlled(){return"function"==typeof this.props.onChange}handleOnBlur(){}fetchOptions(e){const{optionUrl:t,labelKey:n,valueKey:o}=this.props,a=d.default.parse(t,!0);return a.query.term=e,(0,c.default)(d.default.format(a),{credentials:"same-origin"}).then((e=>e.json())).then((e=>e.items.map((e=>({[n]:e.Title,[o]:e.Value,Selected:e.Selected})))))}isValidNewOption(e,t,n){const{valueKey:o}=this.props;if(!e)return!1;if(Array.isArray(t)){if(this.valueInOptions(e,t,o))return!1}else if(e===t[o])return!1;return!this.valueInOptions(e,n,o)}valueInOptions(e,t,n){for(const o of t)if(e===o[n])return!0;return!1}render(){const{lazyLoad:e,options:t,creatable:n,multi:a,disabled:i,labelKey:s,valueKey:r,SelectComponent:c,AsyncCreatableSelectComponent:f,AsyncSelectComponent:d,CreatableSelectComponent:p,...h}=this.props,g=e?{loadOptions:this.getOptions}:{options:t};let v=c;if(e&&n?v=f:e?v=d:n&&(v=p),this.isControlled()||(h.value=this.state.value),!a&&h.value&&Object.keys(h.value).length>0){const e=h.value[Object.keys(h.value)[0]];"object"==typeof e&&(h.value=e)}const y=this.state.hasChanges?"":"no-change-track";return o.default.createElement(l.default,null,o.default.createElement(v,m({},h,{isMulti:a,isDisabled:i,cacheOptions:!0,onChange:this.handleChange,onBlur:this.handleOnBlur},g,{getOptionLabel:e=>e[s],getOptionValue:e=>e[r],noOptionsMessage:e=>{let{inputValue:t}=e;return t?u.default._t("TagField.NO_OPTIONS","No options"):u.default._t("TagField.TYPE_TO_SEARCH","Type to search")},isValidNewOption:this.isValidNewOption,getNewOptionData:(e,t)=>({[s]:t,[r]:e}),classNamePrefix:"ss-tag-field",className:y,ref:this.selectComponentRef})))}}t.Component=y,y.propTypes={name:h.default.string.isRequired,labelKey:h.default.string.isRequired,valueKey:h.default.string.isRequired,lazyLoad:h.default.bool,creatable:h.default.bool,multi:h.default.bool,disabled:h.default.bool,options:h.default.arrayOf(h.default.object),optionUrl:h.default.string,value:h.default.any,onChange:h.default.func,onBlur:h.default.func,SelectComponent:h.default.oneOfType([h.default.object,h.default.func]),AsyncCreatableSelectComponent:h.default.oneOfType([h.default.object,h.default.func]),AsyncSelectComponent:h.default.oneOfType([h.default.object,h.default.func]),CreatableSelectComponent:h.default.oneOfType([h.default.object,h.default.func])},y.defaultProps={labelKey:"Title",valueKey:"Value",disabled:!1,lazyLoad:!1,creatable:!1,multi:!1,SelectComponent:a.default,AsyncCreatableSelectComponent:s.default,AsyncSelectComponent:i.default,CreatableSelectComponent:r.default};var O=(0,f.default)(y);t.default=O},265:function(e,t,n){var o,a=(o=n(363))&&o.__esModule?o:{default:o},i=n(691),s=n(648);function r(){return r=Object.assign?Object.assign.bind():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},r.apply(this,arguments)}window.jQuery.entwine("ss",(e=>{e(".js-injector-boot .ss-tag-field.entwine").entwine({ReactRoot:null,onmatch(){const e=this.closest(".cms-content").attr("id"),t=e?{context:e}:{},n=(0,s.loadComponent)("TagField",t),o={...this.data("schema"),onBlur:()=>{this.parents(".cms-edit-form:first").trigger("change")}};let l=this.getReactRoot();l||(l=(0,i.createRoot)(this[0]),this.setReactRoot(l)),l.render(a.default.createElement(n,r({noHolder:!0},o)))},onunmatch(){const e=this.getReactRoot();e&&(e.unmount(),this.setReactRoot(null))}}),e(".cms-edit-form").entwine({getChangeTrackerOptions(){const t=void 0===this.entwineData("ChangeTrackerOptions");let n=this._super();return t&&(n=e.extend({},n),n.ignoreFieldSelector+=", .ss-tag-field .no-change-track :input",this.setChangeTrackerOptions(n)),n}})}))},496:function(e){e.exports=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=void 0,a=void 0,i=void 0,s=[];return function(){var l=function(e){return"function"==typeof e?e():e}(t),u=(new Date).getTime(),c=!o||u-o>l;o=u;for(var f=arguments.length,d=Array(f),p=0;p<f;p++)d[p]=arguments[p];if(c&&n.leading)return n.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(i):a=function(){var e={};return e.promise=new Promise((function(t,n){e.resolve=t,e.reject=n})),e}(),s.push(d),i=setTimeout(r.bind(this),l),n.accumulate){var h=s.length-1;return a.promise.then((function(e){return e[h]}))}return a.promise};function r(){var t=a;clearTimeout(i),Promise.resolve(n.accumulate?e.call(this,s):e.apply(this,s[s.length-1])).then(t.resolve,t.reject),s=[],a=null}}},78:function(e){e.exports=EmotionCssCacheProvider},42:function(e){e.exports=FieldHolder},648:function(e){e.exports=Injector},875:function(e){e.exports=IsomorphicFetch},872:function(e){e.exports=NodeUrl},86:function(e){e.exports=PropTypes},363:function(e){e.exports=React},691:function(e){e.exports=ReactDomClient},967:function(e){e.exports=ReactSelect},493:function(e){e.exports=ReactSelectAsync},198:function(e){e.exports=ReactSelectAsyncCreatable},453:function(e){e.exports=ReactSelectCreatable},754:function(e){e.exports=i18n}},t={};function n(o){var a=t[o];if(void 0!==a)return a.exports;var i=t[o]={exports:{}};return e[o](i,i.exports,n),i.exports}n(265),n(274)}();

View File

@ -1 +1 @@
.ss-tag-field .Select--multi .Select-value{margin-top:3px}
.ss-tag-field__multi-value{margin-top:3px}.ss-tag-field__control{border-color:#ced5e1;box-shadow:none}.ss-tag-field__control--is-focused{border-color:#29abe2;box-shadow:none}.ss-tag-field__option+.ss-tag-field__option{border-top:1px solid #dbe0e9}.ss-tag-field__option-button{border:1px solid #dbe0e9;border-radius:.23rem;background:#fff;width:auto !important;max-width:25%;margin:-4px -5px -4px 5px;padding:4px 5px 4px 4px;cursor:pointer}.ss-tag-field__option-button:hover{background:#ced5e1}.ss-tag-field__option-button .font-icon-right-open-big{margin:2px 0 0 -1px;width:24px}.ss-tag-field__option-count-icon{padding:0 .6154rem;line-height:.8}.ss-tag-field__option-context{color:#566b8d;font-size:.923rem}.ss-tag-field__option--is-focused{background-color:#f5f5f5}.ss-tag-field__option--is-selected{background:#0071c4;color:#fff}.ss-tag-field__option--is-selected .ss-tag-field__option-button{border-color:#29abe2;background:none;color:#fff}.ss-tag-field__option--is-selected .ss-tag-field__option-button:hover{background:rgba(0,0,0,.2)}.ss-tag-field__option-title--highlighted{font-weight:bold}.ss-tag-field__indicator{cursor:pointer}.ss-tag-field__clear-indicator:hover,.ss-tag-field__clear-indicator:focus{color:#d40404}.ss-tag-field__dropdown-indicator:hover,.ss-tag-field__dropdown-indicator:focus{color:#303b4d}.ss-tag-field__multi-value{color:#43536d;background-color:#fff;border:1px solid #66afe9;border-radius:.23rem}.ss-tag-field__multi-value__remove{font-size:1.231rem;padding:0 5px 2px;border-left:1px solid #66afe9;border-radius:0}.ss-tag-field__multi-value__remove:focus,.ss-tag-field__multi-value__remove:hover{background-color:rgba(0,113,230,.08);color:#0071e6}

View File

@ -1,2 +1,2 @@
require('legacy/entwine/TagField');
require('boot');
import 'legacy/entwine/TagField';
import 'boot';

View File

@ -1,5 +1,10 @@
import React, { Component } from 'react';
import Select from 'react-select';
import AsyncSelect from 'react-select/async';
import AsyncCreatableSelect from 'react-select/async-creatable';
import CreatableSelect from 'react-select/creatable';
import EmotionCssCacheProvider from 'containers/EmotionCssCacheProvider/EmotionCssCacheProvider';
import i18n from 'i18n';
import fetch from 'isomorphic-fetch';
import fieldHolder from 'components/FieldHolder/FieldHolder';
import url from 'url';
@ -10,18 +15,35 @@ class TagField extends Component {
constructor(props) {
super(props);
this.selectComponentRef = React.createRef();
this.state = {
initalState: props.value ? props.value : [],
hasChanges: false,
};
if (!this.isControlled()) {
this.state = {
...this.state,
value: props.value,
};
}
this.handleChange = this.handleChange.bind(this);
this.handleOnBlur = this.handleOnBlur.bind(this);
this.isValidNewOption = this.isValidNewOption.bind(this);
this.getOptions = this.getOptions.bind(this);
this.fetchOptions = debounce(this.fetchOptions, 500);
}
componentDidUpdate(previousProps, previousState) {
if (previousState.hasChanges !== this.state.hasChanges) {
const element = this.selectComponentRef.current.inputRef;
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
@ -33,13 +55,12 @@ class TagField extends Component {
const { lazyLoad, options } = this.props;
if (!lazyLoad) {
return Promise.resolve({ options });
return Promise.resolve(options);
}
if (!input) {
return Promise.resolve({ options: [] });
return Promise.resolve([]);
}
return this.fetchOptions(input);
}
@ -50,6 +71,16 @@ 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;
@ -91,30 +122,87 @@ class TagField extends Component {
return fetch(url.format(fetchURL), { credentials: 'same-origin' })
.then((response) => response.json())
.then((json) => ({
options: json.items.map((item) => ({
.then((json) => json.items.map(
(item) => ({
[labelKey]: item.Title,
[valueKey]: item.Value,
Selected: item.Selected,
})),
}));
})
));
}
/**
* Check if a new option can be created based on a given input
* @param {string} inputValue
* @param {array|object} value
* @param {array} currentOptions
* @returns {boolean}
*/
isValidNewOption(inputValue, value, currentOptions) {
const { valueKey } = this.props;
// Don't allow empty options
if (!inputValue) {
return false;
}
// Don't repeat the currently selected option
if (Array.isArray(value)) {
if (this.valueInOptions(inputValue, value, valueKey)) {
return false;
}
} else if (inputValue === value[valueKey]) {
return false;
}
// Don't repeat any existing option
return !this.valueInOptions(inputValue, currentOptions, valueKey);
}
/**
* Check if a value is in an array of options already
* @param {string} value
* @param {array} options
* @param {string} valueKey
* @returns {boolean}
*/
valueInOptions(value, options, valueKey) {
// eslint-disable-next-line no-restricted-syntax
for (const item of options) {
if (value === item[valueKey]) {
return true;
}
}
return false;
}
render() {
const { lazyLoad, options, creatable, ...passThroughAttributes } =
this.props;
const {
lazyLoad,
options,
creatable,
multi,
disabled,
labelKey,
valueKey,
SelectComponent,
AsyncCreatableSelectComponent,
AsyncSelectComponent,
CreatableSelectComponent,
...passThroughAttributes
} = this.props;
const optionAttributes = lazyLoad
? { loadOptions: this.getOptions }
: { options };
let SelectComponent = Select;
let DynamicSelect = SelectComponent;
if (lazyLoad && creatable) {
SelectComponent = Select.AsyncCreatable;
DynamicSelect = AsyncCreatableSelectComponent;
} else if (lazyLoad) {
SelectComponent = Select.Async;
DynamicSelect = AsyncSelectComponent;
} else if (creatable) {
SelectComponent = Select.Creatable;
DynamicSelect = CreatableSelectComponent;
}
// Update the value to passthrough with the kept state provided this component is not
@ -124,7 +212,7 @@ class TagField extends Component {
}
// if this is a single select then we just need the first value
if (!passThroughAttributes.multi && passThroughAttributes.value) {
if (!multi && passThroughAttributes.value) {
if (Object.keys(passThroughAttributes.value).length > 0) {
const value =
passThroughAttributes.value[
@ -137,14 +225,28 @@ class TagField extends Component {
}
}
const changedClassName = this.state.hasChanges ? '' : 'no-change-track';
return (
<SelectComponent
{...passThroughAttributes}
onChange={this.handleChange}
onBlur={this.handleOnBlur}
inputProps={{ className: 'no-change-track' }}
{...optionAttributes}
/>
<EmotionCssCacheProvider>
<DynamicSelect
{...passThroughAttributes}
isMulti={multi}
isDisabled={disabled}
cacheOptions
onChange={this.handleChange}
onBlur={this.handleOnBlur}
{...optionAttributes}
getOptionLabel={(option) => option[labelKey]}
getOptionValue={(option) => option[valueKey]}
noOptionsMessage={({ inputValue }) => (inputValue ? i18n._t('TagField.NO_OPTIONS', 'No options') : i18n._t('TagField.TYPE_TO_SEARCH', 'Type to search'))}
isValidNewOption={this.isValidNewOption}
getNewOptionData={(inputValue, label) => ({ [labelKey]: label, [valueKey]: inputValue })}
classNamePrefix="ss-tag-field"
className={changedClassName}
ref={this.selectComponentRef}
/>
</EmotionCssCacheProvider>
);
}
}
@ -162,6 +264,10 @@ TagField.propTypes = {
value: PropTypes.any,
onChange: PropTypes.func,
onBlur: PropTypes.func,
SelectComponent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
AsyncCreatableSelectComponent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
AsyncSelectComponent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
CreatableSelectComponent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
};
TagField.defaultProps = {
@ -171,6 +277,10 @@ TagField.defaultProps = {
lazyLoad: false,
creatable: false,
multi: false,
SelectComponent: Select,
AsyncCreatableSelectComponent: AsyncCreatableSelect,
AsyncSelectComponent: AsyncSelect,
CreatableSelectComponent: CreatableSelect,
};
export { TagField as Component };

View File

@ -1,3 +1,110 @@
.ss-tag-field .Select--multi .Select-value {
.ss-tag-field__multi-value {
margin-top: 3px;
}
/* Styles below here are duplicates of the treedropdownfield styles in silverstripe/admin, but with the appropriate classnames for tagfield. */
.ss-tag-field__control {
border-color: $gray-200;
box-shadow: none;
}
.ss-tag-field__control--is-focused {
border-color: $brand-primary;
box-shadow: none;
}
.ss-tag-field__option+.ss-tag-field__option {
border-top: 1px solid $border-color-light;
}
.ss-tag-field__option-button {
border: 1px solid $border-color-light;
border-radius: $border-radius;
background: $white;
// needed to override the width rule in .fill-width
width: auto !important; // sass-lint:disable-line no-important
max-width: 25%;
margin: -4px -5px -4px 5px;
padding: 4px 5px 4px 4px;
cursor: pointer;
&:hover {
background: $gray-200;
}
.font-icon-right-open-big {
margin: 2px 0 0 -1px;
width: 24px;
}
}
.ss-tag-field__option-count-icon {
padding: 0 calc($spacer / 2);
line-height: 0.8;
}
.ss-tag-field__option-context {
color: $gray-600;
font-size: $font-size-sm;
}
.ss-tag-field__option--is-focused {
background-color: $list-group-hover-bg;
}
.ss-tag-field__option--is-selected {
background: $link-color;
color: $white;
.ss-tag-field__option-button {
border-color: $brand-primary;
background: none;
color: $white;
&:hover {
background: rgba(0, 0, 0, 0.2);
}
}
}
.ss-tag-field__option-title--highlighted {
font-weight: bold;
}
.ss-tag-field__indicator {
cursor: pointer;
}
.ss-tag-field__clear-indicator {
&:hover,
&:focus {
color: $brand-danger;
}
}
.ss-tag-field__dropdown-indicator {
&:hover,
&:focus {
color: $body-color-dark;
}
}
.ss-tag-field__multi-value {
color: $body-color;
background-color: $white;
border: 1px solid $input-focus-border-color;
border-radius: $border-radius;
}
.ss-tag-field__multi-value__remove {
font-size: $font-size-lg;
padding: 0 5px 2px;
border-left: 1px solid $input-focus-border-color;
border-radius: 0;
&:focus,
&:hover {
background-color: rgba(0, 113, 230, .08);
color: #0071e6;
}
}

View File

@ -1,36 +1,109 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { Component as TagField } from '../TagField';
import TagField from 'components/TagField';
storiesOf('TagField/TagField', module)
.addDecorator(storyFn => (
<div style={{ width: '250px' }} className="ss-tag-field">
{storyFn()}
</div>
))
.add('Simple Example', () => (
<TagField
name="test"
options={[
{ Title: 'One', Value: 1 },
{ Title: 'Two', Value: 2 },
{ Title: 'Three', Value: 3 },
{ Title: 'Four', Value: 4 },
{ Title: 'Five', Value: 5 },
]}
/>
))
.add('Multiple Selection', () => (
<TagField
name="test"
multi
options={[
{ Title: 'One', Value: 1 },
{ Title: 'Two', Value: 2 },
{ Title: 'Three', Value: 3 },
{ Title: 'Four', Value: 4 },
{ Title: 'Five', Value: 5 },
]}
/>
))
;
const options = [
{ Title: 'One', Value: 1 },
{ Title: 'Two', Value: 2 },
{ Title: 'Three', Value: 3 },
{ Title: 'Four', Value: 4 },
{ Title: 'Five', Value: 5 },
];
export default {
title: 'TagField/TagField',
component: TagField,
decorators: [],
tags: ['autodocs'],
parameters: {
docs: {
description: {
component: 'The Tag Field component.'
},
canvas: {
sourceState: 'shown',
},
}
},
argTypes: {
name: {
control: 'text',
table: {
type: { summary: 'string' },
defaultValue: { summary: null },
},
},
options: {
description: 'List of tags options',
control: 'select',
table: {
type: { summary: 'string' },
},
},
labelKey: {
control: 'text',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'Title' },
},
},
valueKey: {
control: 'text',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'Value' },
},
},
lazyLoad: {
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: false },
},
},
creatable: {
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: false },
},
},
multi: {
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: false },
},
},
disabled: {
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: false },
},
},
optionUrl: {
control: 'text',
table: {
type: { summary: 'string' },
defaultValue: { summary: null },
},
},
},
args: {
name: 'Test',
options
}
};
export const SimpleExample = (args) => (
<TagField
{...args}
/>
);
export const MultipleSelection = (args) => (
<TagField
{...args}
multi
/>
);

View File

@ -1,85 +1,64 @@
/* eslint-disable import/no-extraneous-dependencies */
/* global jest, describe, beforeEach, it, expect, setTimeout, document */
jest.mock('isomorphic-fetch');
/* global jest, test, describe, beforeEach, it, expect, setTimeout, document */
import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-15.4';
import { render } from '@testing-library/react';
import { Component as TagField } from '../TagField';
import Select from 'react-select';
import fetch from 'isomorphic-fetch';
Enzyme.configure({ adapter: new Adapter() });
function makeProps(obj = {}) {
return {
name: 'Test',
labelKey: 'label',
valueKey: 'value',
lazyLoad: false,
creatable: false,
multi: true,
SelectComponent: () => <div className="test-dynamic test-select" />,
CreatableSelectComponent: () => <div className="test-dynamic test-creatable-select" />,
AsyncSelectComponent: () => <div className="test-dynamic test-async-select" />,
AsyncCreatableSelectComponent: () => <div className="test-dynamic test-async-creatable-select" />,
...obj,
};
}
describe('TagField', () => {
let props;
beforeEach(() => {
props = {
name: 'Test',
labelKey: 'label',
valueKey: 'value',
lazyLoad: false,
creatable: false,
multi: true,
};
});
describe('should render a Select component with type', () => {
it('Select', () => {
const wrapper = shallow(
<TagField {...props} />
);
expect(wrapper.find(Select).length).toBe(1);
});
it('Select.Creatable with creatable option', () => {
props.creatable = true;
const wrapper = shallow(
<TagField {...props} />
);
expect(wrapper.find(Select.Creatable).length).toBe(1);
});
it('Select.Async with lazyLoad option', () => {
props.lazyLoad = true;
const wrapper = shallow(
<TagField {...props} />
);
expect(wrapper.find(Select.Async).length).toBe(1);
});
it('Select.AsyncCreatable with both creatable and lazyLoad options', () => {
props.creatable = true;
props.lazyLoad = true;
const wrapper = shallow(
<TagField {...props} />
);
expect(wrapper.find(Select.AsyncCreatable).length).toBe(1);
});
});
describe('with lazyLoad on and given a URL', () => {
let wrapper;
beforeEach(() => {
props.lazyLoad = true;
props.optionUrl = 'localhost/some-fetch-url';
wrapper = shallow(
<TagField {...props} />
);
fetch.mockImplementation(() => Promise.resolve({
json: () => ({}),
}));
});
it('should fetch the URL for results', done => {
wrapper.instance().getOptions('a');
setTimeout(() => {
expect(fetch).toBeCalledWith('localhost/some-fetch-url?term=a', expect.anything());
done();
}, 500);
});
});
test('TagField should render a Select component by default', () => {
const { container } = render(
<TagField {...makeProps()}/>
);
expect(container.querySelectorAll('.test-dynamic')).toHaveLength(1);
expect(container.querySelector('.test-select')).not.toBeNull();
});
test('TagField should render a CreatableSelect with creatable option', () => {
const { container } = render(
<TagField {...makeProps({
creatable: true
})}
/>
);
expect(container.querySelectorAll('.test-dynamic')).toHaveLength(1);
expect(container.querySelector('.test-creatable-select')).not.toBeNull();
});
test('Tagfiled should render an AsyncSelect with lazy load option', () => {
const { container } = render(
<TagField {...makeProps({
lazyLoad: true
})}
/>
);
expect(container.querySelectorAll('.test-dynamic')).toHaveLength(1);
expect(container.querySelector('.test-async-select')).not.toBeNull();
});
test('Tagfiled should render a AsyncCreatableSelect with lazy load and creatable options', () => {
const { container } = render(
<TagField {...makeProps({
lazyLoad: true,
creatable: true
})}
/>
);
expect(container.querySelectorAll('.test-dynamic')).toHaveLength(1);
expect(container.querySelector('.test-async-creatable-select')).not.toBeNull();
});

View File

@ -1,10 +1,12 @@
/* global window */
import React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { loadComponent } from 'lib/Injector';
window.jQuery.entwine('ss', ($) => {
$('.js-injector-boot .ss-tag-field.entwine').entwine({
ReactRoot: null,
onmatch() {
const cmsContent = this.closest('.cms-content').attr('id');
const context = (cmsContent)
@ -18,17 +20,25 @@ window.jQuery.entwine('ss', ($) => {
}
};
ReactDOM.render(
let root = this.getReactRoot();
if (!root) {
root = createRoot(this[0]);
this.setReactRoot(root);
}
root.render(
<TagField
noHolder
{...dataSchema}
/>,
this[0]
/>
);
},
onunmatch() {
ReactDOM.unmountComponentAtNode(this[0]);
const root = this.getReactRoot();
if (root) {
root.unmount();
this.setReactRoot(null);
}
}
});
@ -44,7 +54,7 @@ window.jQuery.entwine('ss', ($) => {
// clone the object (so we don't modify the original),
opts = $.extend({}, opts);
// modify it,
opts.ignoreFieldSelector += ', .ss-tag-field .Select :input';
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);

View File

@ -19,13 +19,13 @@
"issues": "http://github.com/silverstripe/silverstripe-tagfield/issues"
},
"require": {
"php": "^7.4 || ^8.0",
"silverstripe/framework": "^4.10",
"silverstripe/versioned": "^1.3"
"php": "^8.1",
"silverstripe/framework": "^5",
"silverstripe/versioned": "^2"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.0"
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "^3"
},
"autoload": {
"psr-4": {
@ -40,4 +40,4 @@
},
"minimum-stability": "dev",
"prefer-stable": true
}
}

View File

@ -1,8 +1,6 @@
{
"name": "silverstripe-tagfield",
"version": "2.2.0",
"description": "Tag field for SilverStripe",
"main": "./client/src/boot/index.js",
"license": "BSD-3-Clause",
"repository": {
"type": "git",
@ -14,10 +12,10 @@
},
"author": "SilverStripe Ltd.",
"engines": {
"node": ">=6.x"
"node": "^18.x"
},
"scripts": {
"build": "yarn && yarn lint && yarn test && NODE_ENV=production webpack -p --bail --progress",
"build": "yarn && yarn lint && yarn test && rm -rf client/dist/* && NODE_ENV=production webpack --mode production --bail --progress",
"dev": "NODE_ENV=development webpack --progress",
"watch": "NODE_ENV=development webpack --watch --progress",
"css": "WEBPACK_CHILD=css npm run build",
@ -29,7 +27,36 @@
"lint-js-fix": "eslint client/src --fix",
"lint-sass": "sass-lint client/src"
},
"dependencies": {
"classnames": "^2.3.2",
"debounce-promise": "^3.1.2",
"isomorphic-fetch": "^3.0.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-select": "^5.5.8",
"url": "^0.11.0"
},
"devDependencies": {
"@silverstripe/eslint-config": "^1.1.0",
"@silverstripe/webpack-config": "^2.0.0",
"@testing-library/react": "^14.0.0",
"babel-jest": "^29.2.2",
"copy-webpack-plugin": "^11.0.0",
"html-loader": "^4.2.0",
"jest-cli": "^29.2.2",
"jest-environment-jsdom": "^29.3.1",
"webpack": "^5.74.0",
"webpack-cli": "^5.0.0"
},
"resolutions": {
"colors": "1.4.0"
},
"browserslist": [
"defaults"
],
"jest": {
"testEnvironment": "jsdom",
"roots": [
"client/src"
],
@ -46,39 +73,5 @@
"transform": {
".*": "babel-jest"
}
},
"babel": {
"presets": [
"env",
"react"
],
"plugins": [
"transform-object-rest-spread"
]
},
"devDependencies": {
"@silverstripe/eslint-config": "^0.0.6",
"@silverstripe/webpack-config": "^0.10.0",
"@storybook/react": "^3.2.19",
"babel-jest": "^23.6.0",
"copy-webpack-plugin": "^4.2.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-15.4": "^1.0.5",
"html-loader": "^0.5.1",
"jest-cli": "^23.6.0",
"react-addons-test-utils": "15.3.1",
"webpack": "^2"
},
"dependencies": {
"babel-polyfill": "6.7.4",
"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",
"prop-types": "^15.7.2",
"react": "15.3.1",
"react-dom": "15.3.1",
"react-select": "^1.2.1",
"url": "^0.11.0"
}
}

View File

@ -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,7 +58,6 @@ class StringTagField extends DropdownField
*/
protected $isMultiple = true;
/** @skipUpgrade */
protected $schemaComponent = 'TagField';
/**
@ -281,13 +272,6 @@ 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();
}
}
/**
@ -395,7 +379,7 @@ class StringTagField extends DropdownField
*/
public function validate($validator)
{
return true;
return $this->extendValidationResult(true, $validator);
}
/**

View File

@ -63,7 +63,6 @@ class TagField extends MultiSelectField
*/
protected $isMultiple = true;
/** @skipUpgrade */
protected $schemaComponent = 'TagField';
/**
@ -497,7 +496,7 @@ class TagField extends MultiSelectField
}
// Create new instance if not yet saved
if ($this->getCanCreate()) {
if ($this->getCanCreate() && $value) {
$dataClass = $source->dataClass();
$record = Injector::inst()->create($dataClass);
$record->{$titleField} = $value;
@ -573,7 +572,7 @@ class TagField extends MultiSelectField
*/
public function validate($validator)
{
return true;
return $this->extendValidationResult(true, $validator);
}
/**

View File

@ -8,7 +8,6 @@ use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\TagField\StringTagField;
use SilverStripe\TagField\Tests\Stub\StringTagFieldTestBlogPost;
use SilverStripe\Dev\Deprecation;
class StringTagFieldTest extends SapphireTest
{
@ -62,44 +61,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'], []);

View File

@ -1,60 +1,31 @@
const Path = require('path');
const webpackConfig = require('@silverstripe/webpack-config');
const {
resolveJS,
externalJS,
moduleJS,
pluginJS,
moduleCSS,
pluginCSS,
} = webpackConfig;
const { JavascriptWebpackConfig, CssWebpackConfig } = require('@silverstripe/webpack-config');
const ENV = process.env.NODE_ENV;
const PATHS = {
MODULES: 'node_modules',
FILES_PATH: '../',
ROOT: Path.resolve(),
SRC: Path.resolve('client/src'),
DIST: Path.resolve('client/dist'),
};
const externals = externalJS(ENV, PATHS);
delete externals.reactstrap;
const config = [
{
name: 'js',
entry: {
// Main JS bundle
new JavascriptWebpackConfig('js', PATHS, 'silverstripe/tagfield')
.setEntry({
bundle: `${PATHS.SRC}/bundles/bundle.js`,
},
output: {
path: PATHS.DIST,
filename: 'js/[name].js',
},
devtool: (ENV !== 'production') ? 'source-map' : '',
resolve: resolveJS(ENV, PATHS),
externals,
module: moduleJS(ENV, PATHS),
plugins: pluginJS(ENV, PATHS),
},
{
name: 'css',
entry: {
})
.getConfig(),
// sass to css
new CssWebpackConfig('css', PATHS)
.setEntry({
bundle: `${PATHS.SRC}/styles/bundle.scss`,
},
output: {
path: PATHS.DIST,
filename: 'styles/[name].css',
},
devtool: (ENV !== 'production') ? 'source-map' : '',
module: moduleCSS(ENV, PATHS),
plugins: pluginCSS(ENV, PATHS),
},
})
.getConfig(),
];
// Use WEBPACK_CHILD=js or WEBPACK_CHILD=css env var to run a single config
module.exports = (process.env.WEBPACK_CHILD)
? config.find((entry) => entry.name === process.env.WEBPACK_CHILD)
: module.exports = config;
: config;

16055
yarn.lock

File diff suppressed because it is too large Load Diff