Browse Source

Allow to import HTML string as document

pull/5895/head
Artur Arseniev 2 years ago
parent
commit
8512f3eeff
  1. 7
      src/common/index.ts
  2. 18
      src/dom_components/model/ComponentWrapper.ts
  3. 24
      src/dom_components/model/Components.ts
  4. 9
      src/dom_components/model/types.ts
  5. 6
      src/utils/dom.ts

7
src/common/index.ts

@ -25,6 +25,13 @@ export type ObjectStrings = Record<string, string>;
export type Nullable = undefined | null | false;
export interface OptionAsDocument {
/**
* Treat the HTML string as document (option valid on the root component, eg. will include `<head>`, `<html>`, etc.).
*/
asDocument?: boolean;
}
// https://github.com/microsoft/TypeScript/issues/29729#issuecomment-1483854699
export type LiteralUnion<T, U> = T | (U & NOOP);

18
src/dom_components/model/ComponentWrapper.ts

@ -1,3 +1,4 @@
import { attrToString } from '../../utils/dom';
import Component from './Component';
import ComponentHead, { type as typeHead } from './ComponentHead';
import { ToHTMLOptions } from './types';
@ -15,6 +16,7 @@ export default class ComponentWrapper extends Component {
traits: [],
doctype: '',
head: null,
docEl: null,
stylable: [
'background',
'background-color',
@ -30,10 +32,13 @@ export default class ComponentWrapper extends Component {
constructor(...args: ConstructorParameters<typeof Component>) {
super(...args);
const opts = args[1];
const CmpHead = opts?.em?.Components.getType(typeHead)?.model;
const cmp = opts?.em?.Components;
const CmpHead = cmp?.getType(typeHead)?.model;
const CmpDef = cmp?.getType('default').model;
if (CmpHead) {
this.set({
head: new CmpHead({}, opts),
docEl: new CmpDef({ tagName: 'html' }, opts),
});
}
}
@ -42,12 +47,19 @@ export default class ComponentWrapper extends Component {
return this.get('head');
}
get docEl(): Component {
return this.get('docEl');
}
toHTML(opts: ToHTMLOptions = {}) {
const { asDocument } = opts;
const { head, docEl } = this;
const { doctype = '' } = this.attributes;
const body = super.toHTML(opts);
const head = (asDocument && this.head?.toHTML(opts)) || '';
return asDocument ? `${doctype}${head}${body}` : body;
const headStr = (asDocument && head?.toHTML(opts)) || '';
const docElAttr = (asDocument && attrToString(docEl?.getAttrToHTML())) || '';
const docElAttrStr = docElAttr ? ` ${docElAttr}` : '';
return asDocument ? `${doctype}<html${docElAttrStr}>${headStr}${body}</html>` : body;
}
__postAdd() {

24
src/dom_components/model/Components.ts

@ -1,12 +1,13 @@
import { isEmpty, isArray, isString, isFunction, each, includes, extend, flatten, keys } from 'underscore';
import Component from './Component';
import { AddOptions, Collection, ObjectAny } from '../../common';
import { AddOptions, Collection, ObjectAny, OptionAsDocument } from '../../common';
import { DomComponentsConfig } from '../config/config';
import EditorModel from '../../editor/model/Editor';
import ComponentManager from '..';
import CssRule from '../../css_composer/model/CssRule';
import { ComponentAdd, ComponentProperties } from './types';
import ComponentText from './ComponentText';
import ComponentWrapper from './ComponentWrapper';
export const getComponentIds = (cmp?: Component | Component[] | Components, res: string[] = []) => {
if (!cmp) return [];
@ -228,12 +229,23 @@ Component> {
return new model(attrs, options) as Component;
}
parseString(value: string, opt: AddOptions & { temporary?: boolean; keepIds?: string[] } = {}) {
const { em, domc } = this;
parseString(value: string, opt: AddOptions & OptionAsDocument & { temporary?: boolean; keepIds?: string[] } = {}) {
const { em, domc, parent } = this;
const asDocument = opt.asDocument && parent?.is('wrapper');
const cssc = em.Css;
const parsed = em.Parser.parseHtml(value);
const parsed = em.Parser.parseHtml(value, { asDocument });
let components = parsed.html;
if (asDocument) {
const root = parent as ComponentWrapper;
components = (parsed.html as any).components;
root.head.set(parsed.head as any, opt);
root.docEl.set(parsed.root as any, opt);
root.set({ doctype: parsed.doctype });
}
// We need this to avoid duplicate IDs
Component.checkId(parsed.html!, parsed.css, domc!.componentsById, opt);
Component.checkId(components, parsed.css, domc!.componentsById, opt);
if (parsed.css && cssc && !opt.temporary) {
const { at, ...optsToPass } = opt;
@ -243,7 +255,7 @@ Component> {
});
}
return parsed.html;
return components;
}
/** @ts-ignore */

9
src/dom_components/model/types.ts

@ -1,5 +1,5 @@
import Frame from '../../canvas/model/Frame';
import { Nullable } from '../../common';
import { Nullable, OptionAsDocument } from '../../common';
import EditorModel from '../../editor/model/Editor';
import Selectors from '../../selector_manager/model/Selectors';
import { TraitProperties } from '../../trait_manager/types';
@ -264,7 +264,7 @@ type ComponentAddType = Component | ComponentDefinition | ComponentDefinitionDef
export type ComponentAdd = ComponentAddType | ComponentAddType[];
export interface ToHTMLOptions {
export interface ToHTMLOptions extends OptionAsDocument {
/**
* Custom tagName.
*/
@ -280,11 +280,6 @@ export interface ToHTMLOptions {
*/
altQuoteAttr?: boolean;
/**
* Return the HTML string as document (option valid on the root component, eg. will include the <head>).
*/
asDocument?: boolean;
/**
* You can pass an object of custom attributes to replace with the current ones
* or you can even pass a function to generate attributes dynamically.

6
src/utils/dom.ts

@ -214,6 +214,12 @@ export const doctypeToString = (dt?: DocumentType | null) => {
return `<!DOCTYPE ${name}${pubId}${sysId}>`;
};
export const attrToString = (attrs: ObjectAny = {}) => {
const res: string[] = [];
each(attrs, (value, key) => res.push(`${key}="${value}"`));
return res.join(' ');
};
export const on = <E extends Event = Event>(
el: EventTarget | EventTarget[],
ev: string,

Loading…
Cancel
Save