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", "name": "grapesjs",
"description": "Free and Open Source Web Builder Framework", "description": "Free and Open Source Web Builder Framework",
"version": "0.22.12", "version": "0.22.13",
"author": "Artur Arseniev", "author": "Artur Arseniev",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"homepage": "http://grapesjs.com", "homepage": "http://grapesjs.com",

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

@ -11,7 +11,15 @@ import {
bindAll, bindAll,
keys, keys,
} from 'underscore'; } from 'underscore';
import { shallowDiff, capitalize, isEmptyObj, isObject, toLowerCase } from '../../utils/mixins'; import {
shallowDiff,
capitalize,
isEmptyObj,
isObject,
toLowerCase,
escapeAltQuoteAttrValue,
escapeAttrValue,
} from '../../utils/mixins';
import StyleableModel, { import StyleableModel, {
GetStyleOpts, GetStyleOpts,
StyleProps, StyleProps,
@ -1597,9 +1605,9 @@ export default class Component extends StyleableModel<ComponentProperties> {
} else { } else {
let valueRes = ''; let valueRes = '';
if (opts.altQuoteAttr && isString(val) && val.indexOf('"') >= 0) { if (opts.altQuoteAttr && isString(val) && val.indexOf('"') >= 0) {
valueRes = `'${val.replace(/'/g, '&apos;')}'`; valueRes = `'${escapeAltQuoteAttrValue(val)}'`;
} else { } else {
const value = isString(val) ? val.replace(/"/g, '&quot;') : val; const value = isString(val) ? escapeAttrValue(val) : val;
valueRes = `"${value}"`; 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 { 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 { AddOptions, Collection } from '../../common';
import { DomComponentsConfig } from '../config/config'; import { DomComponentsConfig } from '../config/config';
import EditorModel from '../../editor/model/Editor'; import EditorModel from '../../editor/model/Editor';
@ -18,6 +18,21 @@ import ComponentWrapper from './ComponentWrapper';
import { ComponentsEvents, ParseStringOptions } from '../types'; import { ComponentsEvents, ParseStringOptions } from '../types';
import { isSymbolInstance, isSymbolRoot, updateSymbolComps } from './SymbolUtils'; 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[] = []) => { export const getComponentIds = (cmp?: Component | Component[] | Components, res: string[] = []) => {
if (!cmp) return []; if (!cmp) return [];
const cmps = (isArray(cmp) || isFunction((cmp as Components).map) ? cmp : [cmp]) as Component[]; const cmps = (isArray(cmp) || isFunction((cmp as Components).map) ? cmp : [cmp]) as Component[];
@ -35,6 +50,7 @@ const getComponentsFromDefs = (
) => { ) => {
opts.visitedCmps = opts.visitedCmps || {}; opts.visitedCmps = opts.visitedCmps || {};
const { visitedCmps } = opts; const { visitedCmps } = opts;
const updateOptions = (opts.updateOptions as ResetFromStringOptions['updateOptions']) || {};
const itms = isArray(items) ? items : [items]; const itms = isArray(items) ? items : [items];
return itms.map((item) => { return itms.map((item) => {
@ -50,10 +66,21 @@ const getComponentsFromDefs = (
// Update the component if exists already // Update the component if exists already
if (all[id]) { if (all[id]) {
result = all[id] as any; result = all[id] as any;
const cmp = result as unknown as Component; const { onAttributes, onStyle } = updateOptions;
tagName && cmp.set({ tagName }, { ...opts, silent: true }); const component = result as unknown as Component;
keys(restAttr).length && cmp.addAttributes(restAttr, { ...opts }); tagName && component.set({ tagName }, { ...opts, silent: true });
keys(style).length && cmp.addStyle(style, opts);
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 { } else {
// Found another component with the same ID, treat it as a new component // Found another component with the same ID, treat it as a new component
@ -131,7 +158,7 @@ Component> {
models.each((model) => this.onAdd(model)); models.each((model) => this.onAdd(model));
} }
resetFromString(input = '', opts: { visitedCmps?: Record<string, Component[]>; keepIds?: string[] } = {}) { resetFromString(input = '', opts: ResetFromStringOptions = {}) {
opts.keepIds = getComponentIds(this); opts.keepIds = getComponentIds(this);
const { domc, em, parent } = this; const { domc, em, parent } = this;
const cssc = em?.Css; const cssc = em?.Css;

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

@ -315,7 +315,7 @@ TComp> {
* @private * @private
* */ * */
updateClasses() { updateClasses() {
const str = this.model.classes.pluck('name').join(' '); const str = this.model.classes.pluck?.('name').join(' ') || '';
this.setAttribute('class', str); this.setAttribute('class', str);
// Regenerate status class // 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[] = []; const added: Selector[] = [];
if (isString(classes)) { if (isString(classes)) {
classes = classes.trim().split(' '); classes = classes.trim().split(' ').filter(Boolean);
} }
classes.forEach((name) => added.push(this.addSelector(name) as Selector)); 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;'); 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[]) => { export const deepMerge = (...args: ObjectAny[]) => {
const target = { ...args[0] }; const target = { ...args[0] };

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

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