Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55e8e192c9 | |||
| 286f6fd120 | |||
| 1401cd2c92 | |||
| 2323d1a01c | |||
| bbb6d09ecf | |||
| 2f54ee3f85 |
17
changelog.md
17
changelog.md
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
14
package.json
14
package.json
@@ -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
906
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -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'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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%)')};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -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,65 +230,74 @@ 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 {
|
||||||
:host {
|
width: 320px;
|
||||||
width: 100%;
|
flex-shrink: 0;
|
||||||
height: 100%;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
sio-conversation-selector {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
transition: left 300ms ease, opacity 200ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
sio-conversation-view {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
transition: left 300ms ease, opacity 200ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile navigation states */
|
|
||||||
.container.show-list sio-conversation-selector {
|
|
||||||
left: 0;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container.show-list sio-conversation-view {
|
|
||||||
left: 100%;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container.show-conversation sio-conversation-selector {
|
|
||||||
left: -100%;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container.show-conversation sio-conversation-view {
|
|
||||||
left: 0;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 601px) {
|
sio-conversation-view {
|
||||||
sio-conversation-selector {
|
flex: 1;
|
||||||
width: 320px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
sio-conversation-view {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
// Mobile responsive layout - full screen with sliding mechanics
|
||||||
|
cssManager.cssForPhablet(css`
|
||||||
|
:host {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host::before {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
sio-conversation-selector {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: left 300ms ease, opacity 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
sio-conversation-view {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: left 300ms ease, opacity 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile navigation states */
|
||||||
|
.container.show-list sio-conversation-selector {
|
||||||
|
left: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.show-list sio-conversation-view {
|
||||||
|
left: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.show-conversation sio-conversation-selector {
|
||||||
|
left: -100%;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.show-conversation sio-conversation-view {
|
||||||
|
left: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keyboard visible adjustments */
|
||||||
|
:host([keyboard-visible]) {
|
||||||
|
height: 100vh;
|
||||||
|
height: 100dvh;
|
||||||
|
}
|
||||||
|
`),
|
||||||
];
|
];
|
||||||
|
|
||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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,194 +45,235 @@ export class SioFab extends DeesElement {
|
|||||||
domtools.DomTools.setupDomTools();
|
domtools.DomTools.setupDomTools();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static styles = [
|
||||||
|
cssManager.defaultStyles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
will-change: transform;
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 10000;
|
||||||
|
color: #fff;
|
||||||
|
--fab-gradient-start: #6366f1;
|
||||||
|
--fab-gradient-mid: #8b5cf6;
|
||||||
|
--fab-gradient-end: #a855f7;
|
||||||
|
--fab-gradient-hover-end: #c026d3;
|
||||||
|
--fab-shadow-color: rgba(139, 92, 246, 0.25);
|
||||||
|
--fab-size: 60px;
|
||||||
|
--fab-combox-offset: calc(var(--fab-size) + ${unsafeCSS(spacing["4"])});
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox {
|
||||||
|
transition: ${unsafeCSS(transitions.all)};
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
right: 0px;
|
||||||
|
height: var(--fab-size);
|
||||||
|
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);
|
||||||
|
line-height: var(--fab-size);
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-end) 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: ${unsafeCSS(radius.full)};
|
||||||
|
user-select: none;
|
||||||
|
border: none;
|
||||||
|
animation: fabEntrance 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
overflow: hidden;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 50%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
left: -4px;
|
||||||
|
right: -4px;
|
||||||
|
bottom: -4px;
|
||||||
|
background: linear-gradient(135deg, var(--fab-gradient-start), var(--fab-gradient-end));
|
||||||
|
border-radius: inherit;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0;
|
||||||
|
filter: blur(12px);
|
||||||
|
transition: opacity 300ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox:hover::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox:hover::after {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fabEntrance {
|
||||||
|
from {
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-hover-end) 100%);
|
||||||
|
box-shadow: 0 8px 20px -4px var(--fab-shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
box-shadow: 0 4px 12px -2px var(--fab-shadow-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox.pulse::after {
|
||||||
|
animation: fabPulse 0.6s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fabPulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.4);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 12px rgba(139, 92, 246, 0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox .icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox .icon.open {
|
||||||
|
opacity: 1;
|
||||||
|
transform: rotate(0deg) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox .icon.close {
|
||||||
|
opacity: 0;
|
||||||
|
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 {
|
||||||
|
color: white;
|
||||||
|
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainbox .icon.close sio-icon {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#comboxContainer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comboxContainer sio-combox {
|
||||||
|
position: absolute;
|
||||||
|
bottom: var(--fab-combox-offset);
|
||||||
|
right: 0;
|
||||||
|
transition: ${unsafeCSS(transitions.all)};
|
||||||
|
will-change: transform;
|
||||||
|
transform: translateY(${unsafeCSS(spacing["5"])});
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comboxContainer.show {
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comboxContainer.show sio-combox {
|
||||||
|
transform: translateY(0px);
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
// 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 {
|
public render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${domtools.elementBasic.styles}
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
will-change: transform;
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 20px;
|
|
||||||
z-index: 10000;
|
|
||||||
color: #fff;
|
|
||||||
--fab-gradient-start: #6366f1;
|
|
||||||
--fab-gradient-mid: #8b5cf6;
|
|
||||||
--fab-gradient-end: #a855f7;
|
|
||||||
--fab-gradient-hover-end: #c026d3;
|
|
||||||
--fab-shadow-color: rgba(139, 92, 246, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox {
|
|
||||||
transition: ${transitions.all};
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0px;
|
|
||||||
right: 0px;
|
|
||||||
height: 60px;
|
|
||||||
width: 60px;
|
|
||||||
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;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
background: linear-gradient(135deg, var(--fab-gradient-start) 0%, var(--fab-gradient-mid) 50%, var(--fab-gradient-end) 100%);
|
|
||||||
color: white;
|
|
||||||
border-radius: ${radius.full};
|
|
||||||
user-select: none;
|
|
||||||
border: none;
|
|
||||||
animation: fabEntrance 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 50%);
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 200ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: -4px;
|
|
||||||
left: -4px;
|
|
||||||
right: -4px;
|
|
||||||
bottom: -4px;
|
|
||||||
background: linear-gradient(135deg, var(--fab-gradient-start), var(--fab-gradient-end));
|
|
||||||
border-radius: inherit;
|
|
||||||
z-index: -1;
|
|
||||||
opacity: 0;
|
|
||||||
filter: blur(12px);
|
|
||||||
transition: opacity 300ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox:hover::before {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox:hover::after {
|
|
||||||
opacity: 0.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fabEntrance {
|
|
||||||
from {
|
|
||||||
transform: scale(0.8);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: scale(1);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox:hover {
|
|
||||||
transform: scale(1.02);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox:active {
|
|
||||||
transform: scale(0.98);
|
|
||||||
box-shadow: 0 4px 12px -2px var(--fab-shadow-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox.pulse::after {
|
|
||||||
animation: fabPulse 0.6s ease-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fabPulse {
|
|
||||||
0% {
|
|
||||||
box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.4);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 0 0 12px rgba(139, 92, 246, 0);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox .icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
will-change: transform, opacity;
|
|
||||||
transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#mainbox .icon.open {
|
|
||||||
opacity: ${this.showCombox ? '0' : '1'};
|
|
||||||
transform: ${this.showCombox ? 'rotate(45deg) scale(0.9)' : 'rotate(0deg) scale(1)'};
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox .icon.close {
|
|
||||||
opacity: ${this.showCombox ? '1' : '0'};
|
|
||||||
transform: ${this.showCombox ? 'rotate(0deg) scale(1)' : 'rotate(-45deg) scale(0.9)'};
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox .icon sio-icon {
|
|
||||||
color: white;
|
|
||||||
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#mainbox .icon.close sio-icon {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#comboxContainer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#comboxContainer sio-combox {
|
|
||||||
position: absolute;
|
|
||||||
bottom: calc(60px + ${spacing["4"]});
|
|
||||||
right: 0;
|
|
||||||
transition: ${transitions.all};
|
|
||||||
will-change: transform;
|
|
||||||
transform: translateY(${spacing["5"]});
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#comboxContainer.show {
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
|
|
||||||
#comboxContainer.show sio-combox {
|
|
||||||
transform: translateY(0px);
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<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)) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user