Skip to content

Commit

Permalink
Closed #129
Browse files Browse the repository at this point in the history
  • Loading branch information
mantou132 authored Apr 3, 2024
1 parent d22066e commit 22f879e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 32 deletions.
3 changes: 2 additions & 1 deletion packages/duoyun-ui/src/elements/action-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const style = createCSSSheet(css`
text-overflow: ellipsis;
cursor: default;
line-height: 1.5;
--color: initial;
color: var(--color, inherit);
border-radius: ${theme.normalRound};
}
Expand All @@ -29,6 +28,8 @@ const style = createCSSSheet(css`

/**
* @customElement dy-action-text
* @attr color
* @attr tooltip
*/
@customElement('dy-action-text')
@adoptedStyle(style)
Expand Down
4 changes: 1 addition & 3 deletions packages/duoyun-ui/src/elements/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ const style = createCSSSheet(css`
export class DuoyunIconsElement extends GemElement {
#onCopy = async (name: string) => {
await navigator.clipboard.writeText(`<dy-use .element=\${icons.${name}}></dy-use>`);
Toast.open('success', `${locale.copySuccess}: ${name}`, {
duration: 1000,
});
Toast.open('success', `${locale.copySuccess}: ${name}`);
};

render = () => {
Expand Down
86 changes: 58 additions & 28 deletions packages/duoyun-ui/src/elements/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { theme } from '../lib/theme';
import { getStringFromTemplate } from '../lib/utils';

import './use';
import './action-text';

const style = createCSSSheet(css`
:host(:where(:not([hidden]))) {
Expand Down Expand Up @@ -77,26 +78,34 @@ const style = createCSSSheet(css`
text-overflow: ellipsis;
white-space: nowrap;
}
.body:empty {
display: none;
}
.body::first-letter {
text-transform: capitalize;
}
.action {
cursor: pointer;
text-decoration: underline;
}
`);

type Type = 'success' | 'warning' | 'error' | 'default';
type Type = 'info' | 'success' | 'warning' | 'error' | 'loading';

interface ToastItem {
type ToastItem = {
key: string;
type: Type;
content: string | TemplateResult;
}

const itemTimerMap = new WeakMap<ToastItem, number>();
const removedSet = new WeakSet<ToastItem>();
action?: { text: string; handle: () => void };
};

interface ToastOptions {
type ToastOptions = Partial<ToastItem> & {
duration?: number;
debug?: boolean;
}
};

const itemTimerMap = new WeakMap<ToastItem, number>();
const removedSet = new WeakSet<ToastItem>();

/**
* @customElement dy-toast
Expand All @@ -108,17 +117,26 @@ export class DuoyunToastElement extends GemElement {

static instance?: DuoyunToastElement;

static open(type: Type, content: string | TemplateResult, { debug, duration = 3000 }: ToastOptions = {}) {
static open(options: ToastOptions): void;
static open(type: Type, content: string | TemplateResult): void;
static open(arg1: Type | ToastOptions, arg2?: string | TemplateResult) {
const {
action,
type = 'info',
content = '',
debug = false,
duration = action ? 5000 : 3000,
key = type + getStringFromTemplate(content),
} = typeof arg1 === 'string' ? ({ type: arg1, content: arg2 } as ToastOptions) : arg1;
const toast = Toast.instance || new 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 };
const item = toast.items?.find((e) => e.key === key) || { key, type, content, action };
// 如果 item 正在执行删除动画,这里会导致一点小瑕疵
toast.items = [...(toast.items || []).filter((e) => e !== item), item];
// 取消正在执行移除动画的删除定时器
removedSet.delete(item);
clearTimeout(itemTimerMap.get(item));
const removeTimer = window.setTimeout(() => toast.removeItem(item), debug ? 1000000 : duration);
const removeTimer = window.setTimeout(() => toast.#removeItem(item), debug ? 1000000 : duration);
itemTimerMap.set(item, removeTimer);
}

Expand All @@ -139,16 +157,33 @@ export class DuoyunToastElement extends GemElement {
#getIcon = (type: Type) => {
switch (type) {
case 'success':
return icons.success;
case 'info':
case 'warning':
return icons.warning;
case 'error':
return icons.error;
case 'loading':
return icons[type];
default:
return icons.info;
return Reflect.get(icons, type);
}
};

#removeItem = async (item: ToastItem) => {
await this.#over;
removedSet.add(item);
this.update();
window.setTimeout(() => {
if (!this.items || !removedSet.has(item)) return;
this.items = this.items.filter((e) => e !== item);
if (this.items.length === 0) this.remove();
}, 300);
};

#clickAction = (item: ToastItem) => {
item.action?.handle();
this.#over = Promise.resolve();
this.#removeItem(item);
};

mounted = () => {
Toast.instance = this;
return () => (Toast.instance = undefined);
Expand All @@ -163,23 +198,18 @@ export class DuoyunToastElement extends GemElement {
<div class=${classMap({ item: true, [item.type]: true, removed: removedSet.has(item) })}>
<dy-use class="icon" .element=${this.#getIcon(item.type)}></dy-use>
<span class="body">${item.content}</span>
${item.action
? html`
<dy-action-text class="action" color="white" @click=${() => this.#clickAction(item)}>
${item.action.text}
</dy-action-text>
`
: ''}
</div>
`,
)}
`;
};

// debug
removeItem = async (item: ToastItem) => {
await this.#over;
removedSet.add(item);
this.update();
window.setTimeout(() => {
if (!this.items || !removedSet.has(item)) return;
this.items = this.items.filter((e) => e !== item);
if (this.items.length === 0) this.remove();
}, 300);
};
}

export const Toast = DuoyunToastElement;

0 comments on commit 22f879e

Please sign in to comment.