Skip to content

Commit

Permalink
[LiveComponent] Fix trim classes in live_controller.js
Browse files Browse the repository at this point in the history
And various CS / minor fixes while i looked at the code... you may refuse as it's way over the original scope :)
  • Loading branch information
smnandre committed Jan 5, 2024
1 parent 7a54335 commit 43d142d
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 319 deletions.
1 change: 0 additions & 1 deletion src/LiveComponent/assets/dist/HookManager.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export default class {
private hooks;
constructor();
register(hookName: string, callback: () => void): void;
unregister(hookName: string, callback: () => void): void;
triggerHook(hookName: string, ...args: any[]): void;
Expand Down
237 changes: 95 additions & 142 deletions src/LiveComponent/assets/dist/live_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,13 @@ function parseDirectives(content) {
function combineSpacedArray(parts) {
const finalParts = [];
parts.forEach((part) => {
finalParts.push(...part.split(' '));
finalParts.push(...trimAll(part).split(' '));
});
return finalParts;
}
function trimAll(str) {
return str.replace(/[\s]+/g, ' ').trim();
}
function normalizeModelName(model) {
return (model
.replace(/\[]$/, '')
Expand Down Expand Up @@ -365,24 +368,17 @@ class ValueStore {
}
get(name) {
const normalizedName = normalizeModelName(name);
if (this.dirtyProps[normalizedName] !== undefined) {
return this.dirtyProps[normalizedName];
}
if (this.pendingProps[normalizedName] !== undefined) {
return this.pendingProps[normalizedName];
}
if (this.props[normalizedName] !== undefined) {
return this.props[normalizedName];
}
return getDeepData(this.props, normalizedName);
return this.dirtyProps[normalizedName] ||
this.pendingProps[normalizedName] ||
this.props[normalizedName] ||
getDeepData(this.props, normalizedName);
}
has(name) {
return this.get(name) !== undefined;
}
set(name, value) {
const normalizedName = normalizeModelName(name);
const currentValue = this.get(normalizedName);
if (currentValue === value) {
if (this.get(normalizedName) === value) {
return false;
}
this.dirtyProps[normalizedName] = value;
Expand Down Expand Up @@ -1384,17 +1380,14 @@ class HookManager {
unregister(hookName, callback) {
const hooks = this.hooks.get(hookName) || [];
const index = hooks.indexOf(callback);
if (index === -1) {
return;
if (index !== -1) {
hooks.splice(index, 1);
this.hooks.set(hookName, hooks);
}
hooks.splice(index, 1);
this.hooks.set(hookName, hooks);
}
triggerHook(hookName, ...args) {
const hooks = this.hooks.get(hookName) || [];
hooks.forEach((callback) => {
callback(...args);
});
hooks.forEach((callback) => callback(...args));
}
}

Expand Down Expand Up @@ -1449,18 +1442,10 @@ class ChangingItemsTracker {
}
}
getChangedItems() {
const changedItems = [];
this.changedItems.forEach((value, key) => {
changedItems.push({ name: key, value: value.new });
});
return changedItems;
return Array.from(this.changedItems, ([name, { new: value }]) => ({ name, value }));
}
getRemovedItems() {
const removedItems = [];
this.removedItems.forEach((value, key) => {
removedItems.push(key);
});
return removedItems;
return Array.from(this.removedItems.keys());
}
isEmpty() {
return this.changedItems.size === 0 && this.removedItems.size === 0;
Expand All @@ -1469,27 +1454,19 @@ class ChangingItemsTracker {

class ElementChanges {
constructor() {
this.addedClasses = [];
this.removedClasses = [];
this.addedClasses = new Set();
this.removedClasses = new Set();
this.styleChanges = new ChangingItemsTracker();
this.attributeChanges = new ChangingItemsTracker();
}
addClass(className) {
if (this.removedClasses.includes(className)) {
this.removedClasses = this.removedClasses.filter((name) => name !== className);
return;
}
if (!this.addedClasses.includes(className)) {
this.addedClasses.push(className);
if (!this.removedClasses.delete(className)) {
this.addedClasses.add(className);
}
}
removeClass(className) {
if (this.addedClasses.includes(className)) {
this.addedClasses = this.addedClasses.filter((name) => name !== className);
return;
}
if (!this.removedClasses.includes(className)) {
this.removedClasses.push(className);
if (!this.addedClasses.delete(className)) {
this.removedClasses.add(className);
}
}
addStyle(styleName, newValue, originalValue) {
Expand All @@ -1505,10 +1482,10 @@ class ElementChanges {
this.attributeChanges.removeItem(attributeName, originalValue);
}
getAddedClasses() {
return this.addedClasses;
return [...this.addedClasses];
}
getRemovedClasses() {
return this.removedClasses;
return [...this.removedClasses];
}
getChangedStyles() {
return this.styleChanges.getChangedItems();
Expand All @@ -1523,12 +1500,8 @@ class ElementChanges {
return this.attributeChanges.getRemovedItems();
}
applyToElement(element) {
this.addedClasses.forEach((className) => {
element.classList.add(className);
});
this.removedClasses.forEach((className) => {
element.classList.remove(className);
});
element.classList.add(...this.addedClasses);
element.classList.remove(...this.removedClasses);
this.styleChanges.getChangedItems().forEach((change) => {
element.style.setProperty(change.name, change.value);
return;
Expand All @@ -1544,8 +1517,8 @@ class ElementChanges {
});
}
isEmpty() {
return (this.addedClasses.length === 0 &&
this.removedClasses.length === 0 &&
return (this.addedClasses.size === 0 &&
this.removedClasses.size === 0 &&
this.styleChanges.isEmpty() &&
this.attributeChanges.isEmpty());
}
Expand Down Expand Up @@ -2322,17 +2295,17 @@ class LoadingPlugin {
}
handleLoadingToggle(isLoading, targetElement, backendRequest) {
if (isLoading) {
this.addAttributes(targetElement, ['busy']);
targetElement.setAttribute('busy', '');
}
else {
this.removeAttributes(targetElement, ['busy']);
targetElement.removeAttribute('busy');
}
this.getLoadingDirectives(targetElement).forEach(({ element, directives }) => {
if (isLoading) {
this.addAttributes(element, ['data-live-is-loading']);
element.setAttribute('data-live-is-loading', '');
}
else {
this.removeAttributes(element, ['data-live-is-loading']);
element.removeAttribute('data-live-is-loading');
}
directives.forEach((directive) => {
this.handleLoadingDirective(element, isLoading, directive, backendRequest);
Expand All @@ -2344,25 +2317,25 @@ class LoadingPlugin {
const targetedActions = [];
const targetedModels = [];
let delay = 0;
const validModifiers = new Map();
validModifiers.set('delay', (modifier) => {
if (!isLoading) {
return;
}
delay = modifier.value ? parseInt(modifier.value) : 200;
});
validModifiers.set('action', (modifier) => {
if (!modifier.value) {
throw new Error(`The "action" in data-loading must have an action name - e.g. action(foo). It's missing for "${directive.getString()}"`);
}
targetedActions.push(modifier.value);
});
validModifiers.set('model', (modifier) => {
if (!modifier.value) {
throw new Error(`The "model" in data-loading must have an action name - e.g. model(foo). It's missing for "${directive.getString()}"`);
}
targetedModels.push(modifier.value);
});
const validModifiers = new Map(Object.entries({
'delay': (modifier) => {
if (isLoading) {
delay = modifier.value ? parseInt(modifier.value) : 200;
}
},
'action': (modifier) => {
if (!modifier.value) {
throw new Error(`The "action" in data-loading must have an action name - e.g. action(foo). It's missing for "${directive.getString()}"`);
}
targetedActions.push(modifier.value);
},
'model': (modifier) => {
if (!modifier.value) {
throw new Error(`The "model" in data-loading must have an action name - e.g. model(foo). It's missing for "${directive.getString()}"`);
}
targetedModels.push(modifier.value);
},
}));
directive.modifiers.forEach((modifier) => {
var _a;
if (validModifiers.has(modifier.name)) {
Expand All @@ -2378,30 +2351,17 @@ class LoadingPlugin {
if (isLoading && targetedModels.length > 0 && backendRequest && !backendRequest.areAnyModelsUpdated(targetedModels)) {
return;
}
let loadingDirective;
switch (finalAction) {
case 'show':
loadingDirective = () => {
this.showElement(element);
};
break;
case 'hide':
loadingDirective = () => this.hideElement(element);
break;
case 'addClass':
loadingDirective = () => this.addClass(element, directive.args);
break;
case 'removeClass':
loadingDirective = () => this.removeClass(element, directive.args);
break;
case 'addAttribute':
loadingDirective = () => this.addAttributes(element, directive.args);
break;
case 'removeAttribute':
loadingDirective = () => this.removeAttributes(element, directive.args);
break;
default:
throw new Error(`Unknown data-loading action "${finalAction}"`);
const actions = new Map(Object.entries({
'show': () => this.showElement(element),
'hide': () => this.hideElement(element),
'addClass': () => this.addClass(element, directive.args),
'removeClass': () => this.removeClass(element, directive.args),
'addAttribute': () => this.addAttributes(element, directive.args),
'removeAttribute': () => this.removeAttributes(element, directive.args),
}));
const loadingDirective = actions.get(finalAction);
if (!loadingDirective) {
throw new Error(`Unknown data-loading action "${finalAction}"`);
}
if (delay) {
window.setTimeout(() => {
Expand All @@ -2415,9 +2375,9 @@ class LoadingPlugin {
}
getLoadingDirectives(element) {
const loadingDirectives = [];
let matchingElements = element.querySelectorAll('[data-loading]');
const matchingElements = Array.from(element.querySelectorAll('[data-loading]'));
if (element.hasAttribute('data-loading')) {
matchingElements = [element, ...matchingElements];
matchingElements.unshift(element);
}
matchingElements.forEach((element => {
if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) {
Expand All @@ -2443,7 +2403,7 @@ class LoadingPlugin {
removeClass(element, classes) {
element.classList.remove(...combineSpacedArray(classes));
if (element.classList.length === 0) {
this.removeAttributes(element, ['class']);
element.removeAttribute('class');
}
}
addAttributes(element, attributes) {
Expand All @@ -2458,21 +2418,18 @@ class LoadingPlugin {
}
}
const parseLoadingAction = function (action, isLoading) {
switch (action) {
case 'show':
return isLoading ? 'show' : 'hide';
case 'hide':
return isLoading ? 'hide' : 'show';
case 'addClass':
return isLoading ? 'addClass' : 'removeClass';
case 'removeClass':
return isLoading ? 'removeClass' : 'addClass';
case 'addAttribute':
return isLoading ? 'addAttribute' : 'removeAttribute';
case 'removeAttribute':
return isLoading ? 'removeAttribute' : 'addAttribute';
}
throw new Error(`Unknown data-loading action "${action}"`);
const actions = new Map(Object.entries({
'show': isLoading ? 'show' : 'hide',
'hide': isLoading ? 'hide' : 'show',
'addClass': isLoading ? 'addClass' : 'removeClass',
'removeClass': isLoading ? 'removeClass' : 'addClass',
'addAttribute': isLoading ? 'addAttribute' : 'removeAttribute',
'removeAttribute': isLoading ? 'removeAttribute' : 'addAttribute',
}));
if (!actions.has(action)) {
throw new Error(`Unknown data-loading action "${action}"`);
}
return actions.get(action);
};

class ValidatedFieldsPlugin {
Expand Down Expand Up @@ -2908,29 +2865,25 @@ class LiveControllerDefault extends Controller {
let debounce = false;
directives.forEach((directive) => {
let pendingFiles = {};
const validModifiers = new Map();
validModifiers.set('prevent', () => {
event.preventDefault();
});
validModifiers.set('stop', () => {
event.stopPropagation();
});
validModifiers.set('self', () => {
if (event.target !== event.currentTarget) {
return;
}
});
validModifiers.set('debounce', (modifier) => {
debounce = modifier.value ? parseInt(modifier.value) : true;
});
validModifiers.set('files', (modifier) => {
if (!modifier.value) {
pendingFiles = this.pendingFiles;
}
else if (this.pendingFiles[modifier.value]) {
pendingFiles[modifier.value] = this.pendingFiles[modifier.value];
}
});
const validModifiers = new Map(Object.entries({
prevent: () => event.preventDefault(),
stop: () => event.stopPropagation(),
self: () => {
if (event.target !== event.currentTarget)
return;
},
debounce: (modifier) => {
debounce = modifier.value ? parseInt(modifier.value) : true;
},
files: (modifier) => {
if (!modifier.value) {
pendingFiles = this.pendingFiles;
}
else if (this.pendingFiles[modifier.value]) {
pendingFiles[modifier.value] = this.pendingFiles[modifier.value];
}
},
}));
directive.modifiers.forEach((modifier) => {
var _a;
if (validModifiers.has(modifier.name)) {
Expand Down
1 change: 1 addition & 0 deletions src/LiveComponent/assets/dist/string_utils.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export declare function combineSpacedArray(parts: Array<string>): string[];
export declare function trimAll(str: string): string;
export declare function normalizeModelName(model: string): string;
Loading

0 comments on commit 43d142d

Please sign in to comment.