Skip to content

Commit

Permalink
[gem] Built-in lit-html
Browse files Browse the repository at this point in the history
Closed #206, Closed #145
  • Loading branch information
mantou132 committed Oct 13, 2024
1 parent e2b8cf6 commit f0d339e
Show file tree
Hide file tree
Showing 47 changed files with 2,084 additions and 861 deletions.
2 changes: 0 additions & 2 deletions packages/duoyun-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@
"@mantou/gem": "^2.x"
},
"devDependencies": {
"@esm-bundle/chai": "^4.3.4-fix.0",
"@gemjs/config": "^2.1.0",
"@open-wc/testing": "^2.5.33",
"@types/d3-geo": "^3.0.1",
"@types/mocha": "^10.0.7",
"@web/dev-server-esbuild": "^1.0.2",
Expand Down
5 changes: 2 additions & 3 deletions packages/duoyun-ui/src/elements/collapse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ import {
aria,
} from '@mantou/gem/lib/decorators';
import type { TemplateResult } from '@mantou/gem/lib/element';
import { createCSSSheet, GemElement, html, createState, createRef } from '@mantou/gem/lib/element';
import { createCSSSheet, GemElement, html, createState, createRef, nothing } from '@mantou/gem/lib/element';
import { css, classMap, exportPartsMap } from '@mantou/gem/lib/utils';
import { ifDefined } from '@mantou/gem/lib/directives';

import { icons } from '../lib/icons';
import { theme } from '../lib/theme';
Expand Down Expand Up @@ -118,7 +117,7 @@ export class DuoyunCollapsePanelElement extends GemElement {
class=${classMap({ detail: true, expand })}
ref=${this.#contentRef.ref}
part=${DuoyunCollapsePanelElement.detail}
hidden=${ifDefined(expand ? undefined : 'until-found')}
hidden=${expand ? nothing : 'until-found'}
@beforematch=${this.toggleState}
>
<slot></slot>
Expand Down
3 changes: 1 addition & 2 deletions packages/duoyun-ui/src/elements/file-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import {
slot,
shadow,
} from '@mantou/gem/lib/decorators';
import { createCSSSheet, createRef, GemElement, html } from '@mantou/gem/lib/element';
import { createCSSSheet, createRef, GemElement, html, repeat } from '@mantou/gem/lib/element';
import { css, styleMap } from '@mantou/gem/lib/utils';
import { repeat } from '@mantou/gem/lib/directives';

import { icons } from '../lib/icons';
import { theme } from '../lib/theme';
Expand Down
3 changes: 1 addition & 2 deletions packages/duoyun-ui/src/elements/toast.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { adoptedStyle, aria, customElement, mounted, property, shadow } from '@mantou/gem/lib/decorators';
import type { TemplateResult } from '@mantou/gem/lib/element';
import { GemElement, html, createCSSSheet } from '@mantou/gem/lib/element';
import { GemElement, html, createCSSSheet, repeat } from '@mantou/gem/lib/element';
import { css, classMap, addListener } from '@mantou/gem/lib/utils';
import { repeat } from '@mantou/gem/lib/directives';

import { icons } from '../lib/icons';
import { theme } from '../lib/theme';
Expand Down
2 changes: 1 addition & 1 deletion packages/duoyun-ui/src/lib/cache.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect, aTimeout } from '@open-wc/testing';
import { expect, aTimeout } from '@mantou/gem/test/utils';

import { Cache } from './cache';

Expand Down
2 changes: 1 addition & 1 deletion packages/duoyun-ui/src/lib/color.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@open-wc/testing';
import { expect } from '@mantou/gem/test/utils';

import { parseHexColor, rgbToRgbColor, rgbToHslColor, hsvToRgb, rgbToHsv, rgbToHsl, hslToRgb } from './color';
import { formatToPrecision } from './number';
Expand Down
2 changes: 1 addition & 1 deletion packages/duoyun-ui/src/lib/encode.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@open-wc/testing';
import { expect } from '@mantou/gem/test/utils';

import { arrayBufferToBase64, base64ToArrayBuffer, b64ToUtf8, utf8ToB64 } from './encode';

Expand Down
2 changes: 1 addition & 1 deletion packages/duoyun-ui/src/lib/hotkeys.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@open-wc/testing';
import { expect } from '@mantou/gem/test/utils';

import { matchHotKey } from './hotkeys';

Expand Down
2 changes: 1 addition & 1 deletion packages/duoyun-ui/src/lib/number.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@open-wc/testing';
import { expect } from '@mantou/gem/test/utils';

import { formatNumber, formatToPrecision, adjustRange } from './number';

Expand Down
2 changes: 1 addition & 1 deletion packages/duoyun-ui/src/lib/time.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@open-wc/testing';
import { expect } from '@mantou/gem/test/utils';

import { Time, formatDuration } from './time';

Expand Down
2 changes: 1 addition & 1 deletion packages/duoyun-ui/src/lib/timer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@open-wc/testing';
import { expect } from '@mantou/gem/test/utils';

import { once, omitOnce } from './timer';

Expand Down
2 changes: 1 addition & 1 deletion packages/duoyun-ui/src/lib/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@open-wc/testing';
import { expect } from '@mantou/gem/test/utils';

import { getCascaderDeep, getCascaderBubbleWeakMap, readProp, comparer, ComparerType, isIncludesString } from './utils';

Expand Down
16 changes: 6 additions & 10 deletions packages/duoyun-ui/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,13 @@ export function comparer(a: any, comparerType: ComparerType, b: any): boolean {
}

/**Serialized `TemplateResult` */
export function getStringFromTemplate(o: TemplateResult | string, incorrect = false): string {
if (o instanceof TemplateResult) {
if (incorrect) {
const string = o.getTemplateElement().content.textContent || '';
return string + ' ' + o.values.map((e) => getStringFromTemplate(e as string | TemplateResult)).join(' ');
}
const div = document.createElement('div');
render(o, div);
const div = document.createElement('div');
export function getStringFromTemplate(input: TemplateResult | string): string {
if (input instanceof TemplateResult) {
render(input, div);
return div.textContent || '';
}
return String(o);
return String(input);
}

/**Segmentation search word */
Expand All @@ -208,7 +204,7 @@ export function splitString(s: string) {
/**Search */
export function isIncludesString(origin: string | TemplateResult, search: string, caseSensitive = false) {
const getStr = (s: string) => (caseSensitive ? s : s.toLowerCase()).trim();
const oString = getStr(getStringFromTemplate(origin, true));
const oString = getStr(getStringFromTemplate(origin));
const sString = getStr(search);
return splitString(sString).some((s) => oString.includes(s));
}
Expand Down
5 changes: 2 additions & 3 deletions packages/duoyun-ui/src/patterns/console.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { GemElement, html, createCSSSheet } from '@mantou/gem/lib/element';
import { GemElement, html, createCSSSheet, nothing } from '@mantou/gem/lib/element';
import { adoptedStyle, attribute, boolattribute, customElement, effect, property } from '@mantou/gem/lib/decorators';
import { css } from '@mantou/gem/lib/utils';
import { ifDefined } from '@mantou/gem/lib/directives';
import { mediaQuery } from '@mantou/gem/helper/mediaquery';

import { theme } from '../lib/theme';
Expand Down Expand Up @@ -190,7 +189,7 @@ export class DyPatConsoleElement extends GemElement {
? html`
<div class="user-info">
<dy-avatar class="avatar" alt="Avatar" src=${avatar}></dy-avatar>
<dy-link class="user" href=${ifDefined(this.userInfo.profile)}>
<dy-link class="user" href=${this.userInfo.profile ?? nothing}>
<span class="username" aria-label="Username">${this.userInfo.username}</span>
<span class="org" aria-label="Org" ?hidden=${!this.userInfo.org}>@${this.userInfo.org}</span>
</dy-link>
Expand Down
21 changes: 14 additions & 7 deletions packages/duoyun-ui/src/patterns/form.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { createCSSSheet, html, GemElement, TemplateResult, createState, createRef } from '@mantou/gem/lib/element';
import {
createCSSSheet,
html,
GemElement,
createState,
createRef,
nothing,
TemplateResult,
} from '@mantou/gem/lib/element';
import { adoptedStyle, customElement, memo, property, shadow } from '@mantou/gem/lib/decorators';
import type { StyleObject } from '@mantou/gem/lib/utils';
import { GemError, css, styleMap } from '@mantou/gem/lib/utils';
import { history } from '@mantou/gem/lib/history';
import { ifDefined } from '@mantou/gem/lib/directives';

import { icons } from '../lib/icons';
import { blockContainer, focusStyle } from '../lib/styles';
Expand Down Expand Up @@ -405,11 +412,11 @@ export class DyPatFormElement<T = Record<string, unknown>> extends GemElement {
?searchable=${props.searchable || !!props.getOptions}
?required=${props.required}
?multiple=${props.multiple /**影响值 */}
placeholder=${ifDefined(props.placeholder)}
rows=${ifDefined(props.rows)}
step=${ifDefined(props.step)}
min=${ifDefined(props.min)}
max=${ifDefined(props.max)}
placeholder=${props.placeholder ?? nothing}
rows=${props.rows ?? nothing}
step=${props.step ?? nothing}
min=${props.min ?? nothing}
max=${props.max ?? nothing}
@change=${onChange}
@clear=${onChange}
@search=${onSearch}
Expand Down
2 changes: 0 additions & 2 deletions packages/gem-book/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@
},
"devDependencies": {
"@codesandbox/sandpack-client": "^2.18.2",
"@esm-bundle/chai": "^4.3.4-fix.0",
"@gemjs/config": "^2.1.0",
"@open-wc/testing": "^2.5.33",
"@types/express": "^4.17.3",
"@types/jsdom": "^16.2.10",
"@types/marked": "^2.0.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/gem-book/src/element/test/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect } from '@open-wc/testing';
import { expect } from '@mantou/gem/test/utils';

import { capitalize, flatNav } from '../lib/utils';
import { getUserLink } from '../../common/utils';
Expand Down
1 change: 1 addition & 0 deletions packages/gem/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
/lib/
/elements/
/helper/
/test/
/index.*
2 changes: 1 addition & 1 deletion packages/gem/docs/en/001-guide/002-advance/007-vs-other.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ But, Gem does not have a perfect hot reload solution for the time being.

### Performance

React calculates the vDOM of the entire component when it needs to be updated, then finds the DOM/Node that needs to be modified through comparison, and finally writes it to the DOM/Node. Gem uses a completely different method, see lit-html [document](https://github.com/Polymer/lit-html/wiki/How-it-Works), before mounting, lit-html found the DOM/Node corresponding to the data in the template string. When updating, directly put the content on the target DOM/Node. In addition, Gem uses ShadowDOM, when re-calling `render`, the content of custom elements in the template will be skipped. They are only notified of updates by the Attribute/Property/Store of the element "Observe", which will cause the Gem App to be updated in batches. There is a small task management cost.
React calculates the vDOM of the entire component when it needs to be updated, then finds the DOM/Node that needs to be modified through comparison, and finally writes it to the DOM/Node. Gem uses a completely different method, see lit-html [document](https://github.com/lit/lit/blob/main/dev-docs/design/how-lit-html-works.md), before mounting, lit-html found the DOM/Node corresponding to the data in the template string. When updating, directly put the content on the target DOM/Node. In addition, Gem uses ShadowDOM, when re-calling `render`, the content of custom elements in the template will be skipped. They are only notified of updates by the Attribute/Property/Store of the element "Observe", which will cause the Gem App to be updated in batches. There is a small task management cost.

Using Gem may cause too much ShadowDOM, which may slow down the speed of the App.

Expand Down
4 changes: 2 additions & 2 deletions packages/gem/docs/en/001-guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class MyElement extends GemElement {

Decorator `@customElement` use standard [API](https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements) to define a custom element, and then use it in HTML in any way, of course, it can also be used in other custom element templates.

Return the rendering template in the `render` method. Gem uses [lit-html](https://github.com/Polymer/lit-html) as its template engine. He uses ES6 template strings to write HTML templates. There are no other concepts and no compile-time.
Return the rendering template in the `render` method. Gem uses [lit-html](https://lit.dev/docs/templates/overview/) as its template engine. He uses ES6 template strings to write HTML templates. There are no other concepts and no compile-time.

Use variables:

Expand All @@ -63,7 +63,7 @@ Use event bind:
html`<div @click=${clickHandle}></div>`;
```

More detailed syntax can be found in [lit-html](https://lit-html.polymer-project.org/guide) document.
More detailed syntax can be found in [lit-html](https://lit.dev/docs/templates/overview/) document.

## Are you ready?

Expand Down
8 changes: 3 additions & 5 deletions packages/gem/docs/en/003-api/009-re-export.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Re-Export

Gem’s dependency [`lit-html`](https://github.com/Polymer/lit-html), some APIs are re-exported by Gem:
Gem’s dependency [`lit-html`](https://lit.dev/docs/templates/overview/), some APIs are re-exported by Gem:

| name | description |
| ----------- | -------------------------------------------------------------- |
Expand All @@ -9,11 +9,9 @@ Gem’s dependency [`lit-html`](https://github.com/Polymer/lit-html), some APIs
| `render` | Mount lit-html template to DOM |
| `directive` | Custom lit-html template rendering directive |
| `repeat` | Optimize lit-html list rendering directive |
| `guard` | Directive to prevents re-render of a template |
| `ifDefined` | Directive to if the attribute is not set, delete the attribute |

Other APIs and commands can be imported from lit-html:
Other directives can be imported from lit-html:

```js
import { parts } from 'lit-html';
import { cache } from 'lit-html/directives/cache';
```
2 changes: 1 addition & 1 deletion packages/gem/docs/en/004-blog/000-gem-evolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ With [WebComponents](https://developer.mozilla.org/en-US/docs/Web/Web_Components

## Realization idea

Affected by React, declarative writing of UI components has become popular. Using ES6 template strings, you can get a development experience similar to JSX. Use [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) to parse the template into DOM, after traversing the parsed DOM, you can bind the variables in the ES6 template string with Node to achieve the purpose of updating components. For this, Gem uses [lit-html](https://github.com/Polymer/lit-html) as a template engine.
Affected by React, declarative writing of UI components has become popular. Using ES6 template strings, you can get a development experience similar to JSX. Use [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) to parse the template into DOM, after traversing the parsed DOM, you can bind the variables in the ES6 template string with Node to achieve the purpose of updating components. For this, Gem uses [lit-html](https://lit.dev/docs/templates/overview/) as a template engine.

```js
render(html`<div>${name}</div>`, document.body);
Expand Down
2 changes: 1 addition & 1 deletion packages/gem/docs/zh/001-guide/002-advance/007-vs-other.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Gem 是针对 Web 环境的库,所以它能满足 WebApp 的常见需求,而
### 性能

React 在需要更新时计算整个组件的 vDOM,然后通过比较找到需要修改的 DOM/Node,最后写到 DOM/Node 上,
Gem 使用完全不同的方式,参见 lit-html [文档](https://github.com/Polymer/lit-html/wiki/How-it-Works)
Gem 使用完全不同的方式,参见 lit-html [文档](https://github.com/lit/lit/blob/main/dev-docs/design/how-lit-html-works.md)
在挂载前, lit-html 找到了模板字符串中的数据对应的 DOM/Node,在更新时,直接将内容需要目标 DOM/Node 上,
另外,Gem 使用 ShadowDOM,在重新调用 `render` 时,会跳过模板中的自定义元素的内容,
他们只由该元素“Observe”的 Attribute/Property/Store 通知更新,这会造成 Gem App 是分批次列队更新,
Expand Down
4 changes: 2 additions & 2 deletions packages/gem/docs/zh/001-guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class MyElement extends GemElement {

装饰器 `@customElement` 使用标准的 [API](https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements) 定义一个自定义元素,然后以任何方式在 HTML 中使用他,当然也可以在其他自定义元素的模板中使用。

`render` 方法中返回渲染模版。 Gem 将 [lit-html](https://github.com/Polymer/lit-html) 作为其模版引擎,
`render` 方法中返回渲染模版。 Gem 将 [lit-html](https://lit.dev/docs/templates/overview/) 作为其模版引擎,
他使用 ES6 的模版字符串来编写 HTML 模版,没有其他概念,不存在编译时。

使用变量:
Expand All @@ -68,7 +68,7 @@ html`<div title=${title} .data=${data}></div>`;
html`<div @click=${clickHandle}></div>`;
```

更详细的语法可以查看 [lit-html](https://lit-html.polymer-project.org/guide) 文档。
更详细的语法可以查看 [lit-html](https://lit.dev/docs/templates/overview/) 文档。

## 准备好了吗?

Expand Down
8 changes: 3 additions & 5 deletions packages/gem/docs/zh/003-api/009-re-export.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 重导出

Gem 依赖 [`lit-html`](https://github.com/Polymer/lit-html),部分 API 被 Gem 重新导出:
Gem 依赖 [`lit-html`](https://lit.dev/docs/templates/overview/),部分 API 被 Gem 重新导出:

| 名称 | 描述 |
| ----------- | ------------------------------------------- |
Expand All @@ -9,11 +9,9 @@ Gem 依赖 [`lit-html`](https://github.com/Polymer/lit-html),部分 API 被 Ge
| `render` | 挂载 lit-html 模版到 DOM |
| `directive` | 自定义 lit-html 模版渲染指令 |
| `repeat` | 优化 lit-html 列表渲染指令 |
| `guard` | 阻止重新渲染模版的指令 |
| `ifDefined` | 如果值为为定义,则移除 Attribute 的指令 |

其他 API 和指令可以从 lit-html 中导入:
其他指令可以从 lit-html 中导入:

```js
import { parts } from 'lit-html';
import { cache } from 'lit-html/directives/cache';
```
2 changes: 1 addition & 1 deletion packages/gem/docs/zh/004-blog/000-gem-evolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## 实现思路

受 React 的影响,声明式编写 UI 组件已经深入人心。使用 ES6 的模版字符串,可以获得和 JSX 类似的开发体验,利用 [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) 可以将模版解析成 DOM,遍历解析后的 DOM 可以将 ES6 模版字符串中的变量和 Node 进行绑定,从而实现更新组件的目的,为此,Gem 使用 [lit-html](https://github.com/Polymer/lit-html) 作为模版引擎。
受 React 的影响,声明式编写 UI 组件已经深入人心。使用 ES6 的模版字符串,可以获得和 JSX 类似的开发体验,利用 [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) 可以将模版解析成 DOM,遍历解析后的 DOM 可以将 ES6 模版字符串中的变量和 Node 进行绑定,从而实现更新组件的目的,为此,Gem 使用 [lit-html](https://lit.dev/docs/templates/overview/) 作为模版引擎。

```js
render(html`<div>${name}</div>`, document.body);
Expand Down
6 changes: 0 additions & 6 deletions packages/gem/gem-book.cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
"homeMode": true,
"site": "https://gemjs.org",
"sourceBranch": "docs",
"nav": [
{
"title": "Lit-HTML",
"link": "https://lit-html.polymer-project.org/guide"
}
],
"template": "docs/template.html",
"plugin": ["raw", "docsearch", "sandpack", "code-group", "api"],
"debug": true
Expand Down
9 changes: 4 additions & 5 deletions packages/gem/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"exports": {
".": "./index.js",
"./lib/*": "./lib/*.js",
"./test/*": "./test/*.js",
"./elements/*": "./elements/*.js",
"./helper/*": "./helper/*.js"
},
Expand All @@ -39,16 +40,14 @@
"start": "pnpm build:module --watch",
"test": "cross-env NODE_OPTIONS=--no-experimental-fetch wtr"
},
"dependencies": {
"lit-html": "^1.4.1"
},
"devDependencies": {
"@esm-bundle/chai": "^4.3.4-fix.0",
"@gemjs/config": "^2.1.0",
"@open-wc/testing": "^2.5.33",
"@types/chai": "^5.0.0",
"@types/mocha": "^10.0.7",
"@types/trusted-types": "^2.0.7",
"@web/dev-server-esbuild": "^1.0.2",
"@web/test-runner": "^0.18.1",
"chai": "^5.1.1",
"rimraf": "^3.0.2",
"typescript": "^5.6.2",
"vite": "^5.2.10"
Expand Down
5 changes: 2 additions & 3 deletions packages/gem/src/elements/base/link.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createCSSSheet, GemElement, html } from '../../lib/element';
import { createCSSSheet, GemElement, html, nothing } from '../../lib/element';
import {
attribute,
property,
Expand All @@ -15,7 +15,6 @@ import {
} from '../../lib/decorators';
import { history, basePathStore } from '../../lib/history';
import { absoluteLocation, css } from '../../lib/utils';
import { ifDefined } from '../../lib/directives';

import type { RouteItem, RouteOptions } from './route';
import { matchPath, createHistoryParams, createPath } from './route';
Expand Down Expand Up @@ -162,7 +161,7 @@ export class GemLinkElement extends GemElement {
<a
part=${this.link}
@click=${this.#preventDefault}
href=${ifDefined(this.#hint === 'off' ? undefined : this.#getHint())}
href=${this.#hint === 'off' ? nothing : this.#getHint()}
tabindex="-1"
>
<slot></slot>
Expand Down
1 change: 0 additions & 1 deletion packages/gem/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ export * from './lib/version';

// 使用路径时可选模块
export * from './lib/history';
export * from './lib/directives';
Loading

0 comments on commit f0d339e

Please sign in to comment.