@mantou/gem 1.7.0 → 1.7.2

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.
Files changed (98) hide show
  1. package/dist/gem.es.js +402 -543
  2. package/dist/gem.umd.js +27 -33
  3. package/elements/base/dialog.d.ts +9 -7
  4. package/elements/base/dialog.d.ts.map +1 -1
  5. package/elements/base/dialog.js +12 -3
  6. package/elements/base/dialog.js.map +1 -1
  7. package/elements/base/gesture.d.ts +4 -13
  8. package/elements/base/gesture.d.ts.map +1 -1
  9. package/elements/base/gesture.js +152 -135
  10. package/elements/base/gesture.js.map +1 -1
  11. package/elements/base/link.d.ts +12 -9
  12. package/elements/base/link.d.ts.map +1 -1
  13. package/elements/base/link.js +81 -62
  14. package/elements/base/link.js.map +1 -1
  15. package/elements/base/modal-factory.d.ts +104 -80
  16. package/elements/base/modal-factory.d.ts.map +1 -1
  17. package/elements/base/modal-factory.js +12 -8
  18. package/elements/base/modal-factory.js.map +1 -1
  19. package/elements/base/reflect.js.map +1 -1
  20. package/elements/base/route.d.ts +12 -5
  21. package/elements/base/route.d.ts.map +1 -1
  22. package/elements/base/route.js +17 -5
  23. package/elements/base/route.js.map +1 -1
  24. package/elements/base/title.d.ts +3 -1
  25. package/elements/base/title.d.ts.map +1 -1
  26. package/elements/base/title.js +14 -16
  27. package/elements/base/title.js.map +1 -1
  28. package/elements/base/unsafe.js +1 -1
  29. package/elements/base/unsafe.js.map +1 -1
  30. package/elements/base/use.d.ts +1 -1
  31. package/elements/base/use.d.ts.map +1 -1
  32. package/elements/base/use.js +28 -20
  33. package/elements/base/use.js.map +1 -1
  34. package/helper/i18n.d.ts +4 -4
  35. package/helper/i18n.d.ts.map +1 -1
  36. package/helper/i18n.js.map +1 -1
  37. package/helper/mediaquery.d.ts +16 -18
  38. package/helper/mediaquery.d.ts.map +1 -1
  39. package/helper/mediaquery.js +42 -41
  40. package/helper/mediaquery.js.map +1 -1
  41. package/helper/request.d.ts +1 -1
  42. package/helper/request.d.ts.map +1 -1
  43. package/helper/request.js.map +1 -1
  44. package/helper/ssr-shim.d.ts +2 -0
  45. package/helper/ssr-shim.d.ts.map +1 -0
  46. package/helper/ssr-shim.js +29 -0
  47. package/helper/ssr-shim.js.map +1 -0
  48. package/helper/theme.d.ts +2 -2
  49. package/helper/theme.d.ts.map +1 -1
  50. package/helper/theme.js +20 -24
  51. package/helper/theme.js.map +1 -1
  52. package/index.d.ts +0 -10
  53. package/index.d.ts.map +1 -1
  54. package/index.js +0 -7
  55. package/index.js.map +1 -1
  56. package/lib/decorators.d.ts +4 -4
  57. package/lib/decorators.d.ts.map +1 -1
  58. package/lib/decorators.js +1 -2
  59. package/lib/decorators.js.map +1 -1
  60. package/lib/element.d.ts +16 -10
  61. package/lib/element.d.ts.map +1 -1
  62. package/lib/element.js +57 -79
  63. package/lib/element.js.map +1 -1
  64. package/lib/history.d.ts +1 -1
  65. package/lib/history.d.ts.map +1 -1
  66. package/lib/history.js +2 -2
  67. package/lib/history.js.map +1 -1
  68. package/lib/store.d.ts +2 -2
  69. package/lib/store.d.ts.map +1 -1
  70. package/lib/store.js.map +1 -1
  71. package/lib/utils.d.ts +8 -32
  72. package/lib/utils.d.ts.map +1 -1
  73. package/lib/utils.js +6 -21
  74. package/lib/utils.js.map +1 -1
  75. package/lib/version.d.ts +1 -1
  76. package/lib/version.js +1 -1
  77. package/package.json +2 -2
  78. package/src/elements/base/dialog.ts +10 -3
  79. package/src/elements/base/gesture.ts +52 -46
  80. package/src/elements/base/link.ts +69 -61
  81. package/src/elements/base/modal-factory.ts +5 -0
  82. package/src/elements/base/route.ts +22 -5
  83. package/src/elements/base/title.ts +8 -11
  84. package/src/elements/base/unsafe.ts +1 -1
  85. package/src/elements/base/use.ts +4 -6
  86. package/src/helper/i18n.ts +3 -3
  87. package/src/helper/mediaquery.ts +43 -42
  88. package/src/helper/ssr-shim.ts +29 -0
  89. package/src/helper/theme.ts +22 -24
  90. package/src/index.ts +0 -16
  91. package/src/lib/decorators.ts +1 -2
  92. package/src/lib/element.ts +56 -71
  93. package/src/lib/history.ts +2 -1
  94. package/src/lib/utils.ts +10 -42
  95. package/src/lib/version.ts +1 -1
  96. package/src/test/gem-element/decorators.test.ts +6 -6
  97. package/src/test/history.test.ts +3 -1
  98. package/src/test/utils.test.ts +8 -1
