dees-catalog/ts_web/elements/dees-speechbubble.ts

230 lines
5.9 KiB
TypeScript
Raw Normal View History

2024-01-21 00:12:57 +00:00
import * as colors from './00colors.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-speechbubble.demo.js';
2022-05-20 16:44:33 +00:00
import {
customElement,
html,
DeesElement,
property,
2023-08-07 18:02:18 +00:00
type TemplateResult,
2022-05-20 16:44:33 +00:00
cssManager,
css,
2023-08-07 18:02:18 +00:00
type CSSResult,
2022-05-20 16:44:33 +00:00
unsafeCSS,
2024-01-21 00:12:57 +00:00
domtools,
directives,
unsafeHTML,
2023-08-07 17:13:29 +00:00
} from '@design.estate/dees-element';
2024-01-21 00:12:57 +00:00
import { DeesWindowLayer } from './dees-windowlayer.js';
2022-05-20 16:44:33 +00:00
declare global {
interface HTMLElementTagNameMap {
'dees-speechbubble': DeesSpeechbubble;
}
}
@customElement('dees-speechbubble')
export class DeesSpeechbubble extends DeesElement {
2024-01-21 00:12:57 +00:00
public static demo = demoFunc;
// STATIC
public static async createAndShow(refElement: HTMLElement, textArg: string) {
const windowLayer = await DeesWindowLayer.createAndShow({
blur: false,
});
const speechbubble = document.createElement('dees-speechbubble');
speechbubble.windowLayer = windowLayer;
speechbubble.reffedElement = refElement;
speechbubble.text = textArg;
speechbubble.manifested = true;
windowLayer.appendChild(speechbubble);
windowLayer.style.pointerEvents = 'none';
(windowLayer.shadowRoot.querySelector('.windowOverlay') as HTMLElement).style.pointerEvents = 'none';
return speechbubble;
}
2022-05-20 16:44:33 +00:00
2024-01-21 00:12:57 +00:00
// INSTANCE
@property({
type: Object,
})
reffedElement: HTMLElement;
@property({
type: String,
reflect: true,
})
2022-05-20 16:44:33 +00:00
public text: string;
@property({
type: Boolean,
})
2024-01-21 00:12:57 +00:00
public wave: boolean = false;
2022-05-20 16:44:33 +00:00
@property({
type: Boolean,
})
2024-01-21 00:12:57 +00:00
public manifested = false;
2022-05-20 16:44:33 +00:00
@property({
type: String,
})
public status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
2024-01-21 00:12:57 +00:00
public windowLayer: DeesWindowLayer;
2022-05-20 16:44:33 +00:00
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
box-sizing: border-box;
2022-05-20 17:43:16 +00:00
color: ${cssManager.bdTheme('#333', '#fff')};
2022-05-20 16:44:33 +00:00
user-select: none;
}
2022-05-20 19:04:59 +00:00
.maincontainer {
2024-01-21 00:12:57 +00:00
position: relative;
2022-05-24 07:13:15 +00:00
will-change: transform;
transition: transform 0.2s;
transform: translateX(0px);
2022-05-20 19:04:59 +00:00
transition: all 0.2s;
margin-left: 0px;
2024-01-21 00:12:57 +00:00
filter: drop-shadow(0px 0px 2px rgba(0, 0, 0, 0.2));
pointer-events: none;
opacity: 0;
transition: all 0.2s;
2022-05-20 19:04:59 +00:00
}
2022-05-20 16:44:33 +00:00
.arrow {
position: absolute;
transform: rotate(45deg);
2022-05-20 17:43:16 +00:00
background: ${cssManager.bdTheme('#fff', '#333')};
2022-05-20 17:48:48 +00:00
height: 15px;
width: 15px;
2024-01-21 00:12:57 +00:00
left: 2px;
top: 12px;
border-radius: 3px;
2022-05-20 16:44:33 +00:00
}
2022-05-20 19:04:59 +00:00
.speechbubble {
2022-05-20 17:43:16 +00:00
background: ${cssManager.bdTheme('#fff', '#333')};
2024-01-21 00:12:57 +00:00
padding: 0px 16px;
2022-05-20 16:44:33 +00:00
border-radius: 3px;
position: absolute;
2024-01-21 00:12:57 +00:00
min-width: 240px;
2022-05-20 17:48:48 +00:00
font-size: 12px;
2022-05-20 16:44:33 +00:00
top: 0px;
left: 8px;
}
.wave {
animation-name: wave-animation; /* Refers to the name of your @keyframes element below */
animation-duration: 2.5s; /* Change to speed up or slow down */
animation-iteration-count: infinite; /* Never stop waving :) */
transform-origin: 70% 70%; /* Pivot around the bottom-left palm */
display: inline-block;
}
@keyframes wave-animation {
0% {
transform: rotate(0deg);
}
10% {
transform: rotate(14deg);
} /* The following five values can be played with to make the waving more or less extreme */
20% {
transform: rotate(-8deg);
}
30% {
transform: rotate(14deg);
}
40% {
transform: rotate(-4deg);
}
50% {
transform: rotate(10deg);
}
60% {
transform: rotate(0deg);
} /* Reset for the last half to pause */
100% {
transform: rotate(0deg);
}
}
`,
];
public render(): TemplateResult {
return html`
2024-01-21 00:12:57 +00:00
${this.manifested
? html`
<div class="maincontainer" @click=${this.handleClick}>
<div class="arrow"></div>
<div class="speechbubble">
${this.wave ? html`<span class="wave">👋</span>` : html``}
${directives.resolve(this.getHtml())}
</div>
</div>
`
: html``}
2022-05-20 16:44:33 +00:00
`;
}
2022-05-20 17:51:20 +00:00
public async handleClick() {
2024-01-21 00:12:57 +00:00
console.log('speechbubble got clicked.');
}
public async firstUpdated() {
// lets make sure we have a ref
if (!this.reffedElement) {
this.reffedElement = this.previousElementSibling as HTMLElement;
2022-05-20 16:44:33 +00:00
}
2024-01-21 00:12:57 +00:00
if (this.manifested) {
await this.updatePosition();
(this.shadowRoot.querySelector('.maincontainer') as HTMLElement).style.opacity = '1';
} else {
// lets make sure we instrument it
let speechbubble: DeesSpeechbubble;
this.reffedElement.addEventListener('mouseenter', async () => {
speechbubble = await DeesSpeechbubble.createAndShow(this.reffedElement, this.text);
});
this.reffedElement.addEventListener('mouseleave', () => {
speechbubble.destroy();
});
}
}
2022-05-20 16:44:33 +00:00
2024-01-21 00:12:57 +00:00
public async updatePosition() {
const refElement = this.reffedElement;
const boundingClientRect = refElement.getBoundingClientRect();
this.style.position = 'fixed';
this.style.top = `${boundingClientRect.top - 13}px`;
this.style.left = `${boundingClientRect.left + refElement.clientWidth + 4}px`;
if (boundingClientRect.right > 250) {
this.style.width = `250px`;
}
2022-05-20 16:44:33 +00:00
}
2024-01-21 00:12:57 +00:00
public async getHtml(): Promise<any> {
if (!this.text) {
return '';
2022-05-20 16:44:33 +00:00
}
2024-01-21 00:12:57 +00:00
const normalized = domtools.plugins.smartstring.normalize.standard(this.text);
const result = await domtools.plugins.smartmarkdown.SmartMarkdown.easyMarkdownToHtml(
normalized
);
return unsafeHTML(result);
}
public async show() {}
public async destroy() {
(this.shadowRoot.querySelector('.maincontainer') as HTMLElement).style.opacity = '0';
this.windowLayer.destroy();
2022-05-20 16:44:33 +00:00
}
}