Skip to content

Commit

Permalink
feat: VirtualScroll replace all html strings to pure HTML elements
Browse files Browse the repository at this point in the history
- also replace all MultipleSelect `innerHTML` except for when renderAsHtml is enabled
  • Loading branch information
ghiscoding committed Nov 5, 2023
1 parent adcc33d commit 23d8e1f
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 59 deletions.
9 changes: 5 additions & 4 deletions demo/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import './style.scss';
import mainHtml from './main.html?raw';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap';
import 'font-awesome/css/font-awesome.css';
import { createDomElement, emptyElement } from 'multiple-select-vanilla';

import { exampleRouting, navbarRouting } from './app-routing';
import { createDomElement } from 'multiple-select-vanilla';
import mainHtml from './main.html?raw';
import './style.scss';

const pageLayoutGlobs = import.meta.glob('/src/./**/*.html', { as: 'raw', eager: true });

Expand Down Expand Up @@ -97,7 +98,7 @@ class Main {

async loadRoute(routeName: string, changeBrowserState = true) {
const contentElm = document.querySelector('.panel-wm-content') as HTMLElement;
contentElm.innerHTML = '';
emptyElement(contentElm);
contentElm.classList.add('cloak');
let foundRouter = navbarRouting.find((r) => r.name === routeName);

Expand Down
61 changes: 30 additions & 31 deletions lib/src/MultipleSelectInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
applyParsedStyleToElement,
calculateAvailableSpace,
createDomElement,
emptyElement,
findParent,
getElementOffset,
getElementSize,
Expand Down Expand Up @@ -51,6 +52,10 @@ export class MultipleSelectInstance {
protected virtualScroll?: VirtualScroll | null;
locales: MultipleSelectLocales = {};

get isRenderAsHtml() {
return this.options.renderOptionLabelAsHtml || this.options.useSelectOptionLabelToHtml;
}

constructor(
protected elm: HTMLSelectElement,
options?: Partial<Omit<MultipleSelectOption, 'onHardDestroy' | 'onAfterHardDestroy'>>
Expand Down Expand Up @@ -415,8 +420,8 @@ export class MultipleSelectInstance {
}

protected initListItems() {
const rows = this.getListRows();
let offset = 0;
const rows = this.getListRows();

if (this.options.selectAll && !this.options.single) {
offset = -1;
Expand Down Expand Up @@ -465,9 +470,8 @@ export class MultipleSelectInstance {
}
} else {
if (this.ulElm) {
const htmlRows: string[] = [];
rows.forEach((rowElm) => htmlRows.push(rowElm.outerHTML));
this.ulElm.innerHTML = this.options.sanitizer ? this.options.sanitizer(htmlRows.join('')) : htmlRows.join('');
emptyElement(this.ulElm);
rows.forEach((rowElm) => this.ulElm!.appendChild(rowElm));
}
this.updateDataStart = 0;
this.updateDataEnd = this.updateData.length;
Expand All @@ -478,19 +482,15 @@ export class MultipleSelectInstance {

protected getListRows() {
const rows: HTMLElement[] = [];

this.updateData = [];
this.data?.forEach((row) => {
rows.push(...this.initListItem(row));
});

this.data?.forEach((row) => rows.push(...this.initListItem(row)));
rows.push(createDomElement('li', { className: 'ms-no-results', textContent: this.formatNoMatchesFound() }));

return rows;
}

protected initListItem(row: any, level = 0): HTMLElement[] {
const isRenderAsHtml = this.options.renderOptionLabelAsHtml || this.options.useSelectOptionLabelToHtml;
const title = row?.title || '';
const multiple = this.options.multiple ? 'multiple' : '';
const type = this.options.single ? 'radio' : 'checkbox';
Expand Down Expand Up @@ -535,11 +535,7 @@ export class MultipleSelectInstance {
labelElm.appendChild(groupElm);

const spanElm = document.createElement('span');
if (isRenderAsHtml) {
spanElm.innerHTML = this.options.sanitizer ? this.options.sanitizer(row.label) : row.label;
} else {
spanElm.textContent = row.label;
}
this.renderAsTextOrHtmlWhenEnabled(spanElm, row.label);
labelElm.appendChild(spanElm);
const liElm = createDomElement('li', { className: `group ${classes}`.trim() });
applyParsedStyleToElement(liElm, styleStr);
Expand Down Expand Up @@ -585,11 +581,7 @@ export class MultipleSelectInstance {
}

const spanElm = document.createElement('span');
if (isRenderAsHtml) {
spanElm.innerHTML = this.options.sanitizer ? this.options.sanitizer(row.text) : row.text;
} else {
spanElm.textContent = row.text;
}
this.renderAsTextOrHtmlWhenEnabled(spanElm, row.text);

labelElm.appendChild(inputElm);
labelElm.appendChild(spanElm);
Expand Down Expand Up @@ -738,11 +730,8 @@ export class MultipleSelectInstance {
visibleLiElms.push(selectedElm);
}
});
if (visibleLiElms.length) {
const selectItemAttrName = 'data-name'; // [data-name="selectItem"], we want "data-name" attribute
if (visibleLiElms[0].hasAttribute(selectItemAttrName)) {
this.setSelects([visibleLiElms[0].value]);
}
if (visibleLiElms.length && visibleLiElms[0].hasAttribute('data-name')) {
this.setSelects([visibleLiElms[0].value]);
}
} else {
this.selectAllElm?.click();
Expand Down Expand Up @@ -946,6 +935,19 @@ export class MultipleSelectInstance {
this.options.onClose();
}

/**
* Renders value to an HTML element as text or as HTML with innerHTML when enabled
* @param elm
* @param value
*/
protected renderAsTextOrHtmlWhenEnabled(elm: HTMLElement, value: string) {
if (this.isRenderAsHtml) {
elm.innerHTML = this.options.sanitizer ? this.options.sanitizer(value) : value;
} else {
elm.textContent = value;
}
}

protected update(ignoreTrigger = false) {
const valueSelects = this.getSelects();
let textSelects = this.getSelects('text');
Expand All @@ -970,7 +972,7 @@ export class MultipleSelectInstance {
if (sl === 0) {
const placeholder = this.options.placeholder || '';
spanElm.classList.add('ms-placeholder');
spanElm.innerHTML = this.options.sanitizer ? this.options.sanitizer(placeholder) : placeholder;
this.renderAsTextOrHtmlWhenEnabled(spanElm, placeholder);
} else if (sl < this.options.minimumCountSelected) {
html = getSelectOptionHtml();
} else if (this.formatAllSelected() && sl === this.dataTotal) {
Expand All @@ -982,13 +984,10 @@ export class MultipleSelectInstance {
} else {
html = getSelectOptionHtml();
}

if (html !== null) {
spanElm?.classList.remove('ms-placeholder');
if (this.options.renderOptionLabelAsHtml || this.options.useSelectOptionLabelToHtml) {
spanElm.innerHTML = this.options.sanitizer ? this.options.sanitizer(html) : html;
} else {
spanElm.textContent = html;
}
this.renderAsTextOrHtmlWhenEnabled(spanElm, html);
}

if (this.options.displayTitle || this.options.addTitle) {
Expand Down Expand Up @@ -1121,7 +1120,7 @@ export class MultipleSelectInstance {
let selected = false;
if (type === 'text') {
const divElm = document.createElement('div');
divElm.innerHTML = this.options.sanitizer ? this.options.sanitizer(row.text) : row.text;
this.renderAsTextOrHtmlWhenEnabled(divElm, row.text);
selected = values.includes(divElm.textContent?.trim() ?? '');
} else {
selected = values.includes(row._value || row.value);
Expand Down
30 changes: 15 additions & 15 deletions lib/src/services/virtual-scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { emptyElement } from '../utils';

interface VirtualCache {
bottom: number;
data: string;
data: HTMLElement[];
scrollTop: number;
top: number;
}
Expand Down Expand Up @@ -59,30 +59,30 @@ export class VirtualScroll {
initDOM(rows: HTMLElement[]) {
if (typeof this.clusterHeight === 'undefined') {
this.cache.scrollTop = this.scrollEl.scrollTop;
const data = rows[0].outerHTML + rows[0].outerHTML + rows[0].outerHTML;
this.contentEl.innerHTML = this.sanitizer ? this.sanitizer(`${data}`) : `${data}`;
this.cache.data = data;

this.contentEl.appendChild(rows[0]);
this.contentEl.appendChild(rows[0]);
this.contentEl.appendChild(rows[0]);
this.cache.data = [rows[0]];
this.getRowsHeight();
}

const data = this.initData(rows, this.getNum());
const htmlRows: string[] = [];
data.rows.forEach((row) => htmlRows.push(row.outerHTML));
const thisRows = htmlRows.join('');
const dataChanged = this.checkChanges('data', thisRows);
const dataChanged = this.checkChanges('data', data.rows);
const topOffsetChanged = this.checkChanges('top', data.topOffset);
const bottomOffsetChanged = this.checkChanges('bottom', data.bottomOffset);
const html = [];

emptyElement(this.contentEl);

if (dataChanged && topOffsetChanged) {
if (data.topOffset) {
html.push(this.getExtra('top', data.topOffset));
this.contentEl.appendChild(this.getExtra('top', data.topOffset));
}
html.push(thisRows);
data.rows.forEach((h) => this.contentEl.appendChild(h));

if (data.bottomOffset) {
html.push(this.getExtra('bottom', data.bottomOffset));
this.contentEl.appendChild(this.getExtra('bottom', data.bottomOffset));
}
this.contentEl.innerHTML = this.sanitizer ? this.sanitizer(html.join('')) : html.join('');
} else if (bottomOffsetChanged && this.contentEl.lastChild) {
(this.contentEl.lastChild as HTMLElement).style.height = `${data.bottomOffset}px`;
}
Expand Down Expand Up @@ -129,7 +129,7 @@ export class VirtualScroll {
const end = start + this.clusterRows!;
const topOffset = Math.max(start * this.itemHeight!, 0);
const bottomOffset = Math.max((rows.length - end) * this.itemHeight!, 0);
const thisRows = [];
const thisRows: HTMLElement[] = [];
let rowsAbove = start;
if (topOffset < 1) {
rowsAbove++;
Expand Down Expand Up @@ -161,6 +161,6 @@ export class VirtualScroll {
if (height) {
tag.style.height = `${height}px`;
}
return tag.outerHTML;
return tag;
}
}
14 changes: 5 additions & 9 deletions lib/src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,11 @@ export function findByParam(data: any, param: any, value: any) {
}
}

export function stripScripts(str: string) {
const div = document.createElement('div');
div.innerHTML = str;
const scripts = div.getElementsByTagName('script');
let i = scripts.length;
while (i--) {
scripts[i].parentNode?.removeChild(scripts[i]);
}
return div.innerHTML;
export function stripScripts(dirtyHtml: string) {
return dirtyHtml.replace(
/(\b)(on[a-z]+)(\s*)=([^>]*)|javascript:([^>]*)[^>]*|(<\s*)(\/*)script([<>]*).*(<\s*)(\/*)script(>*)|(&lt;|&#60;)(\/*)(script|script defer)(.*)(&#62;|&gt;|&gt;">)/gi,
''
);
}

export function removeUndefined(obj: any) {
Expand Down

0 comments on commit 23d8e1f

Please sign in to comment.