@@ -8,7 +8,6 @@ interface NamePosition {
8
8
  [index: string]: number;
9
9
  }
10
10
 
11
- // TODO: use `URLPattern`
12
11
  class ParamsRegExp extends RegExp {
13
12
  namePosition: NamePosition;
14
13
  constructor(pattern: string) {
@@ -28,10 +27,27 @@ class ParamsRegExp extends RegExp {
28
27
  }
29
28
 
30
29
  type Params = Record<string, string>;
30
+ declare global {
31
+ interface Window {
32
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1731418
33
+ // https://github.com/WebKit/standards-positions/issues/61
34
+ URLPattern: any;
35
+ }
36
+ }
31
37
 
32
- // `/a/b/:c/:d` `/a/b/1/2`
38
+ // URLPattern 大小写敏感
33
39
  // 匹配成功时返回 params
40
+ // `/a/b/:c/:d` `/a/b/1/2`
41
+ // pattern 以 / 结尾时能匹配 2 中路径
42
+ // `/a/b/:c/:d/` `/a/b/1/2`
43
+ // `/a/b/:c/:d/` `/a/b/1/2/`
34
44
  export function matchPath(pattern: string, path: string) {
45
+ if (window.URLPattern) {
46
+ const urLPattern = new window.URLPattern({ pathname: pattern });
47
+ const matchResult = urLPattern.exec({ pathname: path }) || urLPattern.exec({ pathname: `${path}/` });
48
+ if (!matchResult) return null;
49
+ return matchResult.pathname.groups as Params;
50
+ }
35
51
  const reg = new ParamsRegExp(pattern);
36
52
  const matchResult = path.match(reg) || `${path}/`.match(reg);
37
53
  if (!matchResult) return null;
@@ -111,13 +127,14 @@ export class GemRouteElement extends GemElement<State> {
111
127
  * html`<gem-route .locationStore=${locationStore}>`
112
128
  */
113
129
  @property locationStore?: Store<{ path: string; params: Params; query: QueryString; data?: any }>;
114
- @property key: any; // 除了 href 提供另外一种方式来更新,比如语言更新也需要刷新 <gem-route>
130
+ @property key?: any; // 除了 href 提供另外一种方式来更新,比如语言更新也需要刷新 <gem-route>
115
131
  @emitter routechange: Emitter<RouteItem | null>; // path 改变或者 key 改变,包含初始渲染
116
132
  @emitter loading: Emitter<RouteItem>;
117
133
  @emitter error: Emitter<any>;
118
134
 
135
+ /**当前使用的路由对象 */
119
136
  currentRoute: RouteItem | null;
120
- // 当前匹配的路由的 params
137
+ /**当前匹配的路由的 params */
121
138
  currentParams: Params = {};
122
139
 
123
140
  #lastLoader?: Promise<TemplateResult>;
@@ -236,7 +253,7 @@ export class GemRouteElement extends GemElement<State> {
236
253
 
237
254
  return html`
238
255
  <style>
239
- :host,
256
+ :host(:where(:not([hidden]))),
240
257
  :not(:defined) {
241
258
  display: contents;
242
259
  }
@@ -27,26 +27,23 @@ export class GemTitleElement extends GemElement {
27
27
  // 没有后缀的标题
28
28
  static title = document.title;
29
29
  static defaultTitle = document.title;
30
+ static defaultPrefix = `${document.title} | `;
30
31
  static defaultSuffix = ` - ${document.title}`;
31
32
 
32
33
  static setTitle(title: string) {
33
34
  updateStore(titleStore, { title });
34
35
  }
35
36
 
36
- static updateTitle(str = '', suffix = '') {
37
- const { title } = titleStore;
38
- if (title && title !== GemTitleElement.defaultTitle) {
39
- GemTitleElement.title = title;
40
- document.title = GemTitleElement.title + suffix;
41
- } else if (str) {
42
- GemTitleElement.title = str;
43
- document.title = GemTitleElement.title + suffix;
37
+ static updateTitle(title = titleStore.title, prefix = '', suffix = '') {
38
+ if (title === GemTitleElement.defaultTitle) {
39
+ document.title = GemTitleElement.title = title;
44
40
  } else {
45
- GemTitleElement.title = GemTitleElement.defaultTitle;
46
- document.title = GemTitleElement.title;
41
+ GemTitleElement.title = title;
42
+ document.title = prefix + GemTitleElement.title + suffix;
47
43
  }
48
44
  }
49
45
 
46
+ @attribute prefix: string;
50
47
  @attribute suffix: string;
51
48
 
52
49
  constructor() {
@@ -56,7 +53,7 @@ export class GemTitleElement extends GemElement {
56
53
 
57
54
  render() {
58
55
  // 多个 <gem-title> 时,最终 document.title 按执行顺序决定
59
- GemTitleElement.updateTitle(this.textContent || '', this.suffix);
56
+ GemTitleElement.updateTitle(this.textContent || undefined, this.prefix, this.suffix);
60
57
 
61
58
  if (this.hidden) {
62
59
  return html``;
@@ -18,7 +18,7 @@ export class GemUnsafeElement extends GemElement {
18
18
  if (this.shadowRoot) {
19
19
  this.shadowRoot.innerHTML = `
20
20
  <style>
21
- :host {
21
+ :host(:where(:not([hidden]))) {
22
22
  display: contents;
23
23
  }
24
24
  ${this.contentcss}
@@ -18,7 +18,7 @@ export class GemUseElement extends GemElement {
18
18
 
19
19
  @state active: boolean;
20
20
 
21
- private getContent() {
21
+ #getContent = () => {
22
22
  const ele = this.element || (this.selector ? (this.root || document).querySelector(this.selector) : null);
23
23
  if (typeof ele === 'string') {
24
24
  let temp = eleCache.get(ele);
@@ -33,15 +33,13 @@ export class GemUseElement extends GemElement {
33
33
  } else {
34
34
  return ele?.cloneNode(true);
35
35
  }
36
- }
36
+ };
37
37
 
38
38
  render() {
39
39
  return html`
40
40
  <style>
41
- :host {
41
+ :host(:where(:not([hidden]))) {
42
42
  position: relative;
43
- }
44
- :host(:not([hidden])) {
45
43
  display: inline-flex;
46
44
  align-items: center;
47
45
  }
@@ -50,7 +48,7 @@ export class GemUseElement extends GemElement {
50
48
  height: 100%;
51
49
  }
52
50
  </style>
53
- ${this.getContent()}
51
+ ${this.#getContent()}
54
52
  <slot></slot>
55
53
  `;
56
54
  }
@@ -40,7 +40,7 @@ interface Resources<T> {
40
40
  [key: string]: Partial<T> | string;
41
41
  }
42
42
 
43
- interface Options<T> {
43
+ interface I18nOptions<T> {
44
44
  resources: Resources<T>;
45
45
  fallbackLanguage: string;
46
46
  // 可以在构造函数中指定,但会被 URL 中的语言参数覆盖
@@ -107,13 +107,13 @@ export class I18n<T = Record<string, Msg>> {
107
107
  this.onChange?.(lang);
108
108
  }
109
109
 
110
- constructor(options: Options<T>) {
110
+ constructor(options: I18nOptions<T>) {
111
111
  if (!options.resources[options.fallbackLanguage]) {
112
112
  throw new GemError('i18n: fallbackLanguage invalid');
113
113
  }
114
114
 
115
115
  this.store = createStore(this as any);
116
- Object.assign<I18n<T>, Options<T>>(this as I18n<T>, options);
116
+ Object.assign<I18n<T>, I18nOptions<T>>(this as I18n<T>, options);
117
117
 
118
118
  let currentLanguage = this.#urlParamsLang || this.currentLanguage || this.#cacheLang;
119
119
 
@@ -1,77 +1,78 @@
1
1
  // https://gist.github.com/gokulkrishh/242e68d1ee94ad05f488
2
2
  // https://mediag.com/news/popular-screen-resolutions-designing-for-all/
3
3
 
4
- const matche = (query: string) => window.matchMedia(query).matches;
4
+ const match = (query: string) => window.matchMedia(query).matches;
5
5
 
6
6
  export const mediaQuery = {
7
7
  PRINT: 'print',
8
8
  get isPrint() {
9
- return matche(this.PRINT);
9
+ return match(this.PRINT);
10
10
  },
11
+
11
12
  HOVER: '(hover: hover)',
12
13
  get isHover() {
13
- return matche(this.HOVER);
14
- },
15
-
16
- PHONE: '(min-width: 319px) and (max-width: 480px)',
17
- get isPhone() {
18
- return matche(this.PHONE);
14
+ return match(this.HOVER);
19
15
  },
20
16
 
21
- LAPTOP: '(min-width: 1023px) and (max-width: 1280px) and (orientation: landscape)',
22
- get isLaptop() {
23
- return matche(this.LAPTOP);
17
+ PWA: '(display-mode: standalone)',
18
+ // PWA: '(display-mode: browser)', // debugging
19
+ get isPWA() {
20
+ return match(this.PWA);
24
21
  },
25
22
 
26
- DESKTOP: '(min-width: 1279px) and (orientation: landscape)',
27
- get isDesktop() {
28
- return matche(this.DESKTOP);
23
+ MOTION_REDUCE: '(prefers-reduced-motion: reduce)',
24
+ get isMotionReduce() {
25
+ return match(this.MOTION_REDUCE);
29
26
  },
30
27
 
31
- WIDTHSCREEN: '(min-width: 1919px) and (orientation: landscape)',
32
- get isWidthScreen() {
33
- return matche(this.WIDTHSCREEN);
28
+ DATA_REDUCE: '(prefers-reduced-data: reduce)',
29
+ get isDataReduce() {
30
+ return match(this.DATA_REDUCE);
34
31
  },
35
32
 
36
- PHONE_LANDSCAPE: '(min-width: 479px) and (max-width: 959px) and (orientation: landscape)',
37
- // PHONE_LANDSCAPE: '(min-width: 481px) and (max-width: 959px)',
38
- get isPhoneLandscape() {
39
- return matche(this.PHONE_LANDSCAPE);
33
+ WATCH: '(width < 320px)',
34
+ get isWatch() {
35
+ return match(this.WATCH);
40
36
  },
41
37
 
42
- TABLET: '(min-width: 768px) and (max-width: 1024px)',
43
- get isTablet() {
44
- return matche(this.TABLET);
38
+ SMALL_PHONE: '(320px <= width <= 480px) and (height <= 640px)',
39
+ get isSmallPhone() {
40
+ return match(this.SMALL_PHONE);
45
41
  },
46
42
 
47
- SMALL_PHONE: '(min-width: 319px) and (max-width: 480px) and (max-height: 640px)',
48
- get isSmallPhone() {
49
- return matche(this.SMALL_PHONE);
43
+ PHONE: '(320px <= width <= 480px)',
44
+ get isPhone() {
45
+ return match(this.PHONE);
50
46
  },
51
47
 
52
- WATCH: '(max-width: 319px)',
53
- get isWatch() {
54
- return matche(this.WATCH);
48
+ PHONE_LANDSCAPE: '(480px < width < 960px) and (orientation: landscape)',
49
+ // PHONE_LANDSCAPE: '(480px < width < 960px)',
50
+ get isPhoneLandscape() {
51
+ return match(this.PHONE_LANDSCAPE);
55
52
  },
56
53
 
57
- SHORT: '(min-width: 479px) and (max-height: 320px)',
54
+ // Widget
55
+ SHORT: '(width >= 480px) and (height <= 320px)',
58
56
  get isShort() {
59
- return matche(this.SHORT);
57
+ return match(this.SHORT);
60
58
  },
61
59
 
62
- PWA: '(display-mode: standalone)',
63
- // PWA: '(display-mode: browser)', // debugging
64
- get isPWA() {
65
- return matche(this.PWA);
60
+ // 1280 x 850
61
+ // 1024 x 768
62
+ TABLET: '(480px < width <= 1280px)',
63
+ get isTablet() {
64
+ return match(this.TABLET);
66
65
  },
67
66
 
68
- MOTION_REDUCE: '(prefers-reduced-motion: reduce)',
69
- get isMotionReduce() {
70
- return matche(this.MOTION_REDUCE);
67
+ // 1366x768
68
+ DESKTOP: '(width > 1280px) and (orientation: landscape)',
69
+ get isDesktop() {
70
+ return match(this.DESKTOP);
71
71
  },
72
72
 
73
- DATA_REDUCE: '(prefers-reduced-data: reduce)',
74
- get isDataReduce() {
75
- return matche(this.DATA_REDUCE);
73
+ // 1920x1080
74
+ WIDTHSCREEN: '(width >= 1920px) and (orientation: landscape)',
75
+ get isWidthScreen() {
76
+ return match(this.WIDTHSCREEN);
76
77
  },
77
78
  };
@@ -0,0 +1,29 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ if (typeof window === 'undefined') {
4
+ const getAny = () => {
5
+ const anonymous = () => getAny();
6
+ Object.defineProperties(anonymous, {
7
+ length: { writable: true },
8
+ name: { writable: true },
9
+ });
10
+ return new Proxy(anonymous, {
11
+ get(target, key) {
12
+ if (key === Symbol.toPrimitive) return () => '';
13
+ return target[key] || (target[key] = getAny());
14
+ },
15
+ });
16
+ };
17
+ const any = getAny();
18
+
19
+ globalThis.document = any;
20
+ globalThis.addEventListener = any;
21
+ globalThis.history = any;
22
+ globalThis.navigator = any;
23
+ globalThis.customElements = any;
24
+ globalThis.HTMLElement = null;
25
+ globalThis.location = new URL('about:blank');
26
+ globalThis.window = globalThis;
27
+ }
28
+
29
+ export {};
@@ -11,21 +11,13 @@ export function getThemeStore<T>(theme: SomeType<T>) {
11
11
  return themeStoreMap.get(theme) as Store<T>;
12
12
  }
13
13
 
14
- const themePropMap = new WeakMap();
14
+ const themePropsMap = new WeakMap();
15
15
 
16
- export function getThemeProp<T>(theme: SomeType<T>) {
17
- return themePropMap.get(theme) as SomeType<T>;
16
+ export function getThemeProps<T>(theme: SomeType<T>) {
17
+ return themePropsMap.get(theme) as SomeType<T>;
18
18
  }
19
19
 
20
- function replaceStyle(style: HTMLStyleElement, theme: Record<string, unknown>) {
21
- const themeStore = themeStoreMap.get(theme);
22
- const themeProp = themePropMap.get(theme);
23
- const rules = Object.keys(themeStore).reduce((prev, key) => {
24
- if (!themeStore[key] || !themeProp[key]) return prev;
25
- return prev + `${themeProp[key]}:${themeStore[key]};`;
26
- }, '');
27
- style.textContent = `:root, :host {${rules}}`;
28
- }
20
+ const setThemeFnMap = new WeakMap();
29
21
 
30
22
  /**
31
23
  * 创建主题,插入 `document.head`
@@ -39,17 +31,23 @@ function replaceStyle(style: HTMLStyleElement, theme: Record<string, unknown>) {
39
31
  export function createTheme<T extends Record<string, unknown>>(themeObj: T) {
40
32
  const salt = randomStr();
41
33
  const style = document.createElement('style');
42
- const themeStore = createStore<T>(themeObj);
34
+ const store = createStore<T>(themeObj);
43
35
  const theme: Record<string, string> = {};
44
- const themeProp: Record<string, string> = {};
45
- themePropMap.set(theme, themeProp);
46
- themeStoreMap.set(theme, themeStore);
47
- Object.keys(themeStore).forEach((key) => {
48
- themeProp[key] = `--${camelToKebabCase(key)}-${salt}`;
49
- theme[key] = `var(${themeProp[key]})`;
50
- });
51
- const replace = () => replaceStyle(style, theme);
52
- connect(themeStore, replace);
36
+ const props: Record<string, string> = {};
37
+ themePropsMap.set(theme, props);
38
+ themeStoreMap.set(theme, store);
39
+ const setTheme = () =>
40
+ Object.keys(store).forEach((key) => {
41
+ if (props[key]) return;
42
+ props[key] = `--${camelToKebabCase(key)}-${salt}`;
43
+ theme[key] = `var(${props[key]})`;
44
+ });
45
+ setThemeFnMap.set(theme, setTheme);
46
+ setTheme();
47
+ const getStyle = () =>
48
+ `:root, :host {${Object.keys(store).reduce((prev, key) => prev + `${props[key]}:${store[key]};`, '')}}`;
49
+ const replace = () => (style.textContent = getStyle());
50
+ connect(store, replace);
53
51
  replace();
54
52
  (document.head || document.documentElement).append(style);
55
53
  return theme as SomeType<T>;
@@ -61,6 +59,6 @@ export function createTheme<T extends Record<string, unknown>>(themeObj: T) {
61
59
  * @param newThemeObj 新主题
62
60
  */
63
61
  export function updateTheme<T = Record<string, unknown>>(theme: SomeType<T>, newThemeObj: Partial<T>) {
64
- const store = getThemeStore(theme);
65
- updateStore(store, newThemeObj);
62
+ updateStore(getThemeStore(theme), newThemeObj);
63
+ setThemeFnMap.get(theme)();
66
64
  }
package/src/index.ts CHANGED
@@ -1,19 +1,3 @@
1
- /**
2
- * 不支持多个 gem
3
- */
4
- import * as Gem from './';
5
-
6
- export type DevToolsHook = typeof Gem;
7
- declare global {
8
- interface Window {
9
- __GEM_DEVTOOLS__HOOK__?: DevToolsHook;
10
- }
11
- }
12
-
13
- if (window.__GEM_DEVTOOLS__HOOK__) {
14
- Object.assign(window.__GEM_DEVTOOLS__HOOK__, { ...Gem });
15
- }
16
-
17
1
  export * from './lib/store';
18
2
  export * from './lib/history';
19
3
  export * from './lib/element';
@@ -191,7 +191,6 @@ export function adoptedStyle(style: Sheet<unknown>) {
191
191
  * ```
192
192
  */
193
193
  export function connectStore(store: Store<unknown>) {
194
- // 这里的签名该怎么写?
195
194
  return function (cls: unknown) {
196
195
  const con = cls as GemElementConstructor;
197
196
  pushStaticField(con.prototype, 'observedStores', store);
@@ -203,7 +202,7 @@ export function connectStore(store: Store<unknown>) {
203
202
  *
204
203
  * For example
205
204
  * ```ts
206
- * @rootElement(MyElement)
205
+ * @rootElement('my-element')
207
206
  * class App extends GemElement {}
208
207
  * ```
209
208
  */