lupine.web 1.0.29 → 1.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lupine.web",
3
- "version": "1.0.29",
3
+ "version": "1.0.31",
4
4
  "license": "MIT",
5
5
  "author": "uuware.com",
6
6
  "homepage": "https://github.com/uuware/lupine.js",
@@ -22,7 +22,10 @@ export const bindAttributesChildren = (topEl: Element, children: any) => {
22
22
  export const bindAttributes = (topEl: Element, type: any, props: any) => {
23
23
  const newProps = (props._result && props._result.props) || props;
24
24
  if (newProps._id) {
25
- const el = topEl.querySelector(`[${newProps._id}]`);
25
+ let el = topEl.querySelector(`[${newProps._id}]`);
26
+ if (!el && topEl.getAttribute(newProps._id) === '') {
27
+ el = topEl;
28
+ }
26
29
  if (el) {
27
30
  for (let i in newProps) {
28
31
  if (i === 'ref') {
@@ -1,10 +1,9 @@
1
- // import { mountSelfComponents, renderComponent } from "./mount-components";
2
-
3
- import { VNode } from "../jsx";
4
- import { bindAttributes } from "./bind-attributes";
5
- import { bindLinks } from "./bind-links";
6
- import { renderComponents } from "./render-components";
7
- import { replaceInnerhtml } from "./replace-innerhtml";
1
+ import { VNode } from '../jsx';
2
+ import { bindAttributes } from './bind-attributes';
3
+ import { bindLinks } from './bind-links';
4
+ import { mountInnerComponent, mountOuterComponent } from './mount-component';
5
+ import { renderComponent } from './render-component';
6
+ import { replaceInnerhtml } from './replace-innerhtml';
8
7
 
9
8
  export const bindRef = (type: any, newProps: any, el: Element) => {
10
9
  // console.log('========', newProps, el);
@@ -31,22 +30,19 @@ export const bindRef = (type: any, newProps: any, el: Element) => {
31
30
  */
32
31
  newProps['ref'].$ = (selector: string) => {
33
32
  return el.querySelector(`[${id}] ` + selector);
34
- // const dom = LiteDom.queryOne(el);
35
- // return dom && dom.$(selector, value);
36
33
  };
37
34
  newProps['ref'].$all = (selector: string) => {
38
35
  return el.querySelectorAll(`[${id}] ` + selector);
39
36
  };
40
37
 
41
- newProps['ref'].loadContent = async (content: string | VNode<any>) => {
38
+ newProps['ref'].mountInnerComponent = async (content: string | VNode<any>) => {
42
39
  if (typeof content === 'object' && content.type && content.props) {
43
- // await mountComponents(el, content);
44
- renderComponents(content.type, content.props);
45
- await replaceInnerhtml(el, content.props._html.join(''));
46
- bindAttributes(el, content.type, content.props);
47
- bindLinks(el);
40
+ await mountInnerComponent(el, content);
48
41
  } else {
49
42
  await replaceInnerhtml(el, content as string);
50
43
  }
51
44
  };
45
+ newProps['ref'].mountOuterComponent = async (content: VNode<any>) => {
46
+ await mountOuterComponent(el, content);
47
+ };
52
48
  };
package/src/core/index.ts CHANGED
@@ -5,10 +5,9 @@ export * from './bind-meta';
5
5
  export * from './bind-ref';
6
6
  export * from './bind-styles';
7
7
  export * from './bind-theme';
8
- export * from './component-factory';
9
8
  export * from './export-lupine';
10
9
  export * from './camel-to-hyphens';
11
- export * from './mount-components';
10
+ export * from './mount-component';
12
11
  export * from './page-loaded-events';
13
12
  export * from './page-router';
14
13
  export * from './replace-innerhtml';
@@ -1,7 +1,7 @@
1
1
  import { Logger } from '../lib/logger';
2
2
  import { generateAllGlobalStyles } from './bind-styles';
3
3
  import { defaultThemeName, getCurrentTheme, updateTheme } from './bind-theme';
4
- import { mountComponents } from './mount-components';
4
+ import { mountInnerComponent } from './mount-component';
5
5
  // import { callPageResetEvent } from './page-reset-events';
6
6
  import { PageRouter } from './page-router';
7
7
  import { callPageLoadedEvent } from './page-loaded-events';
@@ -42,7 +42,7 @@ const generatePage = async (props: PageProps, toClientDelivery: IToClientDeliver
42
42
  };
43
43
  }
44
44
 
45
- await mountComponents(null, jsxNodes);
45
+ await mountInnerComponent(null, jsxNodes);
46
46
  const currentTheme = getCurrentTheme();
47
47
  const cssText = generateAllGlobalStyles();
48
48
  const content = jsxNodes.props._html.join('');
@@ -93,7 +93,7 @@ export const initializePage = async (newUrl?: string) => {
93
93
  }
94
94
 
95
95
  // generateAllGlobalStyles will be updated directly in Browser
96
- await mountComponents('.lupine-root', jsxNodes);
96
+ await mountInnerComponent('.lupine-root', jsxNodes);
97
97
  updateTheme(getCurrentTheme().themeName);
98
98
 
99
99
  // title
@@ -0,0 +1,68 @@
1
+ import { bindAttributes } from './bind-attributes';
2
+ import { bindLinks } from './bind-links';
3
+ import { VNode } from '../jsx';
4
+ // import { Logger } from '../lib/logger';
5
+ // import { bindPageResetEvent } from './page-reset-events';
6
+ import { callUnload, replaceInnerhtml } from './replace-innerhtml';
7
+ import { renderComponent } from './render-component';
8
+
9
+ // const logger = new Logger('mount-components');
10
+ export const mountInnerComponent = async (selector: string | null | Element, jsxNodes: VNode<any>) => {
11
+ renderComponent(jsxNodes.type, jsxNodes.props);
12
+ const el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
13
+ if (el) {
14
+ // call unload before releace innerHTML
15
+ await replaceInnerhtml(el, jsxNodes.props._html.join(''));
16
+
17
+ bindAttributes(el, jsxNodes.type, jsxNodes.props);
18
+ bindLinks(el);
19
+ }
20
+ };
21
+
22
+ // suggest to use HtmlVar.
23
+ export const mountOuterComponent = async (selector: string | Element, jsxNodes: VNode<any>) => {
24
+ renderComponent(jsxNodes.type, jsxNodes.props);
25
+ let el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
26
+ if (el) {
27
+ // Can't do outerHTML directly because it will lose attributes
28
+ const template = document.createElement('template');
29
+ // template.innerHTML = jsxNodes.props._html.join("");
30
+ // call unload before releace innerHTML
31
+ await replaceInnerhtml(template, jsxNodes.props._html.join(''));
32
+ // renderComponent should only have one element
33
+ template.content.children.length > 1 &&
34
+ console.error('renderComponent should only have one element: ', template.content.children.length);
35
+ const newEl = template.content.firstChild as Element;
36
+ // el.replaceWith(newEl);
37
+ await callUnload(el);
38
+ el.parentNode?.replaceChild(newEl, el);
39
+ bindAttributes(newEl, jsxNodes.type, jsxNodes.props);
40
+ bindLinks(newEl);
41
+ }
42
+ };
43
+
44
+ // suggest to use HtmlVar.
45
+ export const mountSiblingComponent = async (selector: string | Element, jsxNodes: VNode<any>, position: 'before' | 'after' = 'after') => {
46
+ renderComponent(jsxNodes.type, jsxNodes.props);
47
+ let el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
48
+ if (el) {
49
+ // Can't do outerHTML directly because it will lose attributes
50
+ const template = document.createElement('template');
51
+ // template.innerHTML = jsxNodes.props._html.join("");
52
+ // call unload before releace innerHTML
53
+ await replaceInnerhtml(template, jsxNodes.props._html.join(''));
54
+ // renderComponent should only have one element
55
+ template.content.children.length > 1 &&
56
+ console.error('renderComponent should only have one element: ', template.content.children.length);
57
+ const newEl = template.content.firstChild as Element;
58
+ // el.replaceWith(newEl);
59
+ await callUnload(el);
60
+ if (el.nextSibling || position === 'before') {
61
+ el.parentNode?.insertBefore(newEl, position === 'after' ? el.nextSibling : el);
62
+ } else {
63
+ el.parentNode?.appendChild(newEl);
64
+ }
65
+ bindAttributes(newEl, jsxNodes.type, jsxNodes.props);
66
+ bindLinks(newEl);
67
+ }
68
+ };
@@ -1,7 +1,7 @@
1
1
  import { VNode } from '../jsx';
2
2
  import { isFrontEnd } from '../lib/is-frontend';
3
3
  import { PageProps } from './export-lupine';
4
- import { mountComponents } from './mount-components';
4
+ import { mountInnerComponent } from './mount-component';
5
5
  import { Logger } from '../lib/logger';
6
6
 
7
7
  export type PageRouterCallback = (props: PageProps) => Promise<VNode<any> | null>;
@@ -23,6 +23,7 @@ export class PageRouter {
23
23
  private routerData: PageRouterData[] = [];
24
24
  private filter: PageRouterCallback | undefined;
25
25
  private framePage: FramePageProps | undefined;
26
+ private subDir: string = '';
26
27
 
27
28
  // if the filter returns null (passed filter), the router will continue.
28
29
  // it works in the same way as in use method
@@ -30,6 +31,11 @@ export class PageRouter {
30
31
  this.filter = filter;
31
32
  }
32
33
 
34
+ // if the script is under a sub-dir (without last /), then findRoute needs to remove it from the url
35
+ setSubDir(subDir: string) {
36
+ this.subDir = subDir;
37
+ }
38
+
33
39
  setFramePage(framePage: FramePageProps) {
34
40
  this.framePage = framePage;
35
41
  }
@@ -149,6 +155,9 @@ export class PageRouter {
149
155
  }
150
156
 
151
157
  async handleRoute(url: string, props: PageProps, renderPartPage: boolean): Promise<VNode<any> | null> {
158
+ if (url.startsWith(this.subDir)) {
159
+ url = url.substring(this.subDir.length);
160
+ }
152
161
  let vNode = null;
153
162
  if (this.filter) {
154
163
  vNode = await this.callHandle(this.filter, url, props);
@@ -160,7 +169,7 @@ export class PageRouter {
160
169
  if (vNode && this.framePage) {
161
170
  const selector = '.' + this.framePage.placeholderClassname;
162
171
  if (renderPartPage && isFrontEnd() && document.querySelector(selector)) {
163
- await mountComponents(selector, vNode);
172
+ await mountInnerComponent(selector, vNode);
164
173
  return null;
165
174
  } else {
166
175
  return this.framePage.component(this.framePage.placeholderClassname, vNode);
@@ -25,7 +25,7 @@ function renderChildren(html: string[], children: any) {
25
25
  renderChildren(html, item);
26
26
  }
27
27
  } else if (children.type && children.props) {
28
- renderComponents(children.type, children.props);
28
+ renderComponent(children.type, children.props);
29
29
  html.push(...children.props._html);
30
30
  children.props._html.length = 0;
31
31
  } else {
@@ -137,11 +137,11 @@ function renderAttribute(type: any, props: any, jsxNodes: any) {
137
137
  // }
138
138
 
139
139
  // The result has only one element
140
- export const renderComponents = (type: any, props: any) => {
140
+ export const renderComponent = (type: any, props: any) => {
141
141
  // logger.log("==================renderComponent", type);
142
142
  if (Array.isArray(props)) {
143
143
  const jsxNodes = { type: 'Fragment', props: { children: props } } as any;
144
- renderComponents(jsxNodes.type, jsxNodes.props);
144
+ renderComponent(jsxNodes.type, jsxNodes.props);
145
145
  return;
146
146
  }
147
147
 
@@ -159,7 +159,7 @@ export const renderComponents = (type: any, props: any) => {
159
159
  // }
160
160
  // logger.log('==========props._result', props._result);
161
161
  if (typeof props._result.type === 'function') {
162
- renderComponents(props._result.type, props._result.props);
162
+ renderComponent(props._result.type, props._result.props);
163
163
  if (props._result.props._html) {
164
164
  props._html.push(...props._result.props._html);
165
165
  props._result.props._html.length = 0;
@@ -5,6 +5,14 @@ export const replaceInnerhtml = async (el: Element, newHtml: string) => {
5
5
  firstDom.parentNode?.removeChild(firstDom);
6
6
  }
7
7
 
8
+ await callUnload(el);
9
+ el.innerHTML = newHtml;
10
+
11
+ if (firstDom && firstDom.tagName === 'STYLE') {
12
+ el.insertBefore(firstDom, el.firstChild);
13
+ }
14
+ };
15
+ export const callUnload = async (el: Element) => {
8
16
  const promises: Promise<void>[] = [];
9
17
  el.querySelectorAll('[data-ref]').forEach((child: any) => {
10
18
  if (child._lj && child._lj.onUnload) {
@@ -12,9 +20,4 @@ export const replaceInnerhtml = async (el: Element, newHtml: string) => {
12
20
  }
13
21
  });
14
22
  await Promise.all(promises);
15
- el.innerHTML = newHtml;
16
-
17
- if (firstDom && firstDom.tagName === 'STYLE') {
18
- el.insertBefore(firstDom, el.firstChild);
19
- }
20
23
  };
package/src/jsx.ts CHANGED
@@ -25,7 +25,8 @@ export type RefProps = {
25
25
  onUnload?: (el: Element) => Promise<void>;
26
26
  $?: any; // (selector: string) => undefined | Element,
27
27
  $all?: any; // (selector: string) => undefined | Element,
28
- loadContent?: (content: string | VNode<any>) => Promise<void>;
28
+ mountInnerComponent?: (content: string | VNode<any>) => Promise<void>;
29
+ mountOuterComponent?: (content: VNode<any>) => Promise<void>;
29
30
  };
30
31
 
31
32
  export interface ClassAttributes<T> extends Attributes {
@@ -1,19 +0,0 @@
1
- import { RefProps } from '../jsx';
2
- import { mountComponents } from './mount-components';
3
-
4
- export class ComponentFactory {
5
- private static _session: Map<Function, { [key: string]: any }> = new Map<Function, { [key: string]: any }>();
6
-
7
- static getSession(fn: Function) {
8
- let ret = this._session.get(fn);
9
- if (!ret) {
10
- ret = {};
11
- this._session.set(fn, ret);
12
- }
13
- return ret;
14
- }
15
-
16
- static async refresh(fn: Function, props: any, ref: RefProps) {
17
- mountComponents(ref.current!, await fn(props));
18
- }
19
- }
@@ -1,42 +0,0 @@
1
- import { bindAttributes } from './bind-attributes';
2
- import { bindLinks } from './bind-links';
3
- import { VNode } from '../jsx';
4
- // import { Logger } from '../lib/logger';
5
- // import { bindPageResetEvent } from './page-reset-events';
6
- import { replaceInnerhtml } from './replace-innerhtml';
7
- import { renderComponents } from './render-components';
8
-
9
- // const logger = new Logger('mount-components');
10
- export const mountComponents = async (selector: string | null | Element, jsxNodes: VNode<any>) => {
11
- renderComponents(jsxNodes.type, jsxNodes.props);
12
- const el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
13
- if (el) {
14
- // call unload before releace innerHTML
15
- await replaceInnerhtml(el, jsxNodes.props._html.join(''));
16
-
17
- bindAttributes(el, jsxNodes.type, jsxNodes.props);
18
- bindLinks(el);
19
- }
20
- };
21
-
22
- // // suggest to use HtmlVar.
23
- // export const mountSelfComponents = async (selector: string | null | Element, jsxNodes: VNode<any>) => {
24
- // renderComponents(jsxNodes.type, jsxNodes.props);
25
- // let el = selector && (typeof selector === 'string' ? document.querySelector(selector) : selector);
26
- // if (el) {
27
- // const parentNode = el.parentElement;
28
- // // Can't do outerHTML directly because it will lose attributes
29
- // const template = document.createElement('template');
30
- // // template.innerHTML = jsxNodes.props._html.join("");
31
- // // call unload before releace innerHTML
32
- // await replaceInnerhtml(template, jsxNodes.props._html.join(''));
33
-
34
- // // renderComponent should only have one element
35
- // template.content.children.length > 1 &&
36
- // console.error('renderComponent should only have one element: ', template.content.children.length);
37
- // el.replaceWith(template.content.firstChild as Element);
38
- // el = parentNode as Element;
39
- // bindAttributes(el, jsxNodes.type, jsxNodes.props);
40
- // bindLinks(el);
41
- // }
42
- // };