diff --git a/rollup.config.js b/rollup.config.js
index ce8fe67d42b..b930b6505ac 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -38,7 +38,7 @@ const wildcardExternalsPlugin = (peerDependencies) => ({
}
});
-const files = glob.sync("src/React/assets/src/*controller.ts");
+const files = glob.sync('src/*/assets/src/*controller.ts');
const packages = files.map((file) => {
const absolutePath = path.join(__dirname, file);
const packageData = require(pkgUp.sync({ cwd: absolutePath }));
diff --git a/src/Autocomplete/CHANGELOG.md b/src/Autocomplete/CHANGELOG.md
index 5c07c3da3e9..fe58c2d3860 100644
--- a/src/Autocomplete/CHANGELOG.md
+++ b/src/Autocomplete/CHANGELOG.md
@@ -1,9 +1,5 @@
# CHANGELOG
-## Unreleased
-
-- Add support for recent versions of Tom Select
-
## 2.6.0
- [BC BREAK]: The path to `routes.php` changed and you should update your
@@ -17,8 +13,12 @@ ux_autocomplete:
prefix: '/autocomplete'
```
-- Fix issue where `max_results` was not passed as a Stimulus value (#538)
-- Add all possible stylesheets for tom-select to the autoimport to choose from
+- Add support for `tom-select` version `2.2.2` and made this the minimum-supported
+ version.
+- Added support for the `preload` TomSelect option.
+- Fix don't add WHERE IN criteria without params (#561).
+- Fix issue where `max_results` was not passed as a Stimulus value (#538).
+- Add all possible stylesheets for tom-select to the autoimport to choose from.
## 2.5.0
diff --git a/src/Autocomplete/assets/dist/controller.js b/src/Autocomplete/assets/dist/controller.js
index 659be6669a0..547aa63d04d 100644
--- a/src/Autocomplete/assets/dist/controller.js
+++ b/src/Autocomplete/assets/dist/controller.js
@@ -22,11 +22,11 @@ function __classPrivateFieldGet(receiver, state, kind, f) {
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
}
-var _default_1_instances, _default_1_getCommonConfig, _default_1_createAutocomplete, _default_1_createAutocompleteWithHtmlContents, _default_1_createAutocompleteWithRemoteData, _default_1_stripTags, _default_1_mergeObjects, _default_1_createTomSelect, _default_1_dispatchEvent;
+var _instances, _getCommonConfig, _createAutocomplete, _createAutocompleteWithHtmlContents, _createAutocompleteWithRemoteData, _stripTags, _mergeObjects, _createTomSelect, _dispatchEvent;
class default_1 extends Controller {
constructor() {
super(...arguments);
- _default_1_instances.add(this);
+ _instances.add(this);
}
initialize() {
this.element.setAttribute('data-live-ignore', '');
@@ -39,14 +39,14 @@ class default_1 extends Controller {
}
connect() {
if (this.urlValue) {
- this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithRemoteData).call(this, this.urlValue, this.minCharactersValue);
+ this.tomSelect = __classPrivateFieldGet(this, _instances, "m", _createAutocompleteWithRemoteData).call(this, this.urlValue, this.minCharactersValue);
return;
}
if (this.optionsAsHtmlValue) {
- this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithHtmlContents).call(this);
+ this.tomSelect = __classPrivateFieldGet(this, _instances, "m", _createAutocompleteWithHtmlContents).call(this);
return;
}
- this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocomplete).call(this);
+ this.tomSelect = __classPrivateFieldGet(this, _instances, "m", _createAutocomplete).call(this);
}
disconnect() {
this.tomSelect.revertSettings.innerHTML = this.element.innerHTML;
@@ -65,14 +65,19 @@ class default_1 extends Controller {
return this.element;
}
get preload() {
- if (this.preloadValue == 'false')
+ if (!this.hasPreloadValue) {
+ return 'focus';
+ }
+ if (this.preloadValue == 'false') {
return false;
- if (this.preloadValue == 'true')
+ }
+ if (this.preloadValue == 'true') {
return true;
+ }
return this.preloadValue;
}
}
-_default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _default_1_getCommonConfig() {
+_instances = new WeakSet(), _getCommonConfig = function _getCommonConfig() {
const plugins = {};
const isMultiple = !this.selectElement || this.selectElement.multiple;
if (!this.formElement.disabled && !isMultiple) {
@@ -87,7 +92,7 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
const render = {
no_results: () => {
return `
${this.noResultsFoundTextValue}
`;
- }
+ },
};
const config = {
render: render,
@@ -104,19 +109,19 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
if (!this.selectElement && !this.urlValue) {
config.shouldLoad = () => false;
}
- return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeObjects).call(this, config, this.tomSelectOptionsValue);
-}, _default_1_createAutocomplete = function _default_1_createAutocomplete() {
- const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeObjects).call(this, __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this), {
+ return __classPrivateFieldGet(this, _instances, "m", _mergeObjects).call(this, config, this.tomSelectOptionsValue);
+}, _createAutocomplete = function _createAutocomplete() {
+ const config = __classPrivateFieldGet(this, _instances, "m", _mergeObjects).call(this, __classPrivateFieldGet(this, _instances, "m", _getCommonConfig).call(this), {
maxOptions: this.selectElement ? this.selectElement.options.length : 50,
});
- return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config);
-}, _default_1_createAutocompleteWithHtmlContents = function _default_1_createAutocompleteWithHtmlContents() {
- const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeObjects).call(this, __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this), {
+ return __classPrivateFieldGet(this, _instances, "m", _createTomSelect).call(this, config);
+}, _createAutocompleteWithHtmlContents = function _createAutocompleteWithHtmlContents() {
+ const config = __classPrivateFieldGet(this, _instances, "m", _mergeObjects).call(this, __classPrivateFieldGet(this, _instances, "m", _getCommonConfig).call(this), {
maxOptions: this.selectElement ? this.selectElement.options.length : 50,
score: (search) => {
const scoringFunction = this.tomSelect.getScoreFunction(search);
return (item) => {
- return scoringFunction(Object.assign(Object.assign({}, item), { text: __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_stripTags).call(this, item.text) }));
+ return scoringFunction(Object.assign(Object.assign({}, item), { text: __classPrivateFieldGet(this, _instances, "m", _stripTags).call(this, item.text) }));
};
},
render: {
@@ -125,12 +130,12 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
},
option: function (item) {
return `${item.text}
`;
- }
+ },
},
});
- return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config);
-}, _default_1_createAutocompleteWithRemoteData = function _default_1_createAutocompleteWithRemoteData(autocompleteEndpointUrl, minCharacterLength) {
- const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeObjects).call(this, __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this), {
+ return __classPrivateFieldGet(this, _instances, "m", _createTomSelect).call(this, config);
+}, _createAutocompleteWithRemoteData = function _createAutocompleteWithRemoteData(autocompleteEndpointUrl, minCharacterLength) {
+ const config = __classPrivateFieldGet(this, _instances, "m", _mergeObjects).call(this, __classPrivateFieldGet(this, _instances, "m", _getCommonConfig).call(this), {
firstUrl: (query) => {
const separator = autocompleteEndpointUrl.includes('?') ? '&' : '?';
return `${autocompleteEndpointUrl}${separator}query=${encodeURIComponent(query)}`;
@@ -138,8 +143,11 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
load: function (query, callback) {
const url = this.getUrl(query);
fetch(url)
- .then(response => response.json())
- .then(json => { this.setNextUrl(query, json.next_page); callback(json.results); })
+ .then((response) => response.json())
+ .then((json) => {
+ this.setNextUrl(query, json.next_page);
+ callback(json.results);
+ })
.catch(() => callback());
},
shouldLoad: function (query) {
@@ -167,17 +175,17 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
},
preload: this.preload,
});
- return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config);
-}, _default_1_stripTags = function _default_1_stripTags(string) {
+ return __classPrivateFieldGet(this, _instances, "m", _createTomSelect).call(this, config);
+}, _stripTags = function _stripTags(string) {
return string.replace(/(<([^>]+)>)/gi, '');
-}, _default_1_mergeObjects = function _default_1_mergeObjects(object1, object2) {
+}, _mergeObjects = function _mergeObjects(object1, object2) {
return Object.assign(Object.assign({}, object1), object2);
-}, _default_1_createTomSelect = function _default_1_createTomSelect(options) {
- __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_dispatchEvent).call(this, 'autocomplete:pre-connect', { options });
+}, _createTomSelect = function _createTomSelect(options) {
+ __classPrivateFieldGet(this, _instances, "m", _dispatchEvent).call(this, 'autocomplete:pre-connect', { options });
const tomSelect = new TomSelect(this.formElement, options);
- __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_dispatchEvent).call(this, 'autocomplete:connect', { tomSelect, options });
+ __classPrivateFieldGet(this, _instances, "m", _dispatchEvent).call(this, 'autocomplete:connect', { tomSelect, options });
return tomSelect;
-}, _default_1_dispatchEvent = function _default_1_dispatchEvent(name, payload) {
+}, _dispatchEvent = function _dispatchEvent(name, payload) {
this.element.dispatchEvent(new CustomEvent(name, { detail: payload, bubbles: true }));
};
default_1.values = {
diff --git a/src/Autocomplete/assets/package.json b/src/Autocomplete/assets/package.json
index 3c6e0526e65..d47facf1cc6 100644
--- a/src/Autocomplete/assets/package.json
+++ b/src/Autocomplete/assets/package.json
@@ -14,7 +14,6 @@
"enabled": true,
"autoimport": {
"tom-select/dist/css/tom-select.default.css": true,
- "tom-select/dist/css/tom-select.bootstrap4.css": false,
"tom-select/dist/css/tom-select.bootstrap5.css": false
}
}
@@ -22,7 +21,7 @@
},
"peerDependencies": {
"@hotwired/stimulus": "^3.0.0",
- "tom-select": "^2.0.1"
+ "tom-select": "^2.2.2"
},
"devDependencies": {
"@hotwired/stimulus": "^3.0.0",
diff --git a/src/Autocomplete/assets/src/controller.ts b/src/Autocomplete/assets/src/controller.ts
index 24c50850a52..c83922162bd 100644
--- a/src/Autocomplete/assets/src/controller.ts
+++ b/src/Autocomplete/assets/src/controller.ts
@@ -19,6 +19,7 @@ export default class extends Controller {
readonly noResultsFoundTextValue: string;
readonly minCharactersValue: number;
readonly tomSelectOptionsValue: object;
+ readonly hasPreloadValue: boolean;
readonly preloadValue: string;
tomSelect: TomSelect;
@@ -225,6 +226,10 @@ export default class extends Controller {
}
get preload() {
+ if (!this.hasPreloadValue) {
+ return 'focus';
+ }
+
if (this.preloadValue == 'false') {
return false;
}
diff --git a/src/Autocomplete/doc/index.rst b/src/Autocomplete/doc/index.rst
index ea1acc8ae08..2b0a7e790aa 100644
--- a/src/Autocomplete/doc/index.rst
+++ b/src/Autocomplete/doc/index.rst
@@ -167,28 +167,16 @@ Styling Tom Select
In your ``assets/controllers.json`` file, you should see a line that automatically
includes a CSS file for Tom Select which will give you basic styles.
-.. code-block:: text
-
- "autoimport": {
- "tom-select/dist/css/tom-select.default.css": true
- }
-
-If you're using Bootstrap, you can get Bootstrap-ready styling by
-changing this line to ``false``:
+If you're using Bootstrap, set ``tom-select.default.css`` to false
+and ``tom-select.bootstrap5.css`` to true:
.. code-block:: text
"autoimport": {
- "tom-select/dist/css/tom-select.default.css": false
+ "tom-select/dist/css/tom-select.default.css": false,
+ "tom-select/dist/css/tom-select.bootstrap5.css": true
}
-And then importing the Bootstrap CSS file:
-
-.. code-block:: css
-
- /* assets/styles/app.css */
- @import 'tom-select/dist/css/tom-select.bootstrap5.css';
-
To further customize things, you can override the classes with your own custom
CSS and even control how individual parts of Tom Select render. See `Tom Select Render Templates`_.
@@ -382,7 +370,7 @@ This only works for Doctrine entities: see `Manually using the Stimulus Controll
if you're autocompleting something other than an entity.
To expose the endpoint, create a class that implements ``Symfony\UX\Autocomplete\EntityAutocompleterInterface``
-and tag this service with ``ux.entity_autocompleter`` and include an ``alias``::
+and tag this service with ``ux.entity_autocompleter``, including an ``alias`` option::
namespace App\Autocompleter;
diff --git a/src/LiveComponent/CHANGELOG.md b/src/LiveComponent/CHANGELOG.md
index 3d35f84b8c2..b8461efee92 100644
--- a/src/LiveComponent/CHANGELOG.md
+++ b/src/LiveComponent/CHANGELOG.md
@@ -14,6 +14,9 @@ live_component:
+ prefix: /_components
```
+- Removed `Content-Type` header when returning the empty response redirect.
+- Fixed bug when re-rendering SVG's (.#557)
+
## 2.5.0
- [BEHAVIOR CHANGE] Previously, Ajax calls could happen in parallel (if
diff --git a/src/LiveComponent/assets/dist/live_controller.js b/src/LiveComponent/assets/dist/live_controller.js
index d812af0f6d0..ee6267767df 100644
--- a/src/LiveComponent/assets/dist/live_controller.js
+++ b/src/LiveComponent/assets/dist/live_controller.js
@@ -144,13 +144,13 @@ function combineSpacedArray(parts) {
return finalParts;
}
function normalizeModelName(model) {
- return model
+ return (model
.replace(/\[]$/, '')
.split('[')
.map(function (s) {
return s.replace(']', '');
})
- .join('.');
+ .join('.'));
}
function getValueFromElement(element, valueStore) {
@@ -170,7 +170,7 @@ function getValueFromElement(element, valueStore) {
}
if (element instanceof HTMLSelectElement) {
if (element.multiple) {
- return Array.from(element.selectedOptions).map(el => el.value);
+ return Array.from(element.selectedOptions).map((el) => el.value);
}
return element.value;
}
@@ -197,7 +197,7 @@ function setValueOnElement(element, value) {
if (element.type === 'checkbox') {
if (Array.isArray(value)) {
let valueFound = false;
- value.forEach(val => {
+ value.forEach((val) => {
if (val == element.value) {
valueFound = true;
}
@@ -211,10 +211,10 @@ function setValueOnElement(element, value) {
}
}
if (element instanceof HTMLSelectElement) {
- const arrayWrappedValue = [].concat(value).map(value => {
+ const arrayWrappedValue = [].concat(value).map((value) => {
return value + '';
});
- Array.from(element.options).forEach(option => {
+ Array.from(element.options).forEach((option) => {
option.selected = arrayWrappedValue.includes(option.value);
});
return;
@@ -242,7 +242,7 @@ function getModelDirectiveFromElement(element, throwOnMissing = true) {
}
if (element.getAttribute('name')) {
const formElement = element.closest('form');
- if (formElement && ('model' in formElement.dataset)) {
+ if (formElement && 'model' in formElement.dataset) {
const directives = parseDirectives(formElement.dataset.model || '*');
const directive = directives[0];
if (directive.args.length > 0 || directive.named.length > 0) {
@@ -301,13 +301,13 @@ function cloneElementWithNewTagName(element, newTag) {
const endRX = new RegExp(originalTag + '>$', 'i');
const startSubst = '<' + newTag;
const endSubst = newTag + '>';
- const newHTML = element.outerHTML
- .replace(startRX, startSubst)
- .replace(endRX, endSubst);
+ const newHTML = element.outerHTML.replace(startRX, startSubst).replace(endRX, endSubst);
return htmlToElement(newHTML);
}
function getElementAsTagText(element) {
- return element.innerHTML ? element.outerHTML.slice(0, element.outerHTML.indexOf(element.innerHTML)) : element.outerHTML;
+ return element.innerHTML
+ ? element.outerHTML.slice(0, element.outerHTML.indexOf(element.innerHTML))
+ : element.outerHTML;
}
const getMultipleCheckboxValue = function (element, currentValues) {
const value = inputValue(element);
@@ -346,7 +346,7 @@ const parseDeepData = function (data, propertyPath) {
currentLevelData,
finalData,
finalKey,
- parts
+ parts,
};
};
function setDeepData(data, propertyPath, value) {
@@ -1208,7 +1208,8 @@ function executeMorphdom(rootFromElement, rootToElement, modifiedFieldElements,
if (fromEl === rootFromElement) {
return true;
}
- if (!(fromEl instanceof HTMLElement || fromEl instanceof SVGElement) || !(toEl instanceof HTMLElement || toEl instanceof SVGElement)) {
+ if (!(fromEl instanceof HTMLElement || fromEl instanceof SVGElement) ||
+ !(toEl instanceof HTMLElement || toEl instanceof SVGElement)) {
return false;
}
const childComponent = childComponentMap.get(fromEl) || false;
@@ -1234,7 +1235,7 @@ function executeMorphdom(rootFromElement, rootToElement, modifiedFieldElements,
return true;
}
return !node.hasAttribute('data-live-ignore');
- }
+ },
});
}
@@ -1682,10 +1683,10 @@ class BackendRequest {
this.updatedModels = updateModels;
}
containsOneOfActions(targetedActions) {
- return (this.actions.filter(action => targetedActions.includes(action))).length > 0;
+ return this.actions.filter((action) => targetedActions.includes(action)).length > 0;
}
areAnyModelsUpdated(targetedModels) {
- return (this.updatedModels.filter(model => targetedModels.includes(model))).length > 0;
+ return this.updatedModels.filter((model) => targetedModels.includes(model)).length > 0;
}
}
@@ -1701,11 +1702,12 @@ class Backend {
const params = new URLSearchParams(queryString || '');
const fetchOptions = {};
fetchOptions.headers = {
- 'Accept': 'application/vnd.live-component+html',
+ Accept: 'application/vnd.live-component+html',
};
const hasFingerprints = Object.keys(childrenFingerprints).length > 0;
const hasUpdatedModels = Object.keys(updatedModels).length > 0;
- if (actions.length === 0 && this.willDataFitInUrl(JSON.stringify(data), params, JSON.stringify(childrenFingerprints))) {
+ if (actions.length === 0 &&
+ this.willDataFitInUrl(JSON.stringify(data), params, JSON.stringify(childrenFingerprints))) {
params.set('data', JSON.stringify(data));
if (hasFingerprints) {
params.set('childrenFingerprints', JSON.stringify(childrenFingerprints));
@@ -2312,7 +2314,7 @@ class default_1 extends Controller {
return this.element.dispatchEvent(new CustomEvent(name, {
bubbles: canBubble,
cancelable,
- detail
+ detail,
}));
}
}
diff --git a/src/LiveComponent/tests/Fixtures/Kernel.php b/src/LiveComponent/tests/Fixtures/Kernel.php
index 4af38d8a2b5..b630f76ca14 100644
--- a/src/LiveComponent/tests/Fixtures/Kernel.php
+++ b/src/LiveComponent/tests/Fixtures/Kernel.php
@@ -51,7 +51,6 @@ public function registerBundles(): iterable
yield new FrameworkBundle();
yield new TwigBundle();
yield new DoctrineBundle();
- yield new ZenstruckFoundryBundle();
yield new TwigComponentBundle();
yield new LiveComponentBundle();
yield new ZenstruckFoundryBundle();
diff --git a/src/React/assets/dist/render_controller.js b/src/React/assets/dist/render_controller.js
index 6009b0f7744..3d45be3c826 100644
--- a/src/React/assets/dist/render_controller.js
+++ b/src/React/assets/dist/render_controller.js
@@ -24,6 +24,9 @@ class default_1 extends Controller {
connect() {
const props = this.propsValue ? this.propsValue : null;
this._dispatchEvent('react:connect', { component: this.componentValue, props: props });
+ if (!this.componentValue) {
+ throw new Error('No component specified.');
+ }
const component = window.resolveReactComponent(this.componentValue);
this._renderReactElement(React.createElement(component, props, null));
this._dispatchEvent('react:mount', {