diff --git a/packages/duoyun-ui/docs/en/02-elements/result.md b/packages/duoyun-ui/docs/en/02-elements/result.md
index f59146c5..6b00631a 100644
--- a/packages/duoyun-ui/docs/en/02-elements/result.md
+++ b/packages/duoyun-ui/docs/en/02-elements/result.md
@@ -2,10 +2,18 @@
## Example
-
+
+
+```json
+{
+ "icon": "icons.check",
+ "status": "positive",
+ "header": "Complete!",
+ "description": "Do non amet sunt nostrud excepteur ut nostrud veniam aliquip commodo incididunt."
+}
+```
+
+
## API
diff --git a/packages/duoyun-ui/docs/en/02-elements/select.md b/packages/duoyun-ui/docs/en/02-elements/select.md
index 92f0e1d5..e2af478d 100644
--- a/packages/duoyun-ui/docs/en/02-elements/select.md
+++ b/packages/duoyun-ui/docs/en/02-elements/select.md
@@ -2,10 +2,28 @@
## Example
-
+
+
+```json
+[
+ {
+ "searchable": true,
+ "multiple": true,
+ "adder": { "text": "New" },
+ "placeholder": "Please select!",
+ "options": [{ "label": "Option 1" }, { "label": "Option 2" }],
+ "@change": "(evt) => evt.target.value = evt.detail"
+ },
+ {
+ "inline": true,
+ "adder": { "text": "New" },
+ "options": [{ "label": "Option 1" }, { "label": "Option 2" }],
+ "@change": "(evt) => evt.target.value = evt.detail"
+ }
+]
+```
+
+
## API
diff --git a/packages/duoyun-ui/docs/en/02-elements/side-navigation.md b/packages/duoyun-ui/docs/en/02-elements/side-navigation.md
index f62121ca..ed81ecc6 100644
--- a/packages/duoyun-ui/docs/en/02-elements/side-navigation.md
+++ b/packages/duoyun-ui/docs/en/02-elements/side-navigation.md
@@ -2,10 +2,28 @@
## Example
-
+
+
+```json
+{
+ "style": "width: 240px;",
+ "items": [
+ { "title": "Page 1", "hash": "#1" },
+ { "title": "Page 2", "hash": "#2" },
+ { "title": "Page 3", "hash": "#3" },
+ {
+ "title": "Group 2",
+ "group": [
+ { "title": "Route 1", "hash": "#4" },
+ { "title": "Route 2", "hash": "#5" },
+ { "title": "Route 3", "hash": "#6" }
+ ]
+ }
+ ]
+}
+```
+
+
## API
diff --git a/packages/duoyun-ui/docs/zh/02-elements/result.md b/packages/duoyun-ui/docs/zh/02-elements/result.md
index f59146c5..6b00631a 100644
--- a/packages/duoyun-ui/docs/zh/02-elements/result.md
+++ b/packages/duoyun-ui/docs/zh/02-elements/result.md
@@ -2,10 +2,18 @@
## Example
-
+
+
+```json
+{
+ "icon": "icons.check",
+ "status": "positive",
+ "header": "Complete!",
+ "description": "Do non amet sunt nostrud excepteur ut nostrud veniam aliquip commodo incididunt."
+}
+```
+
+
## API
diff --git a/packages/duoyun-ui/docs/zh/02-elements/select.md b/packages/duoyun-ui/docs/zh/02-elements/select.md
index 3ec12f2f..4ac2d979 100644
--- a/packages/duoyun-ui/docs/zh/02-elements/select.md
+++ b/packages/duoyun-ui/docs/zh/02-elements/select.md
@@ -2,10 +2,28 @@
## Example
-
+
+
+```json
+[
+ {
+ "searchable": true,
+ "multiple": true,
+ "adder": { "text": "添加" },
+ "placeholder": "请选择",
+ "options": [{ "label": "Option 1" }, { "label": "Option 2" }],
+ "@change": "(evt) => evt.target.value = evt.detail"
+ },
+ {
+ "inline": true,
+ "adder": { "text": "添加" },
+ "options": [{ "label": "Option 1" }, { "label": "Option 2" }],
+ "@change": "(evt) => evt.target.value = evt.detail"
+ }
+]
+```
+
+
## API
diff --git a/packages/duoyun-ui/docs/zh/02-elements/side-navigation.md b/packages/duoyun-ui/docs/zh/02-elements/side-navigation.md
index f62121ca..ed81ecc6 100644
--- a/packages/duoyun-ui/docs/zh/02-elements/side-navigation.md
+++ b/packages/duoyun-ui/docs/zh/02-elements/side-navigation.md
@@ -2,10 +2,28 @@
## Example
-
+
+
+```json
+{
+ "style": "width: 240px;",
+ "items": [
+ { "title": "Page 1", "hash": "#1" },
+ { "title": "Page 2", "hash": "#2" },
+ { "title": "Page 3", "hash": "#3" },
+ {
+ "title": "Group 2",
+ "group": [
+ { "title": "Route 1", "hash": "#4" },
+ { "title": "Route 2", "hash": "#5" },
+ { "title": "Route 3", "hash": "#6" }
+ ]
+ }
+ ]
+}
+```
+
+
## API
diff --git a/packages/duoyun-ui/src/elements/base/wake-lock.ts b/packages/duoyun-ui/src/elements/base/wake-lock.ts
index 661f9a9f..e9e16138 100644
--- a/packages/duoyun-ui/src/elements/base/wake-lock.ts
+++ b/packages/duoyun-ui/src/elements/base/wake-lock.ts
@@ -2,6 +2,7 @@
import { GemElementOptions } from '@mantou/gem/lib/element';
import { logger } from '@mantou/gem/helper/logger';
+import { addListener } from '@mantou/gem/lib/utils';
import { DuoyunVisibleBaseElement } from './visible';
@@ -25,13 +26,13 @@ export function wakeLock(ele: DuoyunWakeLockBaseElement) {
};
// 当页面处于非活动状态时该锁自动失效
- document.addEventListener('visibilitychange', listener);
- ele.addEventListener('show', listener);
- ele.addEventListener('hide', listener);
+ const removeListener = addListener(document, 'visibilitychange', listener);
+ const removeShowListener = addListener(ele, 'show', listener);
+ const removeHideListener = addListener(ele, 'hide', listener);
return async () => {
- document.removeEventListener('visibilitychange', listener);
- ele.removeEventListener('show', listener);
- ele.removeEventListener('hide', listener);
+ removeListener();
+ removeShowListener();
+ removeHideListener();
(await wakeLockPromise)?.release();
};
diff --git a/packages/duoyun-ui/src/elements/carousel.ts b/packages/duoyun-ui/src/elements/carousel.ts
index 09b4353a..1a77a941 100644
--- a/packages/duoyun-ui/src/elements/carousel.ts
+++ b/packages/duoyun-ui/src/elements/carousel.ts
@@ -9,7 +9,7 @@ import {
emitter,
Emitter,
} from '@mantou/gem/lib/decorators';
-import { createCSSSheet, css, styleMap, classMap } from '@mantou/gem/lib/utils';
+import { createCSSSheet, css, styleMap, classMap, addListener } from '@mantou/gem/lib/utils';
import { theme } from '../lib/theme';
import { icons } from '../lib/icons';
@@ -243,9 +243,9 @@ export class DuoyunCarouselElement extends GemElement {
() => this.change(this.state.currentIndex),
() => [this.state.currentIndex],
);
- document.addEventListener('visibilitychange', this.#pageVisibleChange);
+ const removeListener = addListener(document, 'visibilitychange', this.#pageVisibleChange);
return () => {
- document.removeEventListener('visibilitychange', this.#pageVisibleChange);
+ removeListener();
this.#clearTimer();
};
};
diff --git a/packages/duoyun-ui/src/elements/checkbox.ts b/packages/duoyun-ui/src/elements/checkbox.ts
index 0fe1b181..f770ece1 100644
--- a/packages/duoyun-ui/src/elements/checkbox.ts
+++ b/packages/duoyun-ui/src/elements/checkbox.ts
@@ -35,6 +35,8 @@ const style = createCSSSheet(css`
box-sizing: border-box;
width: 1em;
aspect-ratio: 1;
+ /* webkit bug */
+ height: 1em;
background-clip: content-box;
padding: 0;
color: ${theme.backgroundColor};
diff --git a/packages/duoyun-ui/src/elements/color-panel.ts b/packages/duoyun-ui/src/elements/color-panel.ts
index 8c054903..45a7a4f8 100644
--- a/packages/duoyun-ui/src/elements/color-panel.ts
+++ b/packages/duoyun-ui/src/elements/color-panel.ts
@@ -92,6 +92,10 @@ const style = createCSSSheet(css`
border: 1px solid #0008;
transform: translate(-50%, -50%);
}
+ .current {
+ /* webkit bug */
+ height: 1.2em;
+ }
.current span {
left: 50%;
top: 50%;
diff --git a/packages/duoyun-ui/src/elements/flow.ts b/packages/duoyun-ui/src/elements/flow.ts
index 9913501a..90c6a5f1 100644
--- a/packages/duoyun-ui/src/elements/flow.ts
+++ b/packages/duoyun-ui/src/elements/flow.ts
@@ -1,3 +1,5 @@
+// webkit marker arrow bug: https://duoyun-ui.gemjs.org/zh/elements/flow
+
import { adoptedStyle, customElement, property, part, state, refobject, RefObject } from '@mantou/gem/lib/decorators';
import { html, svg, TemplateResult } from '@mantou/gem/lib/element';
import { createCSSSheet, css, styleMap, exportPartsMap } from '@mantou/gem/lib/utils';
diff --git a/packages/duoyun-ui/src/elements/input-capture.ts b/packages/duoyun-ui/src/elements/input-capture.ts
index f98db479..ea9e4668 100644
--- a/packages/duoyun-ui/src/elements/input-capture.ts
+++ b/packages/duoyun-ui/src/elements/input-capture.ts
@@ -1,6 +1,6 @@
import { adoptedStyle, customElement, part } from '@mantou/gem/lib/decorators';
import { GemElement, html } from '@mantou/gem/lib/element';
-import { createCSSSheet, css } from '@mantou/gem/lib/utils';
+import { addListener, createCSSSheet, css } from '@mantou/gem/lib/utils';
import { theme } from '../lib/theme';
import { getDisplayKey } from '../lib/hotkeys';
@@ -72,17 +72,17 @@ export class DuoyunInputCaptureElement extends GemElement {
#onPointerup = () => this.setState({ mousePosition: null });
mounted = () => {
- addEventListener('keydown', this.#onKeydown, { capture: true });
- addEventListener('pointerdown', this.#onPointerdown, { capture: true });
- addEventListener('pointermove', this.#onPointermove, { capture: true });
- addEventListener('pointerup', this.#onPointerup, { capture: true });
- addEventListener('pointercancel', this.#onPointerup, { capture: true });
+ const removeKeydownListener = addListener(window, 'keydown', this.#onKeydown, { capture: true });
+ const removePointerdownListener = addListener(window, 'pointerdown', this.#onPointerdown, { capture: true });
+ const removePointermoveListener = addListener(window, 'pointermove', this.#onPointermove, { capture: true });
+ const removePointerupListener = addListener(window, 'pointerup', this.#onPointerup, { capture: true });
+ const removePointercancelListener = addListener(window, 'pointercancel', this.#onPointerup, { capture: true });
() => {
- removeEventListener('keydown', this.#onKeydown, { capture: true });
- removeEventListener('pointerdown', this.#onPointerdown, { capture: true });
- removeEventListener('pointermove', this.#onPointermove, { capture: true });
- removeEventListener('pointerup', this.#onPointerup, { capture: true });
- removeEventListener('pointercancel', this.#onPointerup, { capture: true });
+ removeKeydownListener();
+ removePointerdownListener();
+ removePointermoveListener();
+ removePointerupListener();
+ removePointercancelListener();
};
};
diff --git a/packages/duoyun-ui/src/elements/input.ts b/packages/duoyun-ui/src/elements/input.ts
index 2a3c4d99..56e69346 100644
--- a/packages/duoyun-ui/src/elements/input.ts
+++ b/packages/duoyun-ui/src/elements/input.ts
@@ -291,32 +291,22 @@ export class DuoyunInputElement extends GemElement {
#onKeyDown = (evt: KeyboardEvent) => {
const nextValue = (n: number) => String(clamp(this.#min, Number(this.value || this.min) + n, this.#max));
- const prevent = (evt: Event) => {
- evt.stopPropagation();
- evt.preventDefault();
- };
if (this.type === 'number') {
- hotkeys({
- up: () => {
- this.change(nextValue(this.#step));
- prevent(evt);
+ hotkeys(
+ {
+ up: () => this.change(nextValue(this.#step)),
+ down: () => this.change(nextValue(-this.#step)),
},
- down: () => {
- this.change(nextValue(-this.#step));
- prevent(evt);
- },
- })(evt);
+ { stopPropagation: true },
+ )(evt);
}
- hotkeys({
- 'ctrl+z,command+z': () => {
- this.#history.undo();
- prevent(evt);
- },
- 'ctrl+shift+z,command+shift+z': () => {
- this.#history.redo();
- prevent(evt);
+ hotkeys(
+ {
+ 'ctrl+z,command+z': () => this.#history.undo(),
+ 'ctrl+shift+z,command+shift+z': () => this.#history.redo(),
},
- })(evt);
+ { stopPropagation: true },
+ )(evt);
};
mounted = () => {
diff --git a/packages/duoyun-ui/src/elements/keyboard-access.ts b/packages/duoyun-ui/src/elements/keyboard-access.ts
index b9cc85e0..d96c8c66 100644
--- a/packages/duoyun-ui/src/elements/keyboard-access.ts
+++ b/packages/duoyun-ui/src/elements/keyboard-access.ts
@@ -1,6 +1,6 @@
import { GemElement, html } from '@mantou/gem/lib/element';
-import { adoptedStyle, customElement, attribute, part } from '@mantou/gem/lib/decorators';
-import { createCSSSheet, css, styleMap } from '@mantou/gem/lib/utils';
+import { adoptedStyle, customElement, attribute, part, property } from '@mantou/gem/lib/decorators';
+import { addListener, createCSSSheet, css, styleMap } from '@mantou/gem/lib/utils';
import { logger } from '@mantou/gem/helper/logger';
import { hotkeys, HotKeyHandles, unlock } from '../lib/hotkeys';
@@ -67,26 +67,22 @@ export class DuoyunKeyboardAccessElement extends GemElement {
@part static kbd: string;
@attribute activekey: string;
+ @property scrollContainer?: HTMLElement;
+
get #activeKey() {
return this.activekey || 'f';
}
+ get #container() {
+ return this.scrollContainer || document.body;
+ }
+
state: State = {
active: false,
waiting: false,
keydownHandles: {},
};
- #preventEvent = (evt: Event) => {
- evt.stopPropagation();
- evt.preventDefault();
- };
-
- #handler = (evt: KeyboardEvent) => {
- hotkeys(this.state.keydownHandles)(evt);
- this.#preventEvent(evt);
- };
-
#isInputTarget(evt: Event) {
const originElement = evt.composedPath()[0] as HTMLElement;
if (
@@ -114,6 +110,7 @@ export class DuoyunKeyboardAccessElement extends GemElement {
let index = 0;
const keydownHandles: HotKeyHandles = {
+ esc: this.#onInactive,
onLock: () => this.setState({ waiting: true }),
onUnlock: () => this.setState({ waiting: false }),
onUncapture: () => logger.warn('Un Capture!'),
@@ -151,7 +148,7 @@ export class DuoyunKeyboardAccessElement extends GemElement {
const key = getChars(index);
if (!key) return;
// `a-b`
- keydownHandles[[...key].join('-')] = (evt: KeyboardEvent) => {
+ keydownHandles[[...key].join('-')] = () => {
this.setState({ active: false });
if ('showPicker' in element) {
(element as HTMLInputElement).showPicker();
@@ -159,20 +156,17 @@ export class DuoyunKeyboardAccessElement extends GemElement {
element.focus();
element.click();
}
- this.#preventEvent(evt);
};
index++;
return { key, top, left };
})
.filter(isNotNullish),
});
- this.#preventEvent(evt);
};
- #onInactive = (evt: KeyboardEvent) => {
+ #onInactive = () => {
if (!this.state.active) return;
this.setState({ active: false });
- this.#preventEvent(evt);
};
#onCancel = () => {
@@ -182,15 +176,18 @@ export class DuoyunKeyboardAccessElement extends GemElement {
};
#onKeydown = (evt: KeyboardEvent) => {
+ if (this.state.active) return;
if (this.#isInputTarget(evt)) return;
- hotkeys({
- [this.#activeKey]: this.#onActive,
- j: () => document.body.scrollBy(0, -innerHeight / 3),
- k: () => document.body.scrollBy(0, innerHeight / 3),
- h: () => document.body.scrollBy(0, -innerHeight),
- l: () => document.body.scrollBy(0, innerHeight),
- esc: this.#onInactive,
- })(evt);
+ hotkeys(
+ {
+ [this.#activeKey]: this.#onActive,
+ j: () => this.#container.scrollBy(0, -innerHeight / 3),
+ k: () => this.#container.scrollBy(0, innerHeight / 3),
+ h: () => this.#container.scrollBy(0, -innerHeight),
+ l: () => this.#container.scrollBy(0, innerHeight),
+ },
+ { stopPropagation: true },
+ )(evt);
};
mounted = () => {
@@ -198,20 +195,25 @@ export class DuoyunKeyboardAccessElement extends GemElement {
() => {
if (this.state.active) {
document.body.style.pointerEvents = 'none';
- addEventListener('keydown', this.#handler, { capture: true });
+ const removeListener = addListener(
+ window,
+ 'keydown',
+ hotkeys(this.state.keydownHandles, { stopPropagation: true }),
+ { capture: true },
+ );
return () => {
document.body.style.pointerEvents = 'auto';
- removeEventListener('keydown', this.#handler, { capture: true });
+ removeListener();
};
}
},
() => [this.state.active],
);
- addEventListener('keydown', this.#onKeydown, { capture: true });
- addEventListener('pointerdown', this.#onCancel);
+ const removeKeydownListener = addListener(window, 'keydown', this.#onKeydown, { capture: true });
+ const removePointerdown = addListener(window, 'pointerdown', this.#onCancel);
return () => {
- removeEventListener('keydown', this.#onKeydown, { capture: true });
- removeEventListener('pointerdown', this.#onCancel);
+ removeKeydownListener();
+ removePointerdown();
};
};
diff --git a/packages/duoyun-ui/src/elements/list.ts b/packages/duoyun-ui/src/elements/list.ts
index 4d986b3c..ec3957c5 100644
--- a/packages/duoyun-ui/src/elements/list.ts
+++ b/packages/duoyun-ui/src/elements/list.ts
@@ -12,7 +12,7 @@ import {
boolattribute,
} from '@mantou/gem/lib/decorators';
import { GemElement, html, TemplateResult } from '@mantou/gem/lib/element';
-import { createCSSSheet, css, LinkedList, LinkedListItem, styled, styleMap } from '@mantou/gem/lib/utils';
+import { addListener, createCSSSheet, css, LinkedList, LinkedListItem, styled, styleMap } from '@mantou/gem/lib/utils';
import { logger } from '@mantou/gem/helper/logger';
import { theme } from '../lib/theme';
@@ -484,10 +484,8 @@ export class DuoyunListElement extends GemElement {
if (this.#initState) this.scrollContainer.scrollTo(0, this.#initState.scrollTop);
this.effect(() => {
- this.infinite && this.scrollContainer.addEventListener('scroll', this.#onScroll);
- return () => {
- this.scrollContainer.removeEventListener('scroll', this.#onScroll);
- };
+ if (!this.infinite) return;
+ return addListener(this.scrollContainer, 'scroll', this.#onScroll);
});
// 已经等到新值需要检查
diff --git a/packages/duoyun-ui/src/elements/modal.ts b/packages/duoyun-ui/src/elements/modal.ts
index ca30e5ae..899737b4 100644
--- a/packages/duoyun-ui/src/elements/modal.ts
+++ b/packages/duoyun-ui/src/elements/modal.ts
@@ -14,7 +14,7 @@ import {
slot,
} from '@mantou/gem/lib/decorators';
import { GemElement, html, TemplateResult } from '@mantou/gem/lib/element';
-import { createCSSSheet, css, styled } from '@mantou/gem/lib/utils';
+import { addListener, createCSSSheet, css, styled } from '@mantou/gem/lib/utils';
import { mediaQuery } from '@mantou/gem/helper/mediaquery';
import { theme } from '../lib/theme';
@@ -282,8 +282,7 @@ export class DuoyunModalElement extends GemElement {
},
() => [this.open],
);
- this.addEventListener('keydown', this.#keydown);
- return () => this.removeEventListener('keydown', this.#keydown);
+ return addListener(this, 'keydown', this.#keydown);
};
render = () => {
diff --git a/packages/duoyun-ui/src/elements/page-loadbar.ts b/packages/duoyun-ui/src/elements/page-loadbar.ts
index 55a68c54..e019c068 100644
--- a/packages/duoyun-ui/src/elements/page-loadbar.ts
+++ b/packages/duoyun-ui/src/elements/page-loadbar.ts
@@ -43,7 +43,7 @@ export class DuoyunPageLoadbarElement extends GemElement {
clearInterval(Loadbar.timer);
Loadbar.timer = window.setTimeout(() => {
const instance = Loadbar.instance || new Loadbar();
- document.body.append(instance);
+ if (!instance.isConnected) document.body.append(instance);
instance.setState({ progress: 0 });
Loadbar.timer = window.setInterval(() => {
instance.setState({ progress: instance.state.progress + (95 - instance.state.progress) * 0.1 });
diff --git a/packages/duoyun-ui/src/elements/popover.ts b/packages/duoyun-ui/src/elements/popover.ts
index 6f482739..5e8b92bd 100644
--- a/packages/duoyun-ui/src/elements/popover.ts
+++ b/packages/duoyun-ui/src/elements/popover.ts
@@ -10,7 +10,7 @@ import {
RefObject,
} from '@mantou/gem/lib/decorators';
import { GemElement, html, TemplateResult } from '@mantou/gem/lib/element';
-import { createCSSSheet, css, styleMap, StyleObject } from '@mantou/gem/lib/utils';
+import { addListener, createCSSSheet, css, styleMap, StyleObject } from '@mantou/gem/lib/utils';
import { toggleActiveState, getAssignedElements, getBoundingClientRect } from '../lib/element';
import { sleep, setBodyInert } from '../lib/utils';
@@ -466,7 +466,6 @@ export class DuoyunPopoverGhostElement extends GemElement {
#onKeyDown = hotkeys({ esc: () => this.close(null) });
mounted = () => {
- addEventListener('keydown', this.#onKeyDown);
- return () => removeEventListener('keydown', this.#onKeyDown);
+ return addListener(window, 'keydown', this.#onKeyDown);
};
}
diff --git a/packages/duoyun-ui/src/elements/select.ts b/packages/duoyun-ui/src/elements/select.ts
index 004647f3..a3ddc136 100644
--- a/packages/duoyun-ui/src/elements/select.ts
+++ b/packages/duoyun-ui/src/elements/select.ts
@@ -194,75 +194,79 @@ export class DuoyunSelectElement extends GemElement implements BasePicker
search: '',
};
- #open = () => {
+ #open = async () => {
if (this.disabled) return;
+ if (this.state.open) return;
this.setState({ open: true });
+ // safari auto focus
+ await Promise.resolve();
this.searchRef.element?.focus();
};
#close = () => {
+ if (!this.state.open) return;
this.setState({ open: false });
};
- #onKeydown = hotkeys({
- esc: (evt) => {
- if (this.state.open) {
- this.#close();
- if (!this.inline) {
- evt.stopPropagation();
- }
- }
- },
- 'space,enter': (evt) => {
- this.#open();
- this.searchRef.element?.focus();
- evt.preventDefault();
- },
- });
+ #onKeydown = (evt: KeyboardEvent) => {
+ if (this.inline) return;
+ hotkeys(
+ {
+ esc: this.#close,
+ 'space,enter': this.#open,
+ },
+ { stopPropagation: true },
+ )(evt);
+ };
- #onEsc = (evt: KeyboardEvent) => {
+ #onEsc = () => {
this.#close();
(this.searchRef.element || this).focus();
- evt.stopPropagation();
};
- #onSearchKeydown = hotkeys({
- backspace: () => {
- if (!this.state.search && this.#valueOptions?.length) {
- if (this.multiple) {
- this.#onRemoveTag(this.#valueOptions[this.#valueOptions.length - 1]);
- } else {
- this.change(undefined);
+ #onSearchKeydown = hotkeys(
+ {
+ backspace: () => {
+ if (!this.state.search && this.#valueOptions?.length) {
+ if (this.multiple) {
+ this.#onRemoveTag(this.#valueOptions[this.#valueOptions.length - 1]);
+ } else {
+ this.change(undefined);
+ }
}
- }
- },
- tab: (evt) => {
- this.optionsRef.element?.focus();
- evt.preventDefault();
- },
- esc: this.#onEsc,
- enter: (evt) => {
- const options = this.#filteredOptions;
- if (options?.length === 1) {
- const { value, label } = options[0];
- this.#onChange(value ?? label);
- }
- evt.stopPropagation();
- },
- space: (evt) => {
- evt.stopPropagation();
+ },
+ tab: (evt) => {
+ if (!this.state.open) return;
+ this.optionsRef.element?.focus();
+ evt.preventDefault();
+ },
+ esc: this.#onEsc,
+ enter: () => {
+ const options = this.#filteredOptions;
+ if (options?.length === 1) {
+ const { value, label } = options[0];
+ this.#onChange(value ?? label);
+ }
+ },
+ space: () => {
+ // Used hotkey options
+ },
},
- });
+ { preventDefault: false, stopPropagation: true },
+ );
- #onOptionsKeydown = hotkeys({
- esc: this.#onEsc,
- 'shift+tab': (evt) => {
- if (!this.optionsRef.element?.shadowRoot?.activeElement) {
- (this.searchRef.element || this).focus();
- evt.preventDefault();
- }
+ #onOptionsKeydown = hotkeys(
+ {
+ esc: this.#onEsc,
+ 'shift+tab': (evt) => {
+ if (!this.optionsRef.element?.shadowRoot?.activeElement) {
+ (this.searchRef.element || this).focus();
+ evt.preventDefault();
+ }
+ },
},
- });
+ { preventDefault: false, stopPropagation: true },
+ );
#onSearch = (evt: CustomEvent) => {
this.setState({ search: evt.detail, open: true });
diff --git a/packages/duoyun-ui/src/elements/selection-box.ts b/packages/duoyun-ui/src/elements/selection-box.ts
index 9bb3ba5b..06b461e8 100644
--- a/packages/duoyun-ui/src/elements/selection-box.ts
+++ b/packages/duoyun-ui/src/elements/selection-box.ts
@@ -78,6 +78,8 @@ export class DuoyunSelectionBoxElement extends GemElement {
};
#onPointerMove = (evt: PointerEvent) => {
+ // disabled text select
+ evt.preventDefault();
const start = this.state.start!;
const x = evt.x === start[0] ? evt.x + 0.01 : evt.x;
const y = evt.y === start[1] ? evt.y + 0.01 : evt.y;
diff --git a/packages/duoyun-ui/src/elements/slider.ts b/packages/duoyun-ui/src/elements/slider.ts
index 87c68894..d337c429 100644
--- a/packages/duoyun-ui/src/elements/slider.ts
+++ b/packages/duoyun-ui/src/elements/slider.ts
@@ -34,6 +34,9 @@ const style = createCSSSheet(css`
align-items: center;
gap: 1em;
block-size: calc(2.2em + 2px);
+ border-radius: ${theme.normalRound};
+ box-sizing: border-box;
+ padding-inline: 2px;
}
:host([orientation='vertical']) {
writing-mode: vertical-lr;
@@ -185,8 +188,8 @@ export class DuoyunSliderElement extends GemElement {
};
#onKeydown = hotkeys({
- up: () => this.#setPrecisionValue(this.value + 1),
- down: () => this.#setPrecisionValue(this.value - 1),
+ up: () => this.#setPrecisionValue(this.value + this.#step),
+ down: () => this.#setPrecisionValue(this.value - this.#step),
});
#onInputChange = (evt: CustomEvent) => {
@@ -231,6 +234,7 @@ export class DuoyunSliderElement extends GemElement {
?disabled=${this.disabled}
class="input"
type="number"
+ step=${this.step}
value=${String(this.value)}
@change=${this.#onInputChange}
>
diff --git a/packages/duoyun-ui/src/elements/timeline.ts b/packages/duoyun-ui/src/elements/timeline.ts
index 74edaeec..0154e082 100644
--- a/packages/duoyun-ui/src/elements/timeline.ts
+++ b/packages/duoyun-ui/src/elements/timeline.ts
@@ -14,6 +14,7 @@ const style = createCSSSheet(css`
--size: 1.5em;
line-height: var(--size);
display: block;
+ overscroll-behavior: auto;
}
.item {
position: relative;
diff --git a/packages/duoyun-ui/src/elements/toast.ts b/packages/duoyun-ui/src/elements/toast.ts
index 20c6b8cc..7a9a8e8f 100644
--- a/packages/duoyun-ui/src/elements/toast.ts
+++ b/packages/duoyun-ui/src/elements/toast.ts
@@ -69,6 +69,7 @@ const style = createCSSSheet(css`
background: ${theme.negativeColor};
}
.icon {
+ flex-shrink: 0;
width: var(--item-icon-height);
}
.body {
@@ -109,10 +110,10 @@ export class DuoyunToastElement extends GemElement {
static open(type: Type, content: string | TemplateResult, { debug, duration = 3000 }: ToastOptions = {}) {
const toast = Toast.instance || new Toast();
- document.body.append(toast);
+ if (!toast.isConnected) document.body.append(toast);
const key = type + getStringFromTemplate(content);
const item = toast.items?.find((e) => e.key === key) || { key, type, content };
- // 如何 item 正在执行删除动画,这里会导致一点小瑕疵
+ // 如果 item 正在执行删除动画,这里会导致一点小瑕疵
toast.items = [...(toast.items || []).filter((e) => e !== item), item];
// 取消正在执行移除动画的删除定时器
removedSet.delete(item);
diff --git a/packages/duoyun-ui/src/lib/hotkeys.ts b/packages/duoyun-ui/src/lib/hotkeys.ts
index 3302d138..a67fb496 100644
--- a/packages/duoyun-ui/src/lib/hotkeys.ts
+++ b/packages/duoyun-ui/src/lib/hotkeys.ts
@@ -208,12 +208,18 @@ export function unlock() {
unlockCallback.clear();
}
+type HotkeysOptions = {
+ stopPropagation?: boolean;
+ preventDefault?: boolean;
+};
+
/**
* Must have non-control character;
* Not case sensitive;
* Support `a-b`, press `a`, hotkeys be locked, wait next `keydown` event, allow call `unlock`
*/
-export function hotkeys(handles: HotKeyHandles) {
+export function hotkeys(handles: HotKeyHandles, options: HotkeysOptions = {}) {
+ const { stopPropagation, preventDefault = true } = options;
return function (evt: KeyboardEvent) {
if (evt.isComposing) return;
if (locked) return;
@@ -229,6 +235,8 @@ export function hotkeys(handles: HotKeyHandles) {
const matchResult = shortcuts.map((hotkey) => matchHotKey(evt, hotkey));
if (matchResult.some((r) => r === true)) {
captured = true;
+ if (preventDefault) evt.preventDefault();
+ if (stopPropagation) evt.stopPropagation();
handle(evt);
}
matchResult.filter(isNotBoolean).forEach((key) => {
@@ -274,12 +282,6 @@ export function hotkeys(handles: HotKeyHandles) {
* Support space,enter
*/
export const commonHandle = hotkeys({
- 'space,enter': (evt) => {
- (evt.target as HTMLElement).click();
- evt.preventDefault();
- },
- esc: (evt) => {
- (evt.target as HTMLElement).blur();
- evt.preventDefault();
- },
+ 'space,enter': (evt) => (evt.target as HTMLElement).click(),
+ esc: (evt) => (evt.target as HTMLElement).blur(),
});
diff --git a/packages/gem-book/package.json b/packages/gem-book/package.json
index 85f14c5a..5a124e9b 100644
--- a/packages/gem-book/package.json
+++ b/packages/gem-book/package.json
@@ -1,6 +1,6 @@
{
"name": "gem-book",
- "version": "1.5.32",
+ "version": "1.5.33",
"description": "Create your document website easily and quickly",
"keywords": [
"doc",
diff --git a/packages/gem/src/elements/base/gesture.ts b/packages/gem/src/elements/base/gesture.ts
index 24ffba80..db66f265 100644
--- a/packages/gem/src/elements/base/gesture.ts
+++ b/packages/gem/src/elements/base/gesture.ts
@@ -202,7 +202,12 @@ export class GemGestureElement extends GemElement {
#onMoveSet = (evt: PointerEvent) => {
evt.stopPropagation();
- evt.getCoalescedEvents().forEach((evt) => this.#onMove(evt));
+ // https://bugs.webkit.org/show_bug.cgi?id=210454
+ if ('getCoalescedEvents' in evt) {
+ evt.getCoalescedEvents().forEach((evt) => this.#onMove(evt));
+ } else {
+ this.#onMove(evt);
+ }
};
#onEnd = (evt: PointerEvent) => {