├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets ├── LICENSE ├── README.md ├── dist │ ├── controller.d.ts │ └── controller.js └── package.json ├── composer.json ├── config └── routes.php ├── src ├── AutocompleteBundle.php ├── AutocompleteResults.php ├── AutocompleteResultsExecutor.php ├── AutocompleterRegistry.php ├── Checksum │ └── ChecksumCalculator.php ├── Controller │ └── EntityAutocompleteController.php ├── DependencyInjection │ ├── AutocompleteExtension.php │ └── AutocompleteFormTypePass.php ├── Doctrine │ ├── DoctrineRegistryWrapper.php │ ├── EntityMetadata.php │ ├── EntityMetadataFactory.php │ ├── EntitySearchUtil.php │ └── SearchEscaper.php ├── EntityAutocompleterInterface.php ├── Form │ ├── AsEntityAutocompleteField.php │ ├── AutocompleteChoiceTypeExtension.php │ ├── AutocompleteEntityTypeSubscriber.php │ ├── BaseEntityAutocompleteType.php │ ├── ChoiceList │ │ └── Loader │ │ │ └── ExtraLazyChoiceLoader.php │ ├── ParentEntityAutocompleteType.php │ └── WrappedEntityTypeAutocompleter.php ├── Maker │ ├── MakeAutocompleteField.php │ ├── MakerAutocompleteVariables.php │ └── skeletons │ │ └── AutocompleteField.tpl.php └── OptionsAwareEntityAutocompleterInterface.php ├── templates └── autocomplete_form_theme.html.twig └── translations ├── AutocompleteBundle.ar.php ├── AutocompleteBundle.bg.php ├── AutocompleteBundle.ca.php ├── AutocompleteBundle.cs.php ├── AutocompleteBundle.da.php ├── AutocompleteBundle.de.php ├── AutocompleteBundle.el.php ├── AutocompleteBundle.en.php ├── AutocompleteBundle.es.php ├── AutocompleteBundle.eu.php ├── AutocompleteBundle.fa.php ├── AutocompleteBundle.fi.php ├── AutocompleteBundle.fr.php ├── AutocompleteBundle.gl.php ├── AutocompleteBundle.hr.php ├── AutocompleteBundle.hu.php ├── AutocompleteBundle.id.php ├── AutocompleteBundle.it.php ├── AutocompleteBundle.lb.php ├── AutocompleteBundle.lt.php ├── AutocompleteBundle.nl.php ├── AutocompleteBundle.pl.php ├── AutocompleteBundle.pt.php ├── AutocompleteBundle.pt_BR.php ├── AutocompleteBundle.ro.php ├── AutocompleteBundle.ru.php ├── AutocompleteBundle.sk.php ├── AutocompleteBundle.sl.php ├── AutocompleteBundle.sr_RS.php ├── AutocompleteBundle.sv.php ├── AutocompleteBundle.tr.php ├── AutocompleteBundle.uk.php └── AutocompleteBundle.zh_CN.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 2.25.0 4 | 5 | - Escape `querySelector` dynamic selector with `CSS.escape()` #2663 6 | 7 | ## 2.23.0 8 | 9 | - Deprecate `ExtraLazyChoiceLoader` in favor of `Symfony\Component\Form\ChoiceList\Loader\LazyChoiceLoader` 10 | - Reset TomSelect when updating url attribute #1505 11 | - Add `getAttributes()` method to define additional attributes for autocomplete results #2541 12 | 13 | ## 2.22.0 14 | 15 | - Take `labelField` TomSelect option into account #2382 16 | 17 | ## 2.21.0 18 | 19 | - Translate the `option_create` option from TomSelect with remote data setup #2279 20 | - Add one missing Dutch translation #2279 21 | 22 | ## 2.20.0 23 | 24 | - Translate the `option_create` option from TomSelect #2108 25 | 26 | ## 2.17.0 27 | 28 | - Allow `choice_value` option in entity autocomplete fields #1723 29 | 30 | ## 2.16.0 31 | 32 | - Missing translations added for many languages #1527 #1528 #1535 33 | 34 | ## 2.15.0 35 | 36 | - Add doctrine/orm 3 support #1468 37 | - Allow passing extra options to the autocomplete fields #1322 38 | - Fix 2 bugs where TomSelect would reset when not necessary #1502 39 | - Add one missing German translation #1521 40 | 41 | ## 2.14.0 42 | 43 | - Fixed behavior of Autocomplete when the underlying `select` or `option` 44 | elements were modified to hopefully, more reliably, reset the autocomplete 45 | instance. This is particularly important with LiveComponents. 46 | - Add support for the `render.loading_more` Tom Select Virtual Scroll option (`loading_more_text`) 47 | - Avoid losing the selected options when the Stimulus component is disconnected 48 | and reconnected to the DOM. 49 | - Added `tom-select/dist/css/tom-select.bootstrap4.css` to `autoimport` - this 50 | will cause this to appear in your `controllers.json` file by default, but disabled 51 | see. 52 | - Allow passing `extra_options` key in an array passed as a `3rd` argument of the `->add()` method. 53 | It will be used during the Ajax call to fetch results. 54 | 55 | ## 2.13.2 56 | 57 | - Revert "Change JavaScript package to `type: module`" 58 | 59 | ## 2.13.0 60 | 61 | - Add new BaseEntityAutocompleteType 62 | - Drop symfony 5.4 support. 63 | - Add Symfony 7 support. 64 | - Change JavaScript package to `type: module` 65 | 66 | ## 2.9.0 67 | 68 | - Add support for symfony/asset-mapper 69 | 70 | ## 2.8.0 71 | 72 | - The autocomplete now watches for update to any `option` elements inside of 73 | it change, including the empty / placeholder element. Additionally, if the 74 | `select` or `input` element's `disabled` attribute changes, the autocomplete 75 | instance will update accordingly. This makes Autocomplete work correctly inside 76 | of a LiveComponent. This functionality does _not_ work for `multiple` selects. 77 | 78 | - Added support for using [OptionGroups](https://tom-select.js.org/examples/optgroups/). 79 | 80 | ## 2.7.0 81 | 82 | - Add `assets/src` to `.gitattributes` to exclude them from the installation 83 | 84 | - Fix minCharacters option default value handling when using a falsy value like 0. 85 | 86 | - Fix TypeScript types 87 | 88 | - Add a new `route` parameter to `AsEntityAutocompleteField`, which allows to choose another route for Ajax calls. 89 | 90 | - Fix minCharacters option default value handling when using a falsy value like 0. 91 | 92 | - Fix TypeScript types 93 | 94 | - Add a way to detect if a field is an "autocomplete" field in form themes - #608 95 | 96 | ## 2.6.0 97 | 98 | - [BC BREAK]: The path to `routes.php` changed and you should update your 99 | route import accordingly: 100 | 101 | ```diff 102 | # config/routes/ux_autocomplete.yaml 103 | ux_autocomplete: 104 | - resource: '@AutocompleteBundle/Resources/routes.php' 105 | + resource: '@AutocompleteBundle/config/routes.php' 106 | prefix: '/autocomplete' 107 | ``` 108 | 109 | - Add support for `tom-select` version `2.2.2` and made this the minimum-supported 110 | version. 111 | - Added support for the `preload` TomSelect option. 112 | - Fix don't add WHERE IN criteria without params (#561). 113 | - Fix issue where `max_results` was not passed as a Stimulus value (#538). 114 | - Add all possible stylesheets for tom-select to the autoimport to choose from. 115 | 116 | ## 2.5.0 117 | 118 | - Automatic pagination support added: if the query would return more results 119 | than your limit, when the user scrolls to the bottom of the options, it will 120 | make a second Ajax call to load more. 121 | 122 | - Added `max_results` option to limit the number of results returned by the 123 | Ajax endpoint - #478. 124 | 125 | - Support added for setting the required minimum search query length (defaults to 3) (#492) 126 | 127 | - Fix support for more complex ids, like UUIDs - #494. 128 | 129 | - Fixed bug where sometimes an error could occur in the Ajax call related to 130 | the label - #520. 131 | 132 | ## 2.4.0 133 | 134 | - Component added! 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present Fabien Potencier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Symfony UX Autocomplete 2 | 3 | Javascript-powered auto-completion functionality for your Symfony forms! 4 | 5 | **This repository is a READ-ONLY sub-tree split**. See 6 | https://github.com/symfony/ux to create issues or submit pull requests. 7 | 8 | ## Sponsor 9 | 10 | The Symfony UX packages are [backed][1] by [Mercure.rocks][2]. 11 | 12 | Create real-time experiences in minutes! Mercure.rocks provides a realtime API service 13 | that is tightly integrated with Symfony: create UIs that update in live with UX Turbo, 14 | send notifications with the Notifier component, expose async APIs with API Platform and 15 | create low level stuffs with the Mercure component. We maintain and scale the complex 16 | infrastructure for you! 17 | 18 | Help Symfony by [sponsoring][3] its development! 19 | 20 | ## Resources 21 | 22 | - [Documentation](https://symfony.com/bundles/ux-autocomplete/current/index.html) 23 | - [Report issues](https://github.com/symfony/ux/issues) and 24 | [send Pull Requests](https://github.com/symfony/ux/pulls) 25 | in the [main Symfony UX repository](https://github.com/symfony/ux) 26 | 27 | [1]: https://symfony.com/backers 28 | [2]: https://mercure.rocks 29 | [3]: https://symfony.com/sponsor 30 | -------------------------------------------------------------------------------- /assets/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present Fabien Potencier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # @symfony/ux-autocomplete 2 | 3 | JavaScript assets of the [symfony/ux-autocomplete](https://packagist.org/packages/symfony/ux-autocomplete) PHP package. 4 | 5 | ## Installation 6 | 7 | This npm package is **reserved for advanced users** who want to decouple their JavaScript dependencies from their PHP dependencies (e.g., when building Docker images, running JavaScript-only pipelines, etc.). 8 | 9 | We **strongly recommend not installing this package directly**, but instead install the PHP package [symfony/ux-autocomplete](https://packagist.org/packages/symfony/ux-autocomplete) in your Symfony application with [Flex](https://github.com/symfony/flex) enabled. 10 | 11 | If you still want to install this package directly, please make sure its version exactly matches [symfony/ux-autocomplete](https://packagist.org/packages/symfony/ux-autocomplete) PHP package version: 12 | ```shell 13 | composer require symfony/ux-autocomplete:2.23.0 14 | npm add @symfony/ux-autocomplete@2.23.0 15 | ``` 16 | 17 | **Tip:** Your `package.json` file will be automatically modified by [Flex](https://github.com/symfony/flex) when installing or upgrading a PHP package. To prevent this behavior, ensure to **use at least Flex 1.22.0 or 2.5.0**, and run `composer config extra.symfony.flex.synchronize_package_json false`. 18 | 19 | ## Resources 20 | 21 | - [Documentation](https://symfony.com/bundles/ux-autocomplete/current/index.html) 22 | - [Report issues](https://github.com/symfony/ux/issues) and 23 | [send Pull Requests](https://github.com/symfony/ux/pulls) 24 | in the [main Symfony UX repository](https://github.com/symfony/ux) 25 | -------------------------------------------------------------------------------- /assets/dist/controller.d.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@hotwired/stimulus'; 2 | import TomSelect from 'tom-select'; 3 | export interface AutocompletePreConnectOptions { 4 | options: any; 5 | } 6 | export interface AutocompleteConnectOptions { 7 | tomSelect: TomSelect; 8 | options: any; 9 | } 10 | export default class extends Controller { 11 | #private; 12 | static values: { 13 | url: StringConstructor; 14 | optionsAsHtml: BooleanConstructor; 15 | loadingMoreText: StringConstructor; 16 | noResultsFoundText: StringConstructor; 17 | noMoreResultsText: StringConstructor; 18 | createOptionText: StringConstructor; 19 | minCharacters: NumberConstructor; 20 | tomSelectOptions: ObjectConstructor; 21 | preload: StringConstructor; 22 | }; 23 | readonly urlValue: string; 24 | readonly optionsAsHtmlValue: boolean; 25 | readonly loadingMoreTextValue: string; 26 | readonly noMoreResultsTextValue: string; 27 | readonly noResultsFoundTextValue: string; 28 | readonly createOptionTextValue: string; 29 | readonly minCharactersValue: number; 30 | readonly hasMinCharactersValue: boolean; 31 | readonly tomSelectOptionsValue: object; 32 | readonly hasPreloadValue: boolean; 33 | readonly preloadValue: string; 34 | tomSelect: TomSelect; 35 | private mutationObserver; 36 | private isObserving; 37 | private hasLoadedChoicesPreviously; 38 | private originalOptions; 39 | initialize(): void; 40 | connect(): void; 41 | initializeTomSelect(): void; 42 | disconnect(): void; 43 | urlValueChanged(): void; 44 | private getMaxOptions; 45 | get selectElement(): HTMLSelectElement | null; 46 | get formElement(): HTMLInputElement | HTMLSelectElement; 47 | private dispatchEvent; 48 | get preload(): string | boolean; 49 | private resetTomSelect; 50 | private changeTomSelectDisabledState; 51 | private startMutationObserver; 52 | private stopMutationObserver; 53 | private onMutations; 54 | private createOptionsDataStructure; 55 | private areOptionsEquivalent; 56 | } 57 | -------------------------------------------------------------------------------- /assets/dist/controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from '@hotwired/stimulus'; 2 | import TomSelect from 'tom-select'; 3 | 4 | /****************************************************************************** 5 | Copyright (c) Microsoft Corporation. 6 | 7 | Permission to use, copy, modify, and/or distribute this software for any 8 | purpose with or without fee is hereby granted. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 11 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 13 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 15 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 | PERFORMANCE OF THIS SOFTWARE. 17 | ***************************************************************************** */ 18 | /* global Reflect, Promise, SuppressedError, Symbol */ 19 | 20 | 21 | function __classPrivateFieldGet(receiver, state, kind, f) { 22 | if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); 23 | if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); 24 | return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); 25 | } 26 | 27 | typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { 28 | var e = new Error(message); 29 | return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; 30 | }; 31 | 32 | 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; 33 | class default_1 extends Controller { 34 | constructor() { 35 | super(...arguments); 36 | _default_1_instances.add(this); 37 | this.isObserving = false; 38 | this.hasLoadedChoicesPreviously = false; 39 | this.originalOptions = []; 40 | } 41 | initialize() { 42 | if (!this.mutationObserver) { 43 | this.mutationObserver = new MutationObserver((mutations) => { 44 | this.onMutations(mutations); 45 | }); 46 | } 47 | } 48 | connect() { 49 | if (this.selectElement) { 50 | this.originalOptions = this.createOptionsDataStructure(this.selectElement); 51 | } 52 | this.initializeTomSelect(); 53 | } 54 | initializeTomSelect() { 55 | if (this.selectElement) { 56 | this.selectElement.setAttribute('data-skip-morph', ''); 57 | } 58 | if (this.urlValue) { 59 | this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithRemoteData).call(this, this.urlValue, this.hasMinCharactersValue ? this.minCharactersValue : null); 60 | return; 61 | } 62 | if (this.optionsAsHtmlValue) { 63 | this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithHtmlContents).call(this); 64 | return; 65 | } 66 | this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocomplete).call(this); 67 | this.startMutationObserver(); 68 | } 69 | disconnect() { 70 | this.stopMutationObserver(); 71 | let currentSelectedValues = []; 72 | if (this.selectElement) { 73 | if (this.selectElement.multiple) { 74 | currentSelectedValues = Array.from(this.selectElement.options) 75 | .filter((option) => option.selected) 76 | .map((option) => option.value); 77 | } 78 | else { 79 | currentSelectedValues = [this.selectElement.value]; 80 | } 81 | } 82 | this.tomSelect.destroy(); 83 | if (this.selectElement) { 84 | if (this.selectElement.multiple) { 85 | Array.from(this.selectElement.options).forEach((option) => { 86 | option.selected = currentSelectedValues.includes(option.value); 87 | }); 88 | } 89 | else { 90 | this.selectElement.value = currentSelectedValues[0]; 91 | } 92 | } 93 | } 94 | urlValueChanged() { 95 | this.resetTomSelect(); 96 | } 97 | getMaxOptions() { 98 | return this.selectElement ? this.selectElement.options.length : 50; 99 | } 100 | get selectElement() { 101 | if (!(this.element instanceof HTMLSelectElement)) { 102 | return null; 103 | } 104 | return this.element; 105 | } 106 | get formElement() { 107 | if (!(this.element instanceof HTMLInputElement) && !(this.element instanceof HTMLSelectElement)) { 108 | throw new Error('Autocomplete Stimulus controller can only be used on an or