Skip to content

[Live] Smart Rendering! Mix/match live components with external JavaScript #728

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Autocomplete/assets/dist/controller.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { Controller } from '@hotwired/stimulus';
import TomSelect from 'tom-select';
export interface AutocompletePreConnectOptions {
options: any;
}
export interface AutocompleteConnectOptions {
tomSelect: TomSelect;
options: any;
}
export default class extends Controller {
#private;
static values: {
Expand All @@ -23,11 +30,20 @@ export default class extends Controller {
readonly hasPreloadValue: boolean;
readonly preloadValue: string;
tomSelect: TomSelect;
private mutationObserver;
private isObserving;
initialize(): void;
connect(): void;
disconnect(): void;
private getMaxOptions;
get selectElement(): HTMLSelectElement | null;
get formElement(): HTMLInputElement | HTMLSelectElement;
private dispatchEvent;
get preload(): string | boolean;
private resetTomSelect;
private changeTomSelectDisabledState;
private updateTomSelectPlaceholder;
private startMutationObserver;
private stopMutationObserver;
private onMutations;
}
148 changes: 133 additions & 15 deletions src/Autocomplete/assets/dist/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ class default_1 extends Controller {
constructor() {
super(...arguments);
_default_1_instances.add(this);
this.isObserving = false;
}
initialize() {
this.element.setAttribute('data-live-ignore', '');
if (this.element.id) {
const label = document.querySelector(`label[for="${this.element.id}"]`);
if (label) {
label.setAttribute('data-live-ignore', '');
}
if (!this.mutationObserver) {
this.mutationObserver = new MutationObserver((mutations) => {
this.onMutations(mutations);
});
}
}
connect() {
Expand All @@ -47,11 +46,15 @@ class default_1 extends Controller {
return;
}
this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocomplete).call(this);
this.startMutationObserver();
}
disconnect() {
this.tomSelect.revertSettings.innerHTML = this.element.innerHTML;
this.stopMutationObserver();
this.tomSelect.destroy();
}
getMaxOptions() {
return this.selectElement ? this.selectElement.options.length : 50;
}
get selectElement() {
if (!(this.element instanceof HTMLSelectElement)) {
return null;
Expand Down Expand Up @@ -79,6 +82,123 @@ class default_1 extends Controller {
}
return this.preloadValue;
}
resetTomSelect() {
if (this.tomSelect) {
this.stopMutationObserver();
this.tomSelect.clearOptions();
this.tomSelect.settings.maxOptions = this.getMaxOptions();
this.tomSelect.sync();
this.startMutationObserver();
}
}
changeTomSelectDisabledState(isDisabled) {
this.stopMutationObserver();
if (isDisabled) {
this.tomSelect.disable();
}
else {
this.tomSelect.enable();
}
this.startMutationObserver();
}
updateTomSelectPlaceholder() {
const input = this.element;
let placeholder = input.getAttribute('placeholder') || input.getAttribute('data-placeholder');
if (!placeholder && !this.tomSelect.allowEmptyOption) {
const option = input.querySelector('option[value=""]');
if (option) {
placeholder = option.textContent;
}
}
if (placeholder) {
this.stopMutationObserver();
this.tomSelect.control_input.setAttribute('placeholder', placeholder);
this.startMutationObserver();
}
}
startMutationObserver() {
if (!this.isObserving) {
this.mutationObserver.observe(this.element, {
childList: true,
subtree: true,
attributes: true,
characterData: true,
});
this.isObserving = true;
}
}
stopMutationObserver() {
if (this.isObserving) {
this.mutationObserver.disconnect();
this.isObserving = false;
}
}
onMutations(mutations) {
const addedOptionElements = [];
const removedOptionElements = [];
let hasAnOptionChanged = false;
let changeDisabledState = false;
let changePlaceholder = false;
mutations.forEach((mutation) => {
switch (mutation.type) {
case 'childList':
if (mutation.target instanceof HTMLOptionElement) {
if (mutation.target.value === '') {
changePlaceholder = true;
break;
}
hasAnOptionChanged = true;
break;
}
mutation.addedNodes.forEach((node) => {
if (node instanceof HTMLOptionElement) {
if (removedOptionElements.includes(node)) {
removedOptionElements.splice(removedOptionElements.indexOf(node), 1);
return;
}
addedOptionElements.push(node);
}
});
mutation.removedNodes.forEach((node) => {
if (node instanceof HTMLOptionElement) {
if (addedOptionElements.includes(node)) {
addedOptionElements.splice(addedOptionElements.indexOf(node), 1);
return;
}
removedOptionElements.push(node);
}
});
break;
case 'attributes':
if (mutation.target instanceof HTMLOptionElement) {
hasAnOptionChanged = true;
break;
}
if (mutation.target === this.element && mutation.attributeName === 'disabled') {
changeDisabledState = true;
break;
}
break;
case 'characterData':
if (mutation.target instanceof Text && mutation.target.parentElement instanceof HTMLOptionElement) {
if (mutation.target.parentElement.value === '') {
changePlaceholder = true;
break;
}
hasAnOptionChanged = true;
}
}
});
if (hasAnOptionChanged || addedOptionElements.length > 0 || removedOptionElements.length > 0) {
this.resetTomSelect();
}
if (changeDisabledState) {
this.changeTomSelectDisabledState((this.formElement.disabled));
}
if (changePlaceholder) {
this.updateTomSelectPlaceholder();
}
}
}
_default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _default_1_getCommonConfig() {
const plugins = {};
Expand All @@ -103,10 +223,6 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
onItemAdd: () => {
this.tomSelect.setTextboxValue('');
},
onInitialize: function () {
const tomSelect = this;
tomSelect.wrapper.setAttribute('data-live-ignore', '');
},
closeAfterSelect: true,
};
if (!this.selectElement && !this.urlValue) {
Expand All @@ -115,12 +231,12 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
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), {
maxOptions: this.selectElement ? this.selectElement.options.length : 50,
maxOptions: this.getMaxOptions(),
});
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), {
maxOptions: this.selectElement ? this.selectElement.options.length : 50,
maxOptions: this.getMaxOptions(),
score: (search) => {
const scoringFunction = this.tomSelect.getScoreFunction(search);
return (item) => {
Expand Down Expand Up @@ -183,9 +299,11 @@ _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _def
}, _default_1_mergeObjects = function _default_1_mergeObjects(object1, object2) {
return Object.assign(Object.assign({}, object1), object2);
}, _default_1_createTomSelect = function _default_1_createTomSelect(options) {
this.dispatchEvent('pre-connect', { options });
const preConnectPayload = { options };
this.dispatchEvent('pre-connect', preConnectPayload);
const tomSelect = new TomSelect(this.formElement, options);
this.dispatchEvent('connect', { tomSelect, options });
const connectPayload = { tomSelect, options };
this.dispatchEvent('connect', connectPayload);
return tomSelect;
};
default_1.values = {
Expand Down
Loading