6 Commits

Author SHA1 Message Date
55e8e192c9 v1.4.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-17 10:07:18 +00:00
286f6fd120 fix(ui): handle on-screen keyboard visibility to adjust layout and prevent inputs from being obscured 2025-12-17 10:07:18 +00:00
1401cd2c92 update 2025-12-17 09:27:53 +00:00
2323d1a01c update 2025-12-17 09:22:02 +00:00
bbb6d09ecf v1.4.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-17 09:13:52 +00:00
2f54ee3f85 feat(elements): update design tokens and sio-fab component; bump deps and update npmextra config 2025-12-17 09:13:52 +00:00
11 changed files with 1312 additions and 382 deletions

View File

@@ -1,5 +1,22 @@
# Changelog # Changelog
## 2025-12-17 - 1.4.1 - fix(ui)
handle on-screen keyboard visibility to adjust layout and prevent inputs from being obscured
- Add keyboard visibility state (isKeyboardVisible) and keyboardBlurTimeout in sio-combox.ts
- Listen for custom 'input-focus' and 'input-blur' events and toggle keyboard-visible host attribute
- Dispatch 'input-focus'/'input-blur' from sio-conversation-selector and sio-message-input on focus/blur
- Add connected/disconnected lifecycle handlers and updated() hook to manage attribute and cleanup timeouts
- Apply :host([keyboard-visible]) CSS to set height to 100vh / 100dvh when keyboard is visible
## 2025-12-17 - 1.4.0 - feat(elements)
update design tokens and sio-fab component; bump deps and update npmextra config
- Refactor color tokens to a neutral HSL palette (ts_web/elements/00colors.ts) and adjust focus ring token (ts_web/elements/00tokens.ts).
- Refactor sio-fab: move styles to static property, add responsive FAB sizing and getMobileIconSize(), bind icon sizes, manage host class ('combox-open'), and tidy lifecycle methods for better behavior and mobile support.
- Bump dependencies and devDependencies: @design.estate/dees-wcctools -> ^2.0.1, lucide -> ^0.561.0; @git.zone/tsbuild -> ^4.0.2, @git.zone/tsrun -> ^2.0.1, @git.zone/tswatch -> ^2.3.13, @types/node -> ^25.0.3, etc.
- Update npmextra.json: rename configuration keys (gitzone -> @git.zone/cli, npmci -> @ship.zone/szci) and add release.registries and accessLevel for publishing.
## 2025-12-08 - 1.3.0 - feat(components) ## 2025-12-08 - 1.3.0 - feat(components)
Add reusable message input component, refactor element properties to use accessor, update styles and docs, bump dependencies Add reusable message input component, refactor element properties to use accessor, update styles and docs, bump dependencies

View File

