fix(ui): standardize tile-based layouts across input, product card, and terminal preview components

This commit is contained in:
2026-04-03 12:37:57 +00:00
parent 23d672040c
commit 42fd0b276e
8 changed files with 132 additions and 138 deletions

View File

@@ -1,5 +1,12 @@
# Changelog # Changelog
## 2026-04-03 - 3.51.2 - fix(ui)
standardize tile-based layouts across input, product card, and terminal preview components
- Replace custom card containers with dees-tile in code input, richtext editor, shopping product card, and terminal preview components
- Move toolbars and footers into dees-tile header/footer slots for more consistent structure and spacing
- Update hover, focus, and selected state styling to target dees-tile parts while preserving existing component behavior
## 2026-04-03 - 3.51.1 - fix(repo) ## 2026-04-03 - 3.51.1 - fix(repo)
no changes to commit no changes to commit

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@design.estate/dees-catalog', name: '@design.estate/dees-catalog',
version: '3.51.1', version: '3.51.2',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
} }

View File

@@ -12,6 +12,7 @@ import { themeDefaultStyles } from '../../00theme.js';
import { DeesModal } from '../../00group-overlay/dees-modal/dees-modal.js'; import { DeesModal } from '../../00group-overlay/dees-modal/dees-modal.js';
import '../../00group-utility/dees-icon/dees-icon.js'; import '../../00group-utility/dees-icon/dees-icon.js';
import '../../00group-layout/dees-label/dees-label.js'; import '../../00group-layout/dees-label/dees-label.js';
import '../../00group-layout/dees-tile/dees-tile.js';
import '../../00group-workspace/dees-workspace-monaco/dees-workspace-monaco.js'; import '../../00group-workspace/dees-workspace-monaco/dees-workspace-monaco.js';
import { DeesWorkspaceMonaco } from '../../00group-workspace/dees-workspace-monaco/dees-workspace-monaco.js'; import { DeesWorkspaceMonaco } from '../../00group-workspace/dees-workspace-monaco/dees-workspace-monaco.js';
@@ -105,24 +106,16 @@ export class DeesInputCode extends DeesInputBase<string> {
min-height: 0; min-height: 0;
} }
.code-container { dees-tile {
display: flex;
flex-direction: column;
flex: 1; flex: 1;
min-height: 0; min-height: 0;
border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
border-radius: 6px;
overflow: hidden;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 9%)')};
} }
.toolbar { .toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 8px 12px; padding: 4px 12px;
background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(0 0% 7%)')};
border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
gap: 8px; gap: 8px;
} }
@@ -256,8 +249,8 @@ export class DeesInputCode extends DeesInputBase<string> {
</style> </style>
<div class="input-wrapper"> <div class="input-wrapper">
<dees-label .label=${this.label} .description=${this.description} .required=${this.required}></dees-label> <dees-label .label=${this.label} .description=${this.description} .required=${this.required}></dees-label>
<div class="code-container"> <dees-tile>
<div class="toolbar"> <div slot="header" class="toolbar">
<div class="toolbar-left"> <div class="toolbar-left">
<div class="language-selector"> <div class="language-selector">
<button <button
@@ -322,7 +315,7 @@ export class DeesInputCode extends DeesInputBase<string> {
@content-change=${this.handleContentChange} @content-change=${this.handleContentChange}
></dees-workspace-monaco> ></dees-workspace-monaco>
</div> </div>
</div> </dees-tile>
</div> </div>
`; `;
} }

View File

