feat(components): add large set of new UI components and demos, reorganize groups, and bump a few dependencies
This commit is contained in:
231
ts_web/elements/00group-media/dees-tile-note/component.ts
Normal file
231
ts_web/elements/00group-media/dees-tile-note/component.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
import {
|
||||
property,
|
||||
state,
|
||||
html,
|
||||
customElement,
|
||||
css,
|
||||
cssManager,
|
||||
type TemplateResult,
|
||||
type CSSResult,
|
||||
} from '@design.estate/dees-element';
|
||||
import { DeesTileBase } from '../dees-tile-shared/DeesTileBase.js';
|
||||
import { tileBaseStyles } from '../dees-tile-shared/styles.js';
|
||||
import { demo } from './demo.js';
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'dees-tile-note': DeesTileNote;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('dees-tile-note')
|
||||
export class DeesTileNote extends DeesTileBase {
|
||||
public static demo = demo;
|
||||
public static demoGroups = ['Media'];
|
||||
public static styles = [
|
||||
...tileBaseStyles,
|
||||
css`
|
||||
.note-content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: ${cssManager.bdTheme('#ffffff', 'hsl(60 5% 96%)')};
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.note-header {
|
||||
padding: 12px 14px 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.note-title {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: ${cssManager.bdTheme('hsl(215 20% 20%)', 'hsl(215 20% 20%)')};
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.note-body {
|
||||
flex: 1;
|
||||
padding: 0 14px 14px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.note-text {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 10px;
|
||||
line-height: 1.5;
|
||||
color: ${cssManager.bdTheme('hsl(215 10% 40%)', 'hsl(215 10% 35%)')};
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.note-fade {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
background: linear-gradient(
|
||||
transparent,
|
||||
${cssManager.bdTheme('#ffffff', 'hsl(60 5% 96%)')}
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.note-language {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
padding: 2px 6px;
|
||||
background: ${cssManager.bdTheme('hsl(215 20% 92%)', 'hsl(215 20% 88%)')};
|
||||
color: ${cssManager.bdTheme('hsl(215 16% 50%)', 'hsl(215 16% 40%)')};
|
||||
border-radius: 3px;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.note-lines {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 34px;
|
||||
border-right: 1px solid ${cssManager.bdTheme('hsl(0 70% 85%)', 'hsl(0 50% 80%)')};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.line-number {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 9px;
|
||||
line-height: 15px; /* matches 10px * 1.5 line-height */
|
||||
color: ${cssManager.bdTheme('hsl(215 10% 75%)', 'hsl(215 10% 70%)')};
|
||||
text-align: right;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.note-line-indicator {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
padding: 3px 8px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
backdrop-filter: blur(8px);
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
`,
|
||||
] as any;
|
||||
|
||||
@property({ type: String })
|
||||
accessor title: string = '';
|
||||
|
||||
@property({ type: String })
|
||||
accessor content: string = '';
|
||||
|
||||
@property({ type: String })
|
||||
accessor language: string = '';
|
||||
|
||||
@state()
|
||||
accessor isHovering: boolean = false;
|
||||
|
||||
private noteBodyElement: HTMLElement | null = null;
|
||||
|
||||
protected renderTileContent(): TemplateResult {
|
||||
const lines = this.content.split('\n');
|
||||
|
||||
return html`
|
||||
<div class="note-content">
|
||||
${this.language ? html`
|
||||
<div class="note-language">${this.language}</div>
|
||||
` : ''}
|
||||
|
||||
${this.title ? html`
|
||||
<div class="note-header">
|
||||
<div class="note-title">${this.title}</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<div class="note-body">
|
||||
<pre class="note-text">${lines.join('\n')}</pre>
|
||||
${!this.isHovering ? html`<div class="note-fade"></div>` : ''}
|
||||
</div>
|
||||
|
||||
${this.isHovering && lines.length > 12 ? html`
|
||||
<div class="note-line-indicator">
|
||||
Line ${this.getVisibleLineRange(lines.length)}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
|
||||
${this.clickable ? html`
|
||||
<div class="tile-overlay">
|
||||
<dees-icon icon="lucide:FileText"></dees-icon>
|
||||
<span>Open Note</span>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
protected getTileClickDetail(): Record<string, unknown> {
|
||||
return {
|
||||
title: this.title,
|
||||
content: this.content,
|
||||
language: this.language,
|
||||
};
|
||||
}
|
||||
|
||||
protected onTileMouseEnter(): void {
|
||||
this.isHovering = true;
|
||||
if (!this.noteBodyElement) {
|
||||
this.noteBodyElement = this.shadowRoot?.querySelector('.note-body') as HTMLElement;
|
||||
}
|
||||
}
|
||||
|
||||
protected onTileMouseLeave(): void {
|
||||
this.isHovering = false;
|
||||
if (this.noteBodyElement) {
|
||||
this.noteBodyElement.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected onTileMouseMove(e: MouseEvent): void {
|
||||
if (!this.isHovering || !this.noteBodyElement) return;
|
||||
|
||||
const totalLines = this.content.split('\n').length;
|
||||
if (totalLines <= 12) return;
|
||||
|
||||
const rect = this.getBoundingClientRect();
|
||||
const y = e.clientY - rect.top;
|
||||
const percentage = Math.max(0, Math.min(1, y / rect.height));
|
||||
|
||||
const maxScroll = this.noteBodyElement.scrollHeight - this.noteBodyElement.clientHeight;
|
||||
this.noteBodyElement.scrollTop = percentage * maxScroll;
|
||||
}
|
||||
|
||||
private getVisibleLineRange(totalLines: number): string {
|
||||
if (!this.noteBodyElement) return `1–12 of ${totalLines}`;
|
||||
const lineHeight = 15; // 10px font × 1.5 line-height
|
||||
const firstLine = Math.floor(this.noteBodyElement.scrollTop / lineHeight) + 1;
|
||||
const visibleCount = Math.floor(this.noteBodyElement.clientHeight / lineHeight);
|
||||
const lastLine = Math.min(firstLine + visibleCount - 1, totalLines);
|
||||
return `${firstLine}–${lastLine} of ${totalLines}`;
|
||||
}
|
||||
}
|
||||
135
ts_web/elements/00group-media/dees-tile-note/demo.ts
Normal file
135
ts_web/elements/00group-media/dees-tile-note/demo.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { html } from '@design.estate/dees-element';
|
||||
|
||||
export const demo = () => {
|
||||
const sampleCode = `import { html } from 'lit';
|
||||
|
||||
export class MyComponent {
|
||||
private items: string[] = [];
|
||||
|
||||
render() {
|
||||
return html\`
|
||||
<div class="container">
|
||||
\${this.items.map(item => html\`
|
||||
<span>\${item}</span>
|
||||
\`)}
|
||||
</div>
|
||||
\`;
|
||||
}
|
||||
}`;
|
||||
|
||||
const sampleText = `Meeting Notes - Q4 Planning
|
||||
Date: January 15, 2026
|
||||
Attendees: Alice, Bob, Charlie
|
||||
|
||||
Key Decisions:
|
||||
1. Launch new feature by March
|
||||
2. Hire 2 more engineers
|
||||
3. Migrate to new CI/CD pipeline
|
||||
4. Update design system to v3
|
||||
|
||||
Action Items:
|
||||
- Alice: Draft PRD by next week
|
||||
- Bob: Set up interview pipeline
|
||||
- Charlie: Evaluate Jenkins vs GitHub Actions`;
|
||||
|
||||
const sampleJson = `{
|
||||
"name": "@design.estate/dees-catalog",
|
||||
"version": "3.38.0",
|
||||
"description": "Design component catalog",
|
||||
"dependencies": {
|
||||
"@design.estate/dees-element": "^2.0.0",
|
||||
"lit": "^3.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsbuild",
|
||||
"test": "tstest"
|
||||
}
|
||||
}`;
|
||||
|
||||
return html`
|
||||
<style>
|
||||
.demo-container {
|
||||
padding: 40px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.demo-section {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tile-row {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="demo-container">
|
||||
<div class="demo-section">
|
||||
<h3>Note Tiles</h3>
|
||||
<div class="tile-row">
|
||||
<dees-tile-note
|
||||
title="component.ts"
|
||||
.content=${sampleCode}
|
||||
language="typescript"
|
||||
label="component.ts"
|
||||
@tile-click=${(e: CustomEvent) => console.log('Note clicked:', e.detail)}
|
||||
></dees-tile-note>
|
||||
|
||||
<dees-tile-note
|
||||
title="Meeting Notes"
|
||||
.content=${sampleText}
|
||||
label="meeting-notes.txt"
|
||||
></dees-tile-note>
|
||||
|
||||
<dees-tile-note
|
||||
title="package.json"
|
||||
.content=${sampleJson}
|
||||
language="json"
|
||||
label="package.json"
|
||||
></dees-tile-note>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Size Variants</h3>
|
||||
<div class="tile-row">
|
||||
<dees-tile-note
|
||||
size="small"
|
||||
title="small.ts"
|
||||
.content=${sampleCode}
|
||||
language="ts"
|
||||
label="small.ts"
|
||||
></dees-tile-note>
|
||||
|
||||
<dees-tile-note
|
||||
title="default.ts"
|
||||
.content=${sampleCode}
|
||||
language="ts"
|
||||
label="default.ts"
|
||||
></dees-tile-note>
|
||||
|
||||
<dees-tile-note
|
||||
size="large"
|
||||
title="large.ts"
|
||||
.content=${sampleCode}
|
||||
language="ts"
|
||||
label="large.ts"
|
||||
></dees-tile-note>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Without Title</h3>
|
||||
<dees-tile-note
|
||||
.content=${sampleText}
|
||||
label="untitled.txt"
|
||||
></dees-tile-note>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
1
ts_web/elements/00group-media/dees-tile-note/index.ts
Normal file
1
ts_web/elements/00group-media/dees-tile-note/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './component.js';
|
||||
Reference in New Issue
Block a user