Browse Source

Merge branch 'dev' of https://github.com/GrapesJS/grapesjs into pages-datasources

pages-datasources
mohamedsalem401 1 year ago
parent
commit
b600eb6b03
  1. 2
      packages/core/package.json
  2. 14
      packages/core/src/dom_components/model/Component.ts
  3. 39
      packages/core/src/dom_components/model/Components.ts
  4. 2
      packages/core/src/dom_components/view/ComponentView.ts
  5. 2
      packages/core/src/selector_manager/index.ts
  6. 8
      packages/core/src/utils/mixins.ts
  7. 6
      packages/core/test/specs/dom_components/model/Component.ts

2
packages/core/package.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Free and Open Source Web Builder Framework",
"version": "0.22.12",
"version": "0.22.13",
"author": "Artur Arseniev",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",

14
packages/core/src/dom_components/model/Component.ts

@ -11,7 +11,15 @@ import {
bindAll,
keys,
} from 'underscore';
import { shallowDiff, capitalize, isEmptyObj, isObject, toLowerCase } from '../../utils/mixins';
import {
shallowDiff,
capitalize,
isEmptyObj,
isObject,
toLowerCase,
escapeAltQuoteAttrValue,
escapeAttrValue,
} from '../../utils/mixins';
import StyleableModel, {
GetStyleOpts,
StyleProps,
@ -1597,9 +1605,9 @@ export default class Component extends StyleableModel<ComponentProperties> {
} else {
let valueRes = '';
if (opts.altQuoteAttr && isString(val) && val.indexOf('"') >= 0) {
valueRes = `'${val.replace(/'/g, '&apos;')}'`;
valueRes = `'${escapeAltQuoteAttrValue(val)}'`;
} else {
const value = isString(val) ? val.replace(/"/g, '&quot;') : val;
const value = isString(val) ? escapeAttrValue(val) : val;
valueRes = `"${value}"`;
}

39
packages/core/src/dom_components/model/Components.ts

@ -1,5 +1,5 @@
import { isEmpty, isArray, isString, isFunction, each, includes, extend, flatten, keys } from 'underscore';
import Component from './Component';
import Component, { SetAttrOptions } from './Component';
import { AddOptions, Collection } from '../../common';
import { DomComponentsConfig } from '../config/config';
import EditorModel from '../../editor/model/Editor';
@ -18,6 +18,21 @@ import ComponentWrapper from './ComponentWrapper';
import { ComponentsEvents, ParseStringOptions } from '../types';
import { isSymbolInstance, isSymbolRoot, updateSymbolComps } from './SymbolUtils';
export interface ResetCommonUpdateProps {
component: Component;
item: ComponentDefinitionDefined;
options: SetAttrOptions;
}
export interface ResetFromStringOptions {
visitedCmps?: Record<string, Component[]>;
keepIds?: string[];
updateOptions?: {
onAttributes?: (props: ResetCommonUpdateProps & { attributes: Record<string, any> }) => void;
onStyle?: (props: ResetCommonUpdateProps & { style: Record<string, any> }) => void;
};
}
export const getComponentIds = (cmp?: Component | Component[] | Components, res: string[] = []) => {
if (!cmp) return [];
const cmps = (isArray(cmp) || isFunction((cmp as Components).map) ? cmp : [cmp]) as Component[];
@ -35,6 +50,7 @@ const getComponentsFromDefs = (
) => {
opts.visitedCmps = opts.visitedCmps || {};
const { visitedCmps } = opts;
const updateOptions = (opts.updateOptions as ResetFromStringOptions['updateOptions']) || {};
const itms = isArray(items) ? items : [items];
return itms.map((item) => {
@ -50,10 +66,21 @@ const getComponentsFromDefs = (
// Update the component if exists already
if (all[id]) {
result = all[id] as any;
const cmp = result as unknown as Component;
tagName && cmp.set({ tagName }, { ...opts, silent: true });
keys(restAttr).length && cmp.addAttributes(restAttr, { ...opts });
keys(style).length && cmp.addStyle(style, opts);
const { onAttributes, onStyle } = updateOptions;
const component = result as unknown as Component;
tagName && component.set({ tagName }, { ...opts, silent: true });
if (onAttributes) {
onAttributes({ item, component, attributes: restAttr, options: opts });
} else if (keys(restAttr).length) {
component.addAttributes(restAttr, { ...opts });
}
if (onStyle) {
onStyle({ item, component, style, options: opts });
} else if (keys(style).length) {
component.addStyle(style, opts);
}
}
} else {
// Found another component with the same ID, treat it as a new component
@ -131,7 +158,7 @@ Component> {
models.each((model) => this.onAdd(model));
}
resetFromString(input = '', opts: { visitedCmps?: Record<string, Component[]>; keepIds?: string[] } = {}) {
resetFromString(input = '', opts: ResetFromStringOptions = {}) {
opts.keepIds = getComponentIds(this);
const { domc, em, parent } = this;
const cssc = em?.Css;

2
packages/core/src/dom_components/view/ComponentView.ts

@ -315,7 +315,7 @@ TComp> {
* @private
* */
updateClasses() {
const str = this.model.classes.pluck('name').join(' ');
const str = this.model.classes.pluck?.('name').join(' ') || '';
this.setAttribute('class', str);
// Regenerate status class

2
packages/core/src/selector_manager/index.ts

@ -292,7 +292,7 @@ export default class SelectorManager extends ItemManagerModule<SelectorManagerCo
const added: Selector[] = [];
if (isString(classes)) {
classes = classes.trim().split(' ');
classes = classes.trim().split(' ').filter(Boolean);
}
classes.forEach((name) => added.push(this.addSelector(name) as Selector));

8
packages/core/src/utils/mixins.ts

@ -192,6 +192,14 @@ export const escapeNodeContent = (str = '') => {
return `${str}`.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
};
export const escapeAttrValue = (str = '') => {
return `${str}`.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
};
export const escapeAltQuoteAttrValue = (str = '') => {
return `${str}`.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/'/g, '&apos;');
};
export const deepMerge = (...args: ObjectAny[]) => {
const target = { ...args[0] };

6
packages/core/test/specs/dom_components/model/Component.ts

@ -155,7 +155,7 @@ describe('Component', () => {
obj.set({
bool: true,
removable: false,
string: 'st\'ri"ng',
string: 'st\'ri"ng&<>',
array: [1, 'string', true],
object: { a: 1, b: 'string', c: true },
null: null,
@ -164,12 +164,12 @@ describe('Component', () => {
zero: 0,
_private: 'value',
});
let resStr = "st'ri&quot;ng";
let resStr = "st'ri&quot;ng&amp;&lt;&gt;";
let resArr = '[1,&quot;string&quot;,true]';
let resObj = '{&quot;a&quot;:1,&quot;b&quot;:&quot;string&quot;,&quot;c&quot;:true}';
let res = `<div data-gjs-removable="false" data-gjs-bool="true" data-gjs-string="${resStr}" data-gjs-array="${resArr}" data-gjs-object="${resObj}" data-gjs-empty="" data-gjs-zero="0"></div>`;
expect(obj.toHTML({ withProps: true })).toEqual(res);
resStr = 'st&apos;ri"ng';
resStr = 'st&apos;ri"ng&amp;&lt;&gt;';
resArr = '[1,"string",true]';
resObj = '{"a":1,"b":"string","c":true}';
res = `<div data-gjs-removable="false" data-gjs-bool="true" data-gjs-string='${resStr}' data-gjs-array='${resArr}' data-gjs-object='${resObj}' data-gjs-empty="" data-gjs-zero="0"></div>`;

Loading…
Cancel
Save