@@ -16,6 +16,7 @@ import {
import type { Editor } from '@tiptap/core'; import type { Editor } from '@tiptap/core';
import { DeesServiceLibLoader, type ITiptapBundle } from '../../../services/index.js'; import { DeesServiceLibLoader, type ITiptapBundle } from '../../../services/index.js';
import '../../00group-layout/dees-tile/dees-tile.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@@ -23,22 +23,15 @@ export const richtextStyles = [
color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 93.9%)')}; color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 93.9%)')};
} }
.editor-container { dees-tile {
display: flex; min-height: 200px;
flex-direction: column;
min-height: ${cssManager.bdTheme('200px', '200px')};
border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
border-radius: 6px;
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 9%)')};
overflow: hidden;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
} }
.editor-container:hover { dees-tile:hover::part(outer) {
border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')}; border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')};
} }
.editor-container.focused { dees-tile.focused::part(outer) {
border-color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 98%)')}; border-color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 98%)')};
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(0 0% 9% / 0.05)', 'hsl(0 0% 98% / 0.05)')}; box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(0 0% 9% / 0.05)', 'hsl(0 0% 98% / 0.05)')};
} }
@@ -47,9 +40,7 @@ export const richtextStyles = [
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 4px; gap: 4px;
padding: 8px 12px; padding: 4px 12px;
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(0 0% 14.9%)')};
border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
align-items: center; align-items: center;
position: relative; position: relative;
} }
@@ -199,14 +190,15 @@ export const richtextStyles = [
} }
.editor-footer { .editor-footer {
padding: 8px 12px; padding: 0 12px;
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(0 0% 14.9%)')}; height: 28px;
border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; font-size: 11px;
font-size: 12px; color: ${cssManager.bdTheme('hsl(0 0% 45%)', 'hsl(0 0% 55%)')};
color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')};
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
width: 100%;
box-sizing: border-box;
} }
.word-count { .word-count {

View File

@@ -5,8 +5,8 @@ export const renderRichtext = (component: DeesInputRichtext): TemplateResult =>
return html` return html`
<div class="input-wrapper"> <div class="input-wrapper">
${component.label ? html`<label class="label">${component.label}</label>` : ''} ${component.label ? html`<label class="label">${component.label}</label>` : ''}
<div class="editor-container ${component.editor?.isFocused ? 'focused' : ''}" style="--min-height: ${component.minHeight}px"> <dees-tile class="${component.editor?.isFocused ? 'focused' : ''}" style="--min-height: ${component.minHeight}px">
<div class="editor-toolbar"> <div slot="header" class="editor-toolbar">
${component.renderToolbar()} ${component.renderToolbar()}
<div class="link-input ${component.showLinkInput ? 'show' : ''}"> <div class="link-input ${component.showLinkInput ? 'show' : ''}">
<input type="url" placeholder="Enter URL..." @keydown=${component.handleLinkInputKeydown} /> <input type="url" placeholder="Enter URL..." @keydown=${component.handleLinkInputKeydown} />
@@ -20,12 +20,12 @@ export const renderRichtext = (component: DeesInputRichtext): TemplateResult =>
<div class="editor-content"></div> <div class="editor-content"></div>
${component.showWordCount ${component.showWordCount
? html` ? html`
<div class="editor-footer"> <div slot="footer" class="editor-footer">
<span class="word-count">${component.wordCount} word${component.wordCount !== 1 ? 's' : ''}</span> <span class="word-count">${component.wordCount} word${component.wordCount !== 1 ? 's' : ''}</span>
</div> </div>
` `
: ''} : ''}
</div> </dees-tile>
${component.description ? html`<div class="description">${component.description}</div>` : ''} ${component.description ? html`<div class="description">${component.description}</div>` : ''}
</div> </div>
`; `;

View File

@@ -9,6 +9,7 @@ import {
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import { demoFunc } from './dees-shopping-productcard.demo.js'; import { demoFunc } from './dees-shopping-productcard.demo.js';
import { themeDefaultStyles } from '../../00theme.js'; import { themeDefaultStyles } from '../../00theme.js';
import '../../00group-layout/dees-tile/dees-tile.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
@@ -61,28 +62,20 @@ export class DeesShoppingProductcard extends DeesElement {
display: block; display: block;
} }
.product-card { dees-tile {
background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(215 20.2% 11.8%)')};
border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
border-radius: 8px;
overflow: hidden;
transition: all 0.2s ease;
display: flex;
flex-direction: column;
height: 100%; height: 100%;
position: relative;
} }
.product-card:hover { dees-tile:hover::part(outer) {
border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')}; border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')};
box-shadow: 0 4px 6px -1px hsl(0 0% 0% / 0.1), 0 2px 4px -2px hsl(0 0% 0% / 0.1); box-shadow: 0 4px 6px -1px hsl(0 0% 0% / 0.1), 0 2px 4px -2px hsl(0 0% 0% / 0.1);
} }
.product-card.selectable { dees-tile.selectable {
cursor: pointer; cursor: pointer;
} }
.product-card.selected { dees-tile.selected::part(outer) {
border-color: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')}; border-color: ${cssManager.bdTheme('hsl(217.2 91.2% 59.8%)', 'hsl(213.1 93.9% 67.8%)')};
box-shadow: 0 0 0 3px ${cssManager.bdTheme('hsl(217.2 91.2% 59.8% / 0.1)', 'hsl(213.1 93.9% 67.8% / 0.1)')}; box-shadow: 0 0 0 3px ${cssManager.bdTheme('hsl(217.2 91.2% 59.8% / 0.1)', 'hsl(213.1 93.9% 67.8% / 0.1)')};
} }
@@ -143,34 +136,46 @@ export class DeesShoppingProductcard extends DeesElement {
transform: scale(1); transform: scale(1);
} }
.product-content { .product-header-bar {
padding: 16px;
display: flex; display: flex;
flex-direction: column; align-items: center;
gap: 12px; justify-content: space-between;
flex: 1; height: 32px;
padding: 0 16px;
gap: 8px;
} }
.product-header { .product-name {
display: flex; font-size: 14px;
flex-direction: column; font-weight: 500;
gap: 4px; color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 63.9%)')};
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.product-category { .product-category {
font-size: 12px; font-size: 11px;
font-weight: 500; font-weight: 500;
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')}; color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
line-height: 1.3; white-space: nowrap;
flex-shrink: 0;
} }
.product-name { .product-body {
font-size: 16px; display: flex;
font-weight: 600; flex-direction: column;
color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')}; height: 100%;
line-height: 1.4; }
.product-content {
padding: 12px 16px;
display: flex;
flex-direction: column;
gap: 8px;
flex: 1;
} }
.product-description { .product-description {
@@ -185,8 +190,9 @@ export class DeesShoppingProductcard extends DeesElement {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
padding-top: 12px; padding: 12px 16px;
border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; width: 100%;
box-sizing: border-box;
} }
.product-price { .product-price {
@@ -248,10 +254,15 @@ export class DeesShoppingProductcard extends DeesElement {
}; };
return html` return html`
<div <dees-tile
class="product-card ${this.selectable ? 'selectable' : ''} ${this.selected ? 'selected' : ''}" class="${this.selectable ? 'selectable' : ''} ${this.selected ? 'selected' : ''}"
@click=${this.handleCardClick} @click=${this.handleCardClick}
> >
<div slot="header" class="product-header-bar">
<span class="product-name">${name}</span>
${category ? html`<span class="product-category">${category}</span>` : ''}
</div>
<div class="product-body">
<div class="product-image"> <div class="product-image">
${imageUrl ? html` ${imageUrl ? html`
<img src="${imageUrl}" alt="${name}"> <img src="${imageUrl}" alt="${name}">
@@ -271,10 +282,6 @@ export class DeesShoppingProductcard extends DeesElement {
` : ''} ` : ''}
</div> </div>
<div class="product-content"> <div class="product-content">
<div class="product-header">
${category ? html`<div class="product-category">${category}</div>` : ''}
<div class="product-name">${name}</div>
</div>
${description ? html` ${description ? html`
<div class="product-description">${description}</div> <div class="product-description">${description}</div>
` : ''} ` : ''}
@@ -282,7 +289,9 @@ export class DeesShoppingProductcard extends DeesElement {
<dees-icon .icon=${inStock ? 'lucide:check-circle' : 'lucide:x-circle'}></dees-icon> <dees-icon .icon=${inStock ? 'lucide:check-circle' : 'lucide:x-circle'}></dees-icon>
${stockText} ${stockText}
</div> </div>
<div class="product-footer"> </div>
</div>
<div slot="footer" class="product-footer">
<div class="product-price"> <div class="product-price">
<span class="price-current">${formatPrice(price)}</span> <span class="price-current">${formatPrice(price)}</span>
${originalPrice && originalPrice > price ? html` ${originalPrice && originalPrice > price ? html`
@@ -306,8 +315,7 @@ export class DeesShoppingProductcard extends DeesElement {
></dees-input-quantityselector> ></dees-input-quantityselector>
` : ''} ` : ''}
</div> </div>
</div> </dees-tile>
</div>
`; `;
} }

View File

@@ -11,6 +11,7 @@ import type { Terminal } from 'xterm';
import type { FitAddon } from 'xterm-addon-fit'; import type { FitAddon } from 'xterm-addon-fit';
import { themeDefaultStyles } from '../../00theme.js'; import { themeDefaultStyles } from '../../00theme.js';
import { DeesServiceLibLoader } from '../../../services/index.js'; import { DeesServiceLibLoader } from '../../../services/index.js';
import '../../00group-layout/dees-tile/dees-tile.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
@@ -68,27 +69,19 @@ export class DeesWorkspaceTerminalPreview extends DeesElement {
height: 200px; height: 200px;
} }
.terminal-preview { dees-tile {
height: 100%; height: 100%;
border-radius: 8px;
overflow: hidden;
background: ${cssManager.bdTheme('#ffffff', '#000000')};
border: 1px solid ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')};
display: flex;
flex-direction: column;
} }
.terminal-header { .terminal-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
padding: 8px 12px; padding: 0 12px;
background: ${cssManager.bdTheme('hsl(0 0% 96%)', 'hsl(0 0% 10%)')}; height: 32px;
font-size: 12px; font-size: 12px;
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace; font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
color: ${cssManager.bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 60%)')}; color: ${cssManager.bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 60%)')};
border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')};
flex-shrink: 0;
} }
.terminal-header-icon { .terminal-header-icon {
@@ -262,15 +255,15 @@ export class DeesWorkspaceTerminalPreview extends DeesElement {
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="terminal-preview"> <dees-tile>
<div class="terminal-header"> <div slot="header" class="terminal-header">
<span class="terminal-header-icon">$</span> <span class="terminal-header-icon">$</span>
<span class="terminal-header-command">${this.command || 'Waiting...'}</span> <span class="terminal-header-command">${this.command || 'Waiting...'}</span>
</div> </div>
<div class="terminal-container"> <div class="terminal-container">
<div id="xterm-container"></div> <div id="xterm-container"></div>
</div> </div>
</div> </dees-tile>
`; `;
} }