Skip to content

Commit

Permalink
[gem] Remove GemElement static member
Browse files Browse the repository at this point in the history
Closed #166
  • Loading branch information
mantou132 committed Jun 23, 2024
1 parent 417d5ef commit 5be1567
Show file tree
Hide file tree
Showing 21 changed files with 147 additions and 173 deletions.
4 changes: 2 additions & 2 deletions packages/duoyun-ui/src/lib/element.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GemElement } from '@mantou/gem/lib/element';
import { GemElement, gemSymbols } from '@mantou/gem/lib/element';

export function getBoundingClientRect(eleList: Element[]) {
const rects = eleList.map((e) => e.getBoundingClientRect());
Expand All @@ -13,7 +13,7 @@ export function getBoundingClientRect(eleList: Element[]) {
export function toggleActiveState(ele: Element | undefined | null, active: boolean) {
if (!ele) return;
if (ele instanceof GemElement) {
if ((ele.constructor as typeof GemElement).definedCSSStates?.includes('active')) {
if (Reflect.get(ele.constructor, gemSymbols.definedCSSStates)?.includes('active')) {
(ele as any).active = active;
}
if (['button', 'combobox'].includes(ele.role || ele.internals.role || '')) {
Expand Down
1 change: 1 addition & 0 deletions packages/gem-devtools/src/elements/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const style = createCSSSheet(css`
:host {
display: flex;
line-height: 1.5;
padding-inline: 0.5em;
}
.title {
font-style: italic;
Expand Down
2 changes: 1 addition & 1 deletion packages/gem-devtools/src/modules/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class Panel extends GemElement {
`;
}
return html`
<devtools-header></devtools-header>
<devtools-header>${panelStore.gemVersion}</devtools-header>
<devtools-section name="Observed Attributes" .items=${panelStore.observedAttributes}></devtools-section>
<devtools-section name="Observed Properties" .items=${panelStore.observedProperties}></devtools-section>
<devtools-section name="Observed Stores" .items=${panelStore.observedStores}></devtools-section>
Expand Down
70 changes: 31 additions & 39 deletions packages/gem-devtools/src/scripts/get-gem.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import type { GemElement, SheetToken } from '@mantou/gem';
import type { GemElement, SheetToken, Sheet, Store } from '@mantou/gem';

import type { PanelStore } from '../store';

declare let $0: any;

// 不要使用作用域外的变量
export const getSelectedGem = function (data: PanelStore, gemElementSymbols: string[]): PanelStore | string {
export const getSelectedGem = function (data: PanelStore): PanelStore | string {
// https://github.com/bramus/scroll-driven-animations-debugger-extension/issues/19
if (!$0) return `Not Gem: $0 is ${$0}`;
const tagClass = $0.constructor as typeof GemElement;
const devToolsHook = window.__GEM_DEVTOOLS__HOOK__;
if (devToolsHook) {
if (!devToolsHook.GemElement || !($0 instanceof devToolsHook.GemElement)) return 'Not Gem: gem hook';
const { __GEM_DEVTOOLS__HOOK__ } = window;
if (__GEM_DEVTOOLS__HOOK__) {
const { GemElement } = __GEM_DEVTOOLS__HOOK__;
if (!GemElement || !($0 instanceof GemElement)) return 'Not Gem: gem hook';
} else {
// 依赖 `constructor`,如果 `constructor` 被破坏,则扩展不能工作
// 没有严格检查是否是 GemElement
if (!(($0 as any) instanceof HTMLElement)) return 'Not Gem: not HTMLElement';

const elementSymbols = new Set(Object.getOwnPropertySymbols($0).map(String));
if (gemElementSymbols.some((symbol) => !elementSymbols.has(symbol))) return 'Not Gem: some symbol diff';
}

const tagClass = $0.constructor as typeof GemElement;
const { get } = Reflect;
// support v1
const gemSymbols = new Proxy(get(__GEM_DEVTOOLS__HOOK__ || {}, 'gemSymbols') || {}, {
get(target, p) {
return get(target, p) || p;
},
});

const inspectable = (value: any) => {
const type = typeof value;
return (type === 'object' && value) || type === 'function';
Expand Down Expand Up @@ -116,7 +121,7 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
const buildInProperty = new Set(['internals']);
const buildInAttribute = new Set(['ref']);
const memberSet = getProps($0);
tagClass.observedAttributes?.forEach((attr) => {
get(tagClass, gemSymbols.observedAttributes)?.forEach((attr: string) => {
const prop = kebabToCamelCase(attr);
const value = $0[prop];
memberSet.delete(prop);
Expand All @@ -137,7 +142,7 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
buildIn: buildInAttribute.has(attr) ? 1 : 0,
});
});
tagClass.observedProperties?.forEach((prop) => {
get(tagClass, gemSymbols.observedProperties)?.forEach((prop: string) => {
memberSet.delete(prop);
const value = $0[prop];
const type = value === null ? 'null' : typeof value;
Expand All @@ -148,7 +153,7 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
path: inspectable(value) ? [prop] : undefined,
});
});
tagClass.definedEvents?.forEach((event) => {
get(tagClass, gemSymbols.definedEvents)?.forEach((event: string) => {
const prop = kebabToCamelCase(event);
memberSet.delete(prop);
data.emitters.push({
Expand All @@ -158,23 +163,23 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
path: [prop],
});
});
tagClass.adoptedStyleSheets?.forEach((sheet, index) => {
get(tagClass, gemSymbols.adoptedStyleSheets)?.forEach((sheet: Sheet<unknown>, index: number) => {
data.adoptedStyles.push({
name: `StyleSheet${index + 1}`,
value: objectToString(sheet[Object.getOwnPropertySymbols(sheet)[0] as typeof SheetToken]),
type: 'object',
path: ['constructor', 'adoptedStyleSheets', String(index)],
path: ['constructor', 'gem@adoptedStyleSheets', String(index)],
});
});
tagClass.observedStores?.forEach((store, index) => {
get(tagClass, gemSymbols.observedStores)?.forEach((store: Store<unknown>, index: number) => {
data.observedStores.push({
name: `Store${index + 1}`,
value: objectToString(store),
type: 'object',
path: ['constructor', 'observedStores', String(index)],
path: ['constructor', 'gem@observedStores', String(index)],
});
});
tagClass.definedSlots?.forEach((slot) => {
get(tagClass, gemSymbols.definedSlots)?.forEach((slot: string) => {
const isUnnamed = slot === 'unnamed';
const prop = kebabToCamelCase(slot);
if (!$0.constructor[prop]) {
Expand All @@ -194,7 +199,7 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
path: isNode ? ['firstChild'] : element ? ['querySelector', selector] : undefined,
});
});
tagClass.definedParts?.forEach((part) => {
get(tagClass, gemSymbols.definedParts)?.forEach((part: string) => {
const prop = kebabToCamelCase(part);
if (!$0.constructor[prop]) {
memberSet.delete(prop);
Expand All @@ -207,7 +212,7 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
path: [['shadowRoot', ''], 'querySelector', selector],
});
});
tagClass.definedRefs?.forEach((ref) => {
get(tagClass, gemSymbols.definedRefs)?.forEach((ref: string) => {
const prop = kebabToCamelCase(ref.replace(/-\w+$/, ''));
memberSet.delete(prop);
data.refs.push({
Expand All @@ -217,7 +222,7 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
path: [['shadowRoot', ''], 'querySelector', `[ref=${$0[prop].ref}]`],
});
});
tagClass.definedCSSStates?.forEach((state) => {
get(tagClass, gemSymbols.definedCSSStates)?.forEach((state: string) => {
const prop = kebabToCamelCase(state);
memberSet.delete(prop);
data.cssStates.push({
Expand Down Expand Up @@ -270,27 +275,13 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
});
});

const buildInStaticMember = new Set([
'length',
'name',
'prototype',
'observedAttributes',
'observedProperties',
'observedStores',
'adoptedStyleSheets',
'definedEvents',
'definedCSSStates',
'definedRefs',
'definedParts',
'definedSlots',
]);
const buildInShowStaticMember = new Set(['rootElement']);
const buildInStaticMember = new Set(['length', 'name', 'prototype']);
const getStaticMember = (cls: any, set = new Set<string>()) => {
Object.getOwnPropertyNames(cls).forEach((key) => {
if (
!buildInStaticMember.has(key) &&
!tagClass.definedParts?.includes(cls[key]) &&
!tagClass.definedSlots?.includes(cls[key])
!get(tagClass, gemSymbols.definedParts)?.includes(cls[key]) &&
!get(tagClass, gemSymbols.definedSlots)?.includes(cls[key])
) {
set.add(key);
}
Expand All @@ -307,7 +298,7 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
type: typeof value,
value: objectToString(value),
path: inspectable(value) ? ['constructor', key] : undefined,
buildIn: buildInShowStaticMember.has(key) ? 1 : 0,
buildIn: 0,
});
});
// `Class` self
Expand All @@ -317,5 +308,6 @@ export const getSelectedGem = function (data: PanelStore, gemElementSymbols: str
value: objectToString(tagClass),
path: ['constructor'],
});
data.gemVersion = __GEM_DEVTOOLS__HOOK__?.version ? `v${__GEM_DEVTOOLS__HOOK__.version}` : '';
return data;
};
3 changes: 2 additions & 1 deletion packages/gem-devtools/src/scripts/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export function preload() {
// [["shadowRoot", ""], "querySelector", "[ref=child-ref]"]
// 只有 constructor 函数会当成对象读取
readProp(path) {
return path.reduce((p, c, index) => {
return path.reduce((p, k, index) => {
const c = typeof k === 'string' && k.startsWith('gem@') ? Symbol.for(k) : k;
if (typeof p === 'function' && path[index - 1] !== 'constructor') {
if (Array.isArray(c)) {
return p(...c);
Expand Down
10 changes: 2 additions & 8 deletions packages/gem-devtools/src/sidebarpanel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { devtools } from 'webextension-polyfill';
import { customElement, GemElement, html, render } from '@mantou/gem';
import { html, render } from '@mantou/gem';
import { logger } from '@mantou/gem/helper/logger';

import { getSelectedGem } from './scripts/get-gem';
Expand All @@ -10,15 +10,9 @@ import { execution } from './common';

import './modules/panel';

@customElement('devtools-gem-discover')
class GemDiscover extends GemElement {}

async function updateElementProperties() {
try {
const result = await execution(getSelectedGem, [
new PanelStore(),
Object.getOwnPropertySymbols(new GemDiscover()).map(String),
]);
const result = await execution(getSelectedGem, [new PanelStore()]);
if (typeof result !== 'string') {
changePanelStore(result);
} else {
Expand Down
1 change: 1 addition & 0 deletions packages/gem-devtools/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class PanelStore {
Object.assign(this, options);
}
isGemElement = true;
gemVersion = '';
elements = new Array<string>();
customElements = new Array<string>();
gemElements = new Array<string>();
Expand Down
6 changes: 2 additions & 4 deletions packages/gem-examples/src/elements/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { GemElement, html, customElement } from '@mantou/gem';

import { EXAMPLES, VERSION } from './env';

const getGitPageUrl = (name: string) => `../${name}/`;

@customElement('gem-examples-nav')
export class Nav extends GemElement {
mounted = () => {
Expand Down Expand Up @@ -78,9 +76,9 @@ export class Nav extends GemElement {
<div class="group-title"><span>${group}</span></div>
<ol>
${(examples as typeof EXAMPLES).map(
({ name = '' }) => html`
({ path = '', name = '' }) => html`
<li>
<a class=${location.pathname.includes(name) ? 'active' : ''} href=${getGitPageUrl(name)}>
<a class=${location.pathname.includes(path) ? 'active' : ''} href=${`../${path}/`}>
<div>${name.replace('-', ' ')}</div>
</a>
</li>
Expand Down
1 change: 1 addition & 0 deletions packages/gem-examples/src/hello-world/manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"path": "",
"order": -1,
"name": "hello world",
"group": "",
Expand Down
4 changes: 2 additions & 2 deletions packages/gem-examples/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export default defineConfig({
'process.env.EXAMPLES': JSON.stringify(
examples.map((example) => {
try {
return { name: example, ...require(`./src/${example}/manifest.json`) };
return { name: example, ...require(`./src/${example}/manifest.json`), path: example };
} catch {
return { name: example };
return { path: example, name: example };
}
}),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ update({ a: 2 });
disconnect();
```

As mentioned in the previous section, use `static observedStores`/`@connectStore` to connect to `Store`, in fact, their role is only to register the `update` method of the`GemElement` instance, therefore, when the `Store` is updated, the instance of the `GemElement` connected to the `Store` will call `update` to achieve automatic update.
As mentioned in the previous section, use `@connectStore` to connect to `Store`, in fact, their role is only to register the `update` method of the`GemElement` instance, therefore, when the `Store` is updated, the instance of the `GemElement` connected to the `Store` will call `update` to achieve automatic update.

## Planning the store

Expand Down
11 changes: 0 additions & 11 deletions packages/gem/docs/en/003-api/001-gem-element.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@ class GemElement<State> extends HTMLElement {
| `delegatesFocus` | When the element attempts to focus, the automatic proxy to the focus part |
| `slotAssignment` | Allow manual allocation of slot |

## Static properties

| name | description |
| -------------------- | ------------------------------------------------------------- |
| `observedStores` | Observe the specified `Store`, re-rendered by `Store` changes |
| `adoptedStyleSheets` | See [`DocumentOrShadowRoot.adoptedStyleSheets`][1] |

[1]: https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/adoptedStyleSheets

_Use the [decorator](./007-decorator.md) to instead_

## Lifecycle hook

| name | description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ update({ a: 2 });
disconnect();
```

前一节有提到,使用 `static observedStores`/`@connectStore` 来连接 `Store`
前一节有提到,使用 `@connectStore` 来连接 `Store`
实际上,他们的作用只是注册 `GemElement` 实例的 `update` 方法,
所以,当 `Store` 更新时,连接 `Store``GemElement` 的实例会调用 `update`,从而实现自动更新。

Expand Down
11 changes: 0 additions & 11 deletions packages/gem/docs/zh/003-api/001-gem-element.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@ class GemElement<State> extends HTMLElement {
| `delegatesFocus` | 当元素尝试聚焦时自动代理到可聚焦部分 |
| `slotAssignment` | 允许手动分配插槽 |

## 静态属性

| 名称 | 描述 |
| -------------------- | ----------------------------------------------------------- |
| `observedStores` | 监听指定的 `Store`, 当被监听的 `Store` 变化时元素将重新渲染 |
| `adoptedStyleSheets` |[`DocumentOrShadowRoot.adoptedStyleSheets`][1] |

[1]: https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/adoptedStyleSheets

_可以使用等效的[装饰器](./007-decorator.md)替代_

## 生命周期钩子

| 名称 | 描述 |
Expand Down
Loading

0 comments on commit 5be1567

Please sign in to comment.