@@ -1,5 +1,5 @@
{ {
"gitzone": { "@git.zone/cli": {
"projectType": "wcc", "projectType": "wcc",
"module": { "module": {
"githost": "gitlab.com", "githost": "gitlab.com",
@@ -9,11 +9,16 @@
"npmPackagename": "@social.io_private/catalog", "npmPackagename": "@social.io_private/catalog",
"license": "UNLICENSED", "license": "UNLICENSED",
"projectDomain": "social.io" "projectDomain": "social.io"
},
"release": {
"registries": [
"https://verdaccio.lossless.digital"
],
"accessLevel": "public"
} }
}, },
"npmci": { "@ship.zone/szci": {
"npmRegistryUrl": "verdaccio.lossless.one", "npmRegistryUrl": "verdaccio.lossless.one",
"npmGlobalTools": [], "npmGlobalTools": []
"npmAccessLevel": "private"
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@social.io/catalog", "name": "@social.io/catalog",
"version": "1.3.0", "version": "1.4.1",
"private": false, "private": false,
"description": "catalog for social.io", "description": "catalog for social.io",
"main": "dist_ts_web/index.js", "main": "dist_ts_web/index.js",
@@ -17,23 +17,23 @@
"dependencies": { "dependencies": {
"@design.estate/dees-domtools": "^2.3.6", "@design.estate/dees-domtools": "^2.3.6",
"@design.estate/dees-element": "^2.1.3", "@design.estate/dees-element": "^2.1.3",
"@design.estate/dees-wcctools": "^1.2.1", "@design.estate/dees-wcctools": "^2.0.1",
"@losslessone_private/loint-pubapi": "^1.0.14", "@losslessone_private/loint-pubapi": "^1.0.14",
"@social.io/interfaces": "^1.2.1", "@social.io/interfaces": "^1.2.1",
"lucide": "^0.556.0", "lucide": "^0.561.0",
"rrweb": "2.0.0-alpha.4", "rrweb": "2.0.0-alpha.4",
"rrweb-player": "1.0.0-alpha.4", "rrweb-player": "1.0.0-alpha.4",
"rrweb-snapshot": "2.0.0-alpha.4" "rrweb-snapshot": "2.0.0-alpha.4"
}, },
"devDependencies": { "devDependencies": {
"@git.zone/tsbuild": "^3.1.2", "@git.zone/tsbuild": "^4.0.2",
"@git.zone/tsbundle": "^2.6.3", "@git.zone/tsbundle": "^2.6.3",
"@git.zone/tsrun": "^2.0.0", "@git.zone/tsrun": "^2.0.1",
"@git.zone/tstest": "^3.1.3", "@git.zone/tstest": "^3.1.3",
"@git.zone/tswatch": "^2.3.5", "@git.zone/tswatch": "^2.3.13",
"@push.rocks/projectinfo": "^5.0.2", "@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/smartenv": "^6.0.0", "@push.rocks/smartenv": "^6.0.0",
"@types/node": "^24.10.1" "@types/node": "^25.0.3"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",

906
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@social.io/catalog', name: '@social.io/catalog',
version: '1.3.0', version: '1.4.1',
description: 'catalog for social.io' description: 'catalog for social.io'
} }

View File

@@ -4,35 +4,35 @@ export const colors = {
// Background colors - softer, more subtle // Background colors - softer, more subtle
background: { background: {
light: 'hsl(0 0% 100%)', light: 'hsl(0 0% 100%)',
dark: 'hsl(224 71.4% 4.1%)' dark: 'hsl(0 0% 4%)'
}, },
// Foreground colors - less contrast for modern look // Foreground colors - less contrast for modern look
foreground: { foreground: {
light: 'hsl(224 71.4% 4.1%)', light: 'hsl(0 0% 4%)',
dark: 'hsl(210 20% 98%)' dark: 'hsl(0 0% 98%)'
}, },
// Card colors - subtle elevation // Card colors - subtle elevation
card: { card: {
light: 'hsl(0 0% 100%)', light: 'hsl(0 0% 100%)',
dark: 'hsl(224 71.4% 4.1%)' dark: 'hsl(0 0% 4%)'
}, },
cardForeground: { cardForeground: {
light: 'hsl(224 71.4% 4.1%)', light: 'hsl(0 0% 4%)',
dark: 'hsl(210 20% 98%)' dark: 'hsl(0 0% 98%)'
}, },
// Popover colors // Popover colors
popover: { popover: {
light: 'hsl(0 0% 100%)', light: 'hsl(0 0% 100%)',
dark: 'hsl(222.2 84% 4.9%)' dark: 'hsl(0 0% 5%)'
}, },
popoverForeground: { popoverForeground: {
light: 'hsl(222.2 84% 4.9%)', light: 'hsl(0 0% 5%)',
dark: 'hsl(210 40% 98%)' dark: 'hsl(0 0% 98%)'
}, },
// Primary colors - modern indigo/blue // Primary colors - modern indigo/blue
@@ -42,41 +42,41 @@ export const colors = {
}, },
primaryForeground: { primaryForeground: {
light: 'hsl(210 20% 98%)', light: 'hsl(0 0% 98%)',
dark: 'hsl(224 71.4% 4.1%)' dark: 'hsl(0 0% 4%)'
}, },
// Secondary colors - more subtle // Secondary colors - more subtle
secondary: { secondary: {
light: 'hsl(220 14.3% 95.9%)', light: 'hsl(0 0% 96%)',
dark: 'hsl(215 27.9% 16.9%)' dark: 'hsl(0 0% 17%)'
}, },
secondaryForeground: { secondaryForeground: {
light: 'hsl(220.9 39.3% 11%)', light: 'hsl(0 0% 11%)',
dark: 'hsl(210 20% 98%)' dark: 'hsl(0 0% 98%)'
}, },
// Muted colors - softer grays // Muted colors - softer grays
muted: { muted: {
light: 'hsl(220 14.3% 95.9%)', light: 'hsl(0 0% 96%)',
dark: 'hsl(215 27.9% 16.9%)' dark: 'hsl(0 0% 17%)'
}, },
mutedForeground: { mutedForeground: {
light: 'hsl(220 8.9% 46.1%)', light: 'hsl(0 0% 46%)',
dark: 'hsl(217.9 10.6% 64.9%)' dark: 'hsl(0 0% 65%)'
}, },
// Accent colors - subtle hover states // Accent colors - subtle hover states
accent: { accent: {
light: 'hsl(220 14.3% 95.9%)', light: 'hsl(0 0% 96%)',
dark: 'hsl(215 27.9% 16.9%)' dark: 'hsl(0 0% 17%)'
}, },
accentForeground: { accentForeground: {
light: 'hsl(220.9 39.3% 11%)', light: 'hsl(0 0% 11%)',
dark: 'hsl(210 20% 98%)' dark: 'hsl(0 0% 98%)'
}, },
// Destructive colors - softer red // Destructive colors - softer red
@@ -92,14 +92,14 @@ export const colors = {
// Border color - very subtle // Border color - very subtle
border: { border: {
light: 'hsl(220 13% 91%)', light: 'hsl(0 0% 91%)',
dark: 'hsl(215 27.9% 16.9%)' dark: 'hsl(0 0% 17%)'
}, },
// Input color // Input color
input: { input: {
light: 'hsl(214.3 31.8% 91.4%)', light: 'hsl(0 0% 91%)',
dark: 'hsl(217.2 32.6% 17.5%)' dark: 'hsl(0 0% 18%)'
}, },
// Ring color - subtle focus indicator // Ring color - subtle focus indicator

View File

@@ -185,7 +185,7 @@ export const focusRing = css`
outline: 2px solid transparent; outline: 2px solid transparent;
outline-offset: 2px; outline-offset: 2px;
&:focus-visible { &:focus-visible {
outline-color: ${cssManager.bdTheme('hsl(222.2 84% 4.9%)', 'hsl(212.7 26.8% 83.9%)')}; outline-color: ${cssManager.bdTheme('hsl(0 0% 5%)', 'hsl(0 0% 84%)')};
} }
`; `;

View File

@@ -42,6 +42,11 @@ export class SioCombox extends DeesElement {
@state() @state()
private accessor selectedConversationId: string | null = null; private accessor selectedConversationId: string | null = null;
@state()
private accessor isKeyboardVisible: boolean = false;
private keyboardBlurTimeout?: number;
@state() @state()
private accessor conversations: IConversation[] = [ private accessor conversations: IConversation[] = [
{ {
@@ -127,6 +132,50 @@ export class SioCombox extends DeesElement {
domtools.DomTools.setupDomTools(); domtools.DomTools.setupDomTools();
} }
async connectedCallback() {
await super.connectedCallback();
this.addEventListener('input-focus', this.handleInputFocus as EventListener);
this.addEventListener('input-blur', this.handleInputBlur as EventListener);
}
async disconnectedCallback() {
await super.disconnectedCallback();
this.removeEventListener('input-focus', this.handleInputFocus as EventListener);
this.removeEventListener('input-blur', this.handleInputBlur as EventListener);
if (this.keyboardBlurTimeout) {
clearTimeout(this.keyboardBlurTimeout);
}
}
private handleInputFocus = () => {
if (this.keyboardBlurTimeout) {
clearTimeout(this.keyboardBlurTimeout);
this.keyboardBlurTimeout = undefined;
}
this.isKeyboardVisible = true;
}
private handleInputBlur = () => {
if (this.keyboardBlurTimeout) {
clearTimeout(this.keyboardBlurTimeout);
}
this.keyboardBlurTimeout = window.setTimeout(() => {
this.isKeyboardVisible = false;
this.keyboardBlurTimeout = undefined;
}, 150);
}
updated(changedProperties: Map<string, any>) {
super.updated(changedProperties);
if (changedProperties.has('isKeyboardVisible')) {
if (this.isKeyboardVisible) {
this.setAttribute('keyboard-visible', '');
} else {
this.removeAttribute('keyboard-visible');
}
}
}
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
css` css`
@@ -181,16 +230,31 @@ export class SioCombox extends DeesElement {
border-radius: ${unsafeCSS(radius['2xl'])}; border-radius: ${unsafeCSS(radius['2xl'])};
} }
/* Responsive layout */ /* Desktop layout (default) */
@media (max-width: 600px) { sio-conversation-selector {
width: 320px;
flex-shrink: 0;
}
sio-conversation-view {
flex: 1;
}
`,
// Mobile responsive layout - full screen with sliding mechanics
cssManager.cssForPhablet(css`
:host { :host {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 0; border-radius: 0;
} }
:host::before {
border-radius: 0;
}
.container { .container {
position: relative; position: relative;
overflow: hidden;
} }
sio-conversation-selector { sio-conversation-selector {
@@ -227,19 +291,13 @@ export class SioCombox extends DeesElement {
left: 0; left: 0;
opacity: 1; opacity: 1;
} }
}
@media (min-width: 601px) { /* Keyboard visible adjustments */
sio-conversation-selector { :host([keyboard-visible]) {
width: 320px; height: 100vh;
flex-shrink: 0; height: 100dvh;
} }
`),
sio-conversation-view {
flex: 1;
}
}
`,
]; ];
public render(): TemplateResult { public render(): TemplateResult {

View File

@@ -266,7 +266,17 @@ export class SioConversationSelector extends DeesElement {
.conversation-list::-webkit-scrollbar-thumb:hover { .conversation-list::-webkit-scrollbar-thumb:hover {
background: ${bdTheme('mutedForeground')}; background: ${bdTheme('mutedForeground')};
} }
.close-button {
display: none;
}
`, `,
// Mobile: show close button
cssManager.cssForPhablet(css`
.close-button {
display: flex;
}
`),
]; ];
public render(): TemplateResult { public render(): TemplateResult {
@@ -278,6 +288,14 @@ export class SioConversationSelector extends DeesElement {
return html` return html`
<div class="header"> <div class="header">
<div class="header-top"> <div class="header-top">
<sio-button
class="close-button"
type="ghost"
size="sm"
@click=${() => this.handleClose()}
>
<sio-icon icon="x" size="20"></sio-icon>
</sio-button>
<h2 class="title">Messages</h2> <h2 class="title">Messages</h2>
<sio-button <sio-button
type="primary" type="primary"
@@ -295,6 +313,8 @@ export class SioConversationSelector extends DeesElement {
placeholder="Search conversations..." placeholder="Search conversations..."
.value=${this.searchQuery} .value=${this.searchQuery}
@input=${(e: Event) => this.searchQuery = (e.target as HTMLInputElement).value} @input=${(e: Event) => this.searchQuery = (e.target as HTMLInputElement).value}
@focus=${this.handleInputFocus}
@blur=${this.handleInputBlur}
/> />
<sio-icon class="search-icon" icon="search" size="16"></sio-icon> <sio-icon class="search-icon" icon="search" size="16"></sio-icon>
</div> </div>
@@ -346,4 +366,27 @@ export class SioConversationSelector extends DeesElement {
composed: true composed: true
})); }));
} }
private handleClose() {
this.dispatchEvent(new CustomEvent('close', {
bubbles: true,
composed: true
}));
}
private handleInputFocus() {
setTimeout(() => {
this.dispatchEvent(new CustomEvent('input-focus', {
bubbles: true,
composed: true
}));
}, 50);
}
private handleInputBlur() {
this.dispatchEvent(new CustomEvent('input-blur', {
bubbles: true,
composed: true
}));
}
} }

View File

@@ -6,12 +6,13 @@ import {
type TemplateResult, type TemplateResult,
cssManager, cssManager,
css, css,
unsafeCSS,
state,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import * as domtools from '@design.estate/dees-domtools'; import * as domtools from '@design.estate/dees-domtools';
import { SioCombox } from './sio-combox.js'; import { SioCombox } from './sio-combox.js';
import { SioIcon } from './sio-icon.js'; import { SioIcon } from './sio-icon.js';
import { state } from '@design.estate/dees-element';
SioCombox; SioCombox;
SioIcon; SioIcon;
@@ -44,10 +45,9 @@ export class SioFab extends DeesElement {
domtools.DomTools.setupDomTools(); domtools.DomTools.setupDomTools();
} }
public render(): TemplateResult { public static styles = [
return html` cssManager.defaultStyles,
${domtools.elementBasic.styles} css`
<style>
:host { :host {
will-change: transform; will-change: transform;
position: absolute; position: absolute;
@@ -61,27 +61,28 @@ export class SioFab extends DeesElement {
--fab-gradient-end: #a855f7; --fab-gradient-end: #a855f7;
--fab-gradient-hover-end: #c026d3; --fab-gradient-hover-end: #c026d3;
--fab-shadow-color: rgba(139, 92, 246, 0.25); --fab-shadow-color: rgba(139, 92, 246, 0.25);
--fab-size: 60px;
--fab-combox-offset: calc(var(--fab-size) + ${unsafeCSS(spacing["4"])});
} }
#mainbox { #mainbox {
transition: ${transitions.all}; transition: ${unsafeCSS(transitions.all)};
position: absolute; position: absolute;
bottom: 0px; bottom: 0px;
right: 0px; right: 0px;
height: 60px; height: var(--fab-size);
width: 60px; width: var(--fab-size);
box-shadow: 0 4px 16px -2px rgba(0, 0, 0, 0.1), 0 2px 8px -2px rgba(0, 0, 0, 0.06); box-shadow: 0 4px 16px -2px rgba(0, 0, 0, 0.1), 0 2px 8px -2px rgba(0, 0, 0, 0.06);
line-height: 60px; line-height: var(--fab-size);
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-end) 100%); background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-end) 100%);
color: white; color: white;
border-radius: ${radius.full}; border-radius: ${unsafeCSS(radius.full)};
user-select: none; user-select: none;
border: none; border: none;
animation: fabEntrance 300ms cubic-bezier(0.4, 0, 0.2, 1); animation: fabEntrance 300ms cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden; overflow: hidden;
position: relative;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
} }
@@ -135,9 +136,6 @@ export class SioFab extends DeesElement {
#mainbox:hover { #mainbox:hover {
transform: scale(1.02); transform: scale(1.02);
background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-hover-end) 100%); background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-hover-end) 100%);
}
#mainbox:hover {
box-shadow: 0 8px 20px -4px var(--fab-shadow-color); box-shadow: 0 8px 20px -4px var(--fab-shadow-color);
} }
@@ -174,15 +172,25 @@ export class SioFab extends DeesElement {
justify-content: center; justify-content: center;
} }
#mainbox .icon.open { #mainbox .icon.open {
opacity: ${this.showCombox ? '0' : '1'}; opacity: 1;
transform: ${this.showCombox ? 'rotate(45deg) scale(0.9)' : 'rotate(0deg) scale(1)'}; transform: rotate(0deg) scale(1);
} }
#mainbox .icon.close { #mainbox .icon.close {
opacity: ${this.showCombox ? '1' : '0'}; opacity: 0;
transform: ${this.showCombox ? 'rotate(0deg) scale(1)' : 'rotate(-45deg) scale(0.9)'}; transform: rotate(-45deg) scale(0.9);
}
/* When combox is open */
:host(.combox-open) #mainbox .icon.open {
opacity: 0;
transform: rotate(45deg) scale(0.9);
}
:host(.combox-open) #mainbox .icon.close {
opacity: 1;
transform: rotate(0deg) scale(1);
} }
#mainbox .icon sio-icon { #mainbox .icon sio-icon {
@@ -203,11 +211,11 @@ export class SioFab extends DeesElement {
#comboxContainer sio-combox { #comboxContainer sio-combox {
position: absolute; position: absolute;
bottom: calc(60px + ${spacing["4"]}); bottom: var(--fab-combox-offset);
right: 0; right: 0;
transition: ${transitions.all}; transition: ${unsafeCSS(transitions.all)};
will-change: transform; will-change: transform;
transform: translateY(${spacing["5"]}); transform: translateY(${unsafeCSS(spacing["5"])});
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
} }
@@ -221,17 +229,51 @@ export class SioFab extends DeesElement {
opacity: 1; opacity: 1;
pointer-events: all; pointer-events: all;
} }
</style> `,
// Mobile responsive styles - smaller FAB and full-screen combox
cssManager.cssForPhablet(css`
:host {
--fab-size: 48px;
bottom: 16px;
right: 16px;
will-change: auto;
}
#comboxContainer {
position: fixed;
top: 0;
left: 0;
bottom: auto;
right: auto;
width: 100vw;
height: 100vh;
height: 100dvh;
}
#comboxContainer sio-combox {
bottom: 0;
right: 0;
transform: none;
}
#comboxContainer.show sio-combox {
transform: none;
}
`),
];
public render(): TemplateResult {
return html`
<div id="mainbox" <div id="mainbox"
class="${this.shouldPulse ? 'pulse' : ''}" class="${this.shouldPulse ? 'pulse' : ''}"
@click=${this.toggleCombox} @click=${this.toggleCombox}
@animationend=${() => { this.shouldPulse = false; }} @animationend=${() => { this.shouldPulse = false; }}
> >
<div class="icon open"> <div class="icon open">
<sio-icon icon="message-square" size="28"></sio-icon> <sio-icon icon="message-square" size="24"></sio-icon>
</div> </div>
<div class="icon close"> <div class="icon close">
<sio-icon icon="x" size="22"></sio-icon> <sio-icon icon="x" size="20"></sio-icon>
</div> </div>
</div> </div>
<div id="comboxContainer" class="${this.showCombox ? 'show' : ''}"> <div id="comboxContainer" class="${this.showCombox ? 'show' : ''}">
@@ -272,6 +314,15 @@ export class SioFab extends DeesElement {
public async updated(changedProperties: Map<string | number | symbol, unknown>) { public async updated(changedProperties: Map<string | number | symbol, unknown>) {
super.updated(changedProperties); super.updated(changedProperties);
// Update host class based on combox state
if (changedProperties.has('showCombox')) {
if (this.showCombox) {
this.classList.add('combox-open');
} else {
this.classList.remove('combox-open');
}
}
// Set reference object when combox is rendered // Set reference object when combox is rendered
if ((changedProperties.has('showCombox') || changedProperties.has('hasShownOnce')) && if ((changedProperties.has('showCombox') || changedProperties.has('hasShownOnce')) &&
(this.showCombox || this.hasShownOnce)) { (this.showCombox || this.hasShownOnce)) {

View File

@@ -168,6 +168,8 @@ export class SioMessageInput extends DeesElement {
.value=${this.messageText} .value=${this.messageText}
@input=${this.handleInput} @input=${this.handleInput}
@keydown=${this.handleKeyDown} @keydown=${this.handleKeyDown}
@focus=${this.handleFocus}
@blur=${this.handleBlur}
?disabled=${this.disabled} ?disabled=${this.disabled}
rows="1" rows="1"
></textarea> ></textarea>
@@ -216,6 +218,22 @@ export class SioMessageInput extends DeesElement {
} }
} }
private handleFocus() {
setTimeout(() => {
this.dispatchEvent(new CustomEvent('input-focus', {
bubbles: true,
composed: true
}));
}, 50);
}
private handleBlur() {
this.dispatchEvent(new CustomEvent('input-blur', {
bubbles: true,
composed: true
}));
}
private sendMessage() { private sendMessage() {
if (!this.messageText.trim() && this.pendingAttachments.length === 0) { if (!this.messageText.trim() && this.pendingAttachments.length === 0) {
return; return;