diff --git a/ts_web/elements/00group-chart/dees-chart-area/component.ts b/ts_web/elements/00group-chart/dees-chart-area/component.ts index ca99605..ba93e4a 100644 --- a/ts_web/elements/00group-chart/dees-chart-area/component.ts +++ b/ts_web/elements/00group-chart/dees-chart-area/component.ts @@ -13,6 +13,7 @@ import { renderChartArea } from './template.js'; import type { IChartApi, ISeriesApi, UTCTimestamp, MouseEventParams } from 'lightweight-charts'; import { DeesServiceLibLoader, type ILightweightChartsBundle } from '../../../services/index.js'; +import '../../00group-layout/dees-tile/dees-tile.js'; export type ChartSeriesConfig = { name?: string; diff --git a/ts_web/elements/00group-chart/dees-chart-area/styles.ts b/ts_web/elements/00group-chart/dees-chart-area/styles.ts index 071df8c..45a8a3b 100644 --- a/ts_web/elements/00group-chart/dees-chart-area/styles.ts +++ b/ts_web/elements/00group-chart/dees-chart-area/styles.ts @@ -4,52 +4,27 @@ export const chartAreaStyles = [ cssManager.defaultStyles, css` :host { + display: block; + height: 400px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; color: ${cssManager.bdTheme('hsl(0 0% 3.9%)', 'hsl(0 0% 98%)')}; font-size: 14px; } - .mainbox { - position: relative; - width: 100%; - height: 400px; - background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')}; - border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - border-radius: 8px; - overflow: hidden; - } - .chartTitle { - position: absolute; - top: 0; - left: 0; - width: 100%; - text-align: left; - padding: 16px 24px; - z-index: 10; - font-size: 14px; - font-weight: 500; - letter-spacing: -0.01em; - color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 63.9%)')}; + dees-tile { + height: 100%; } .chartContainer { position: absolute; - top: 44px; - left: 0; - bottom: 32px; - right: 0; + inset: 0; } .statsBar { - position: absolute; - bottom: 0; - left: 0; - right: 0; height: 32px; + padding: 0 16px; display: flex; align-items: center; gap: 24px; - padding: 0 16px; - border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - font-size: 11px; - color: ${cssManager.bdTheme('hsl(0 0% 45%)', 'hsl(0 0% 55%)')}; + width: 100%; + box-sizing: border-box; } .statsSeries { display: flex; @@ -68,9 +43,14 @@ export const chartAreaStyles = [ } .statsName { font-weight: 500; + font-size: 11px; color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 80%)')}; margin-right: 4px; } + .statsItem { + font-size: 11px; + color: ${cssManager.bdTheme('hsl(0 0% 45%)', 'hsl(0 0% 55%)')}; + } .statsItem strong { color: ${cssManager.bdTheme('hsl(0 0% 15%)', 'hsl(0 0% 90%)')}; } diff --git a/ts_web/elements/00group-chart/dees-chart-area/template.ts b/ts_web/elements/00group-chart/dees-chart-area/template.ts index 444ec9f..72ccef4 100644 --- a/ts_web/elements/00group-chart/dees-chart-area/template.ts +++ b/ts_web/elements/00group-chart/dees-chart-area/template.ts @@ -3,11 +3,10 @@ import type { DeesChartArea } from './component.js'; export const renderChartArea = (component: DeesChartArea): TemplateResult => { return html` -
-
${component.label}
+
${component.seriesStats.length > 0 ? html` -
+
${component.seriesStats.map(s => html`
@@ -20,6 +19,6 @@ export const renderChartArea = (component: DeesChartArea): TemplateResult => { `)}
` : ''} -
+ `; }; diff --git a/ts_web/elements/00group-chart/dees-chart-log/dees-chart-log.ts b/ts_web/elements/00group-chart/dees-chart-log/dees-chart-log.ts index 0dc772c..3ab627e 100644 --- a/ts_web/elements/00group-chart/dees-chart-log/dees-chart-log.ts +++ b/ts_web/elements/00group-chart/dees-chart-log/dees-chart-log.ts @@ -13,6 +13,7 @@ import * as domtools from '@design.estate/dees-domtools'; import { demoFunc } from './dees-chart-log.demo.js'; import { themeDefaultStyles } from '../../00theme.js'; import { DeesServiceLibLoader, type IXtermSearchAddon, CDN_BASE, CDN_VERSIONS } from '../../../services/index.js'; +import '../../00group-layout/dees-tile/dees-tile.js'; // Type imports (no runtime overhead) import type { Terminal } from 'xterm'; @@ -103,30 +104,20 @@ export class DeesChartLog extends DeesElement { css` :host { display: block; + height: 400px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: ${cssManager.bdTheme('hsl(0 0% 3.9%)', 'hsl(0 0% 98%)')}; } - .mainbox { - position: relative; - width: 100%; - height: 400px; - background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')}; - border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - border-radius: 8px; - display: flex; - flex-direction: column; - overflow: hidden; + dees-tile { + height: 100%; } .header { - background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(0 0% 7%)')}; padding: 8px 12px; - border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; display: flex; align-items: center; gap: 12px; - flex-shrink: 0; flex-wrap: wrap; } @@ -241,10 +232,10 @@ export class DeesChartLog extends DeesElement { } .terminal-container { - flex: 1; + position: absolute; + inset: 0; overflow: hidden; padding: 8px; - background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')}; } .terminal-container .xterm { @@ -262,14 +253,15 @@ export class DeesChartLog extends DeesElement { } .metrics-bar { - background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(0 0% 7%)')}; - border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - padding: 6px 12px; + height: 32px; + padding: 0 12px; display: flex; + align-items: center; gap: 16px; font-size: 11px; font-weight: 500; - flex-shrink: 0; + width: 100%; + box-sizing: border-box; } .metric { @@ -323,8 +315,8 @@ export class DeesChartLog extends DeesElement { public render(): TemplateResult { return html` -
-
+ +
${this.label}
+ `; } diff --git a/ts_web/elements/00group-dataview/dees-table/dees-table.ts b/ts_web/elements/00group-dataview/dees-table/dees-table.ts index a44da9f..6f250c8 100644 --- a/ts_web/elements/00group-dataview/dees-table/dees-table.ts +++ b/ts_web/elements/00group-dataview/dees-table/dees-table.ts @@ -15,6 +15,7 @@ import { } from './data.js'; import { compileLucenePredicate } from './lucene.js'; import { themeDefaultStyles } from '../../00theme.js'; +import '../../00group-layout/dees-tile/dees-tile.js'; export type { Column, ITableAction, ITableActionDataArg, TDisplayFunction } from './types.js'; @@ -221,9 +222,8 @@ export class DeesTable extends DeesElement { ); (this as any)._lastViewData = viewData; return html` -
- -
+ +
${this.label || this.heading1}
${this.heading2}
@@ -496,7 +496,7 @@ export class DeesTable extends DeesElement {
` : html`
No data set!
`} - + `; } diff --git a/ts_web/elements/00group-dataview/dees-table/styles.ts b/ts_web/elements/00group-dataview/dees-table/styles.ts index 973e5d0..924feb9 100644 --- a/ts_web/elements/00group-dataview/dees-table/styles.ts +++ b/ts_web/elements/00group-dataview/dees-table/styles.ts @@ -12,17 +12,11 @@ export const tableStyles: CSSResult[] = [ width: 100%; } - .mainbox { + dees-tile { color: ${cssManager.bdTheme('hsl(0 0% 3.9%)', 'hsl(0 0% 98%)')}; font-family: ${cssGeistFontFamily}; font-weight: 400; font-size: 14px; - display: block; - width: 100%; - background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')}; - border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - border-radius: 8px; - overflow: hidden; cursor: default; } @@ -30,30 +24,31 @@ export const tableStyles: CSSResult[] = [ display: flex; justify-content: space-between; align-items: center; - padding: 16px 24px; - min-height: 64px; - border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + padding: 0 16px; + height: 40px; } .headingContainer { flex: 1; + display: flex; + align-items: baseline; } .heading { - line-height: 1.5; + line-height: 1; } .heading1 { - font-size: 18px; - font-weight: 600; - color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')}; - letter-spacing: -0.025em; - } - - .heading2 { font-size: 14px; + font-weight: 500; + color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 63.9%)')}; + letter-spacing: -0.01em; + } + + .heading2 { + font-size: 12px; color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')}; - margin-top: 2px; + margin-left: 8px; } .headingSeparation { @@ -70,14 +65,14 @@ export const tableStyles: CSSResult[] = [ .headerAction { display: flex; align-items: center; - gap: 6px; - padding: 6px 12px; - font-size: 14px; + gap: 4px; + padding: 4px 10px; + font-size: 12px; font-weight: 500; color: ${cssManager.bdTheme('hsl(0 0% 45.1%)', 'hsl(0 0% 63.9%)')}; background: transparent; border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - border-radius: 6px; + border-radius: 4px; cursor: pointer; transition: all 0.15s ease; } @@ -199,7 +194,7 @@ export const tableStyles: CSSResult[] = [ } tbody tr:hover { - background: ${cssManager.bdTheme('hsl(210 40% 96.1% / 0.5)', 'hsl(0 0% 14.9% / 0.5)')}; + background: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2% / 0.06)', 'hsl(217.2 91.2% 59.8% / 0.08)')}; } /* Column hover effect for better traceability */ @@ -214,7 +209,7 @@ export const tableStyles: CSSResult[] = [ bottom: 0; left: 0; right: 0; - background: ${cssManager.bdTheme('hsl(210 40% 96.1% / 0.3)', 'hsl(0 0% 14.9% / 0.3)')}; + background: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2% / 0.04)', 'hsl(217.2 91.2% 59.8% / 0.05)')}; opacity: 0; pointer-events: none; transition: opacity 0.15s ease; @@ -238,15 +233,19 @@ export const tableStyles: CSSResult[] = [ border-top: none; } - :host([show-grid]) th:first-child, - :host([show-grid]) td:first-child { - border-left: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; - } - :host([show-grid]) tbody tr:first-child td { border-top: none; } + /* Remove edge borders that would double with tile frame */ + :host([show-grid]) th:last-child, + :host([show-grid]) td:last-child { + border-right: none; + } + :host([show-grid]) tbody tr:last-child td { + border-bottom: none; + } + /* Sticky Actions column (right pinned) */ thead th.actionsCol, tbody td.actionsCol { @@ -261,7 +260,7 @@ export const tableStyles: CSSResult[] = [ } tbody tr.selected { - background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(0 0% 14.9%)')}; + background: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2% / 0.1)', 'hsl(217.2 91.2% 59.8% / 0.12)')}; } tbody tr.hasAttachment { @@ -269,10 +268,11 @@ export const tableStyles: CSSResult[] = [ } th { - height: 48px; - padding: 12px 24px; + height: 36px; + padding: 8px 16px; text-align: left; font-weight: 500; + font-size: 12px; color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')}; letter-spacing: -0.01em; } @@ -282,8 +282,9 @@ export const tableStyles: CSSResult[] = [ } td { - padding: 12px 24px; + padding: 8px 16px; vertical-align: middle; + font-size: 13px; color: ${cssManager.bdTheme('hsl(0 0% 3.9%)', 'hsl(0 0% 98%)')}; } @@ -293,18 +294,23 @@ export const tableStyles: CSSResult[] = [ th:first-child, td:first-child { - padding-left: 24px; + padding-left: 16px; } th:last-child, td:last-child { - padding-right: 24px; + padding-right: 16px; } :host([show-vertical-lines]) th:last-child, :host([show-vertical-lines]) td:last-child { border-right: none; } + + /* Default bottom border on last row removed — tile frame handles it */ + :host(:not([show-horizontal-lines])) tbody tr:last-child { + border-bottom: none; + } .innerCellContainer { position: relative; @@ -389,12 +395,12 @@ export const tableStyles: CSSResult[] = [ display: flex; align-items: center; justify-content: space-between; - height: 52px; - padding: 0 24px; - font-size: 14px; - color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')}; - background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(0 0% 9%)')}; - border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + height: 32px; + padding: 0 16px; + font-size: 11px; + color: ${cssManager.bdTheme('hsl(0 0% 45%)', 'hsl(0 0% 55%)')}; + width: 100%; + box-sizing: border-box; } .tableStatistics { @@ -409,9 +415,10 @@ export const tableStyles: CSSResult[] = [ .footerActions .footerAction { display: flex; align-items: center; - gap: 6px; - padding: 6px 12px; + gap: 4px; + padding: 2px 8px; font-weight: 500; + font-size: 11px; color: ${cssManager.bdTheme('hsl(215.4 16.3% 46.9%)', 'hsl(215 20.2% 65.1%)')}; border-radius: 6px; cursor: pointer; diff --git a/ts_web/elements/00group-layout/dees-tile/dees-tile.demo.ts b/ts_web/elements/00group-layout/dees-tile/dees-tile.demo.ts new file mode 100644 index 0000000..1df15d5 --- /dev/null +++ b/ts_web/elements/00group-layout/dees-tile/dees-tile.demo.ts @@ -0,0 +1,67 @@ +import { html, css, cssManager } from '@design.estate/dees-element'; +import './dees-tile.js'; + +export const demoFunc = () => { + return html` + + +
+ +
Content area with rounded corners
+
+ + +
Content goes here
+ +
+ + +
+ Custom Header + +
+
Custom header slot with search input
+
+
+
+ `; +}; diff --git a/ts_web/elements/00group-layout/dees-tile/dees-tile.ts b/ts_web/elements/00group-layout/dees-tile/dees-tile.ts new file mode 100644 index 0000000..eb75252 --- /dev/null +++ b/ts_web/elements/00group-layout/dees-tile/dees-tile.ts @@ -0,0 +1,139 @@ +import { + customElement, + DeesElement, + html, + css, + cssManager, + property, + state, + type TemplateResult, +} from '@design.estate/dees-element'; +import { demoFunc } from './dees-tile.demo.js'; +import { cssGeistFontFamily } from '../../00fonts.js'; +import { themeDefaultStyles } from '../../00theme.js'; + +declare global { + interface HTMLElementTagNameMap { + 'dees-tile': DeesTile; + } +} + +/** + * dees-tile — the unified "rounded on rounded" tile frame. + * + * RESPONSIBILITIES (what this component owns): + * 1. Outer card — border, border-radius, background, overflow clipping + * 2. Flex column layout — stacking header / content / footer vertically + * 3. Content inset — the rounded inner area with border-top and border-bottom + * 4. Default heading — styled 32px heading text when no slot="header" is provided + * 5. Footer visibility — auto-hides footer area when slot="footer" is empty + * + * NOT RESPONSIBILITIES (what consumer components own): + * - Header/footer height, padding, font-size, colors when using custom slots + * - Content layout (absolute, flex, grid — consumer decides) + * - Any semantic meaning of header/footer content + * + * The header and footer slots are BARE containers (just flex-shrink: 0). + * The slotted content fully controls its own appearance. + * Only the default heading fallback (.tile-heading) carries tile-level styling. + */ +@customElement('dees-tile') +export class DeesTile extends DeesElement { + public static demo = demoFunc; + public static demoGroups = ['Layout']; + + @property({ type: String }) + accessor heading: string = ''; + + @state() + accessor hasFooter: boolean = false; + + public static styles = [ + themeDefaultStyles, + cssManager.defaultStyles, + css` + :host { + display: flex; + flex-direction: column; + font-family: ${cssGeistFontFamily}; + color: ${cssManager.bdTheme('hsl(0 0% 3.9%)', 'hsl(0 0% 98%)')}; + } + + /* --- The frame --- */ + .tile-outer { + position: relative; + flex: 1; + min-height: 0; + background: ${cssManager.bdTheme('hsl(0 0% 100%)', 'hsl(0 0% 3.9%)')}; + border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + border-radius: 8px; + overflow: hidden; + display: flex; + flex-direction: column; + } + + /* --- Header: bare container, only the default heading gets styled --- */ + .tile-header { + flex-shrink: 0; + } + + .tile-heading { + height: 32px; + line-height: 32px; + padding: 0 16px; + font-size: 14px; + font-weight: 500; + letter-spacing: -0.01em; + color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 63.9%)')}; + } + + /* --- Content: the rounded inset --- */ + .tile-content { + flex: 1; + position: relative; + border-radius: 8px; + border-top: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')}; + overflow: hidden; + } + + .tile-content.no-footer { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + + /* --- Footer: bare container, consumer styles the slotted content --- */ + .tile-footer { + flex-shrink: 0; + } + + .tile-footer.hidden { + display: none; + } + `, + ]; + + public render(): TemplateResult { + return html` +
+
+ +
${this.heading}
+
+
+
+ +
+ +
+ `; + } + + private onFooterSlotChange(e: Event) { + const slot = e.target as HTMLSlotElement; + this.hasFooter = slot.assignedNodes({ flatten: true }).length > 0; + } +} diff --git a/ts_web/elements/00group-layout/dees-tile/index.ts b/ts_web/elements/00group-layout/dees-tile/index.ts new file mode 100644 index 0000000..4543af1 --- /dev/null +++ b/ts_web/elements/00group-layout/dees-tile/index.ts @@ -0,0 +1 @@ +export * from './dees-tile.js'; diff --git a/ts_web/elements/00group-layout/index.ts b/ts_web/elements/00group-layout/index.ts index cbe5463..a61ad17 100644 --- a/ts_web/elements/00group-layout/index.ts +++ b/ts_web/elements/00group-layout/index.ts @@ -6,3 +6,4 @@ export * from './dees-label/index.js'; export * from './dees-pagination/index.js'; export * from './dees-panel/index.js'; export * from './dees-stepper/index.js'; +export * from './dees-tile/index.js';