Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions apps/demo-app/src/app/app.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
<div class="demo-container">
<h1>Blender UI Demo</h1>
<p>Current Theme: {{ activeTheme().name }}</p>
<button (click)="toggleTheme()">Toggle Theme</button>
<bui-modifier-panel></bui-modifier-panel>

<div class="panel">This is a panel.</div>

<router-outlet></router-outlet>
</div>
<button class="theme-toggle" (click)="toggleTheme()">
{{ activeTheme().name === 'dark' ? '☀️' : '🌙' }}
</button>
36 changes: 19 additions & 17 deletions apps/demo-app/src/app/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,31 @@
display: block;
width: 100vw;
height: 100vh;
background-color: bui.$bui-bg-dark;
color: bui.$bui-text-color;
padding: 20px;
margin: 0;
padding: 0;
overflow: hidden;
font-family: sans-serif;
}

.panel {
background-color: bui.$bui-panel-bg;
padding: 20px;
border-radius: bui.$bui-border-radius;
margin-top: 20px;
.theme-toggle {
position: fixed;
bottom: 20px;
right: 20px;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: bui.$bui-bg-light;
color: bui.$bui-text-color;
border: 1px solid bui.$bui-border-dark;
}

button {
padding: 10px 20px;
background-color: bui.$bui-primary;
color: white;
border: none;
border-radius: bui.$bui-border-radius;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 1000;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
padding: 0;

&:hover {
opacity: 0.9;
background-color: bui.$bui-panel-bg;
}
}
18 changes: 11 additions & 7 deletions apps/demo-app/src/app/app.spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { TestBed } from '@angular/core/testing';
import { App } from './app';
import { NxWelcome } from './nx-welcome';

describe('App', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [App, NxWelcome],
imports: [App],
}).compileComponents();
});

it('should render title', async () => {
it('should render modifier panel', async () => {
const fixture = TestBed.createComponent(App);
await fixture.whenStable();
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain(
'Welcome demo-app',
);
expect(compiled.querySelector('bui-modifier-panel')).toBeTruthy();
});

it('should render theme toggle', async () => {
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.theme-toggle')).toBeTruthy();
});
});
4 changes: 2 additions & 2 deletions apps/demo-app/src/app/app.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Component, inject, computed } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BuiThemeService } from '@blender-ui/core';
import { BuiThemeService, ModifierPanelComponent } from '@blender-ui/core';

@Component({
imports: [RouterModule],
imports: [RouterModule, ModifierPanelComponent],
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.scss',
Expand Down
28 changes: 24 additions & 4 deletions demo-app.log
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@

> nx run demo-app:serve:development --port=4200
> nx run demo-app:serve:development

❯ Building...
✔ Building...
Initial chunk files | Names  |  Raw size
main.js  | main  |  7.80 kB | 
main.js  | main  |  73.04 kB | 
styles.css  | styles  | 110 bytes | 
   | Initial total |  7.91 kB
Application bundle generation complete. [3.360 seconds] - 2026-02-22T22:28:04.671Z
   | Initial total |  73.15 kB
Application bundle generation complete. [4.012 seconds] - 2026-02-22T23:47:13.515Z

Watch mode enabled. Watching for file changes...
NOTE: Raw file sizes do not reflect development server per-request transformations.
➜ Local: http://localhost:4200/
❯ Changes detected. Rebuilding...
✔ Changes detected. Rebuilding...
Initial chunk files | Names | Raw size
main.js  | main  | 73.11 kB | 
Application bundle generation complete. [0.764 seconds] - 2026-02-22T23:50:56.104Z

Page reload sent to client(s).
❯ Changes detected. Rebuilding...
✔ Changes detected. Rebuilding...

No output file changes.
Application bundle generation complete. [2.666 seconds] - 2026-02-22T23:51:14.860Z

❯ Changes detected. Rebuilding...
✔ Changes detected. Rebuilding...
Initial chunk files | Names | Raw size
main.js  | main  | 73.11 kB | 
Application bundle generation complete. [0.797 seconds] - 2026-02-22T23:51:31.420Z

Component update sent to client(s).
5 changes: 5 additions & 0 deletions libs/blender-ui/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
export * from './lib/core/core';
export * from './lib/services/theme.service';
export * from './lib/services/themes';
export * from './lib/components/checkbox/checkbox.component';
export * from './lib/components/number-input/number-input.component';
export * from './lib/components/select/select.component';
export * from './lib/components/section/section.component';
export * from './lib/components/modifier-panel/modifier-panel.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div class="bui-checkbox-container" [class.disabled]="disabled()" (click)="toggle()">
<div class="bui-checkbox" [class.checked]="checked()"></div>
<span class="label" *ngIf="label()">{{ label() }}</span>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
@use '../../styles/variables' as *;

:host {
display: inline-block;
}

.bui-checkbox-container {
display: flex;
align-items: center;
cursor: pointer;
user-select: none;

&.disabled {
cursor: not-allowed;
opacity: 0.5;
}
}

.bui-checkbox {
appearance: none;
background-color: transparent;
margin: 0;
width: 1rem;
height: 1rem;
border: 1px solid $bui-text-color; // Fallback or distinct color
border-radius: 2px;
display: grid;
place-content: center;
margin-right: 0.5rem;
transition: all 0.2s ease;

// The checkmark
&::before {
content: "";
width: 0.65em;
height: 0.65em;
transform: scale(0);
transition: 120ms transform ease-in-out;
box-shadow: inset 1em 1em white;
transform-origin: center;
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}

&.checked {
background-color: $bui-primary;
border-color: $bui-primary;

&::before {
transform: scale(1);
}
}
}

.label {
font-family: var(--bui-font-display, "Inter", "system-ui", "sans-serif");
font-size: 0.75rem; // 12px
color: $bui-text-color;
}
21 changes: 21 additions & 0 deletions libs/blender-ui/src/lib/components/checkbox/checkbox.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Component, input, output } from '@angular/core';
import { CommonModule } from '@angular/common';

@Component({
selector: 'bui-checkbox',
standalone: true,
imports: [CommonModule],
templateUrl: './checkbox.component.html',
styleUrl: './checkbox.component.scss',
})
export class BuiCheckboxComponent {
checked = input(false);
label = input('');
disabled = input(false);
checkedChange = output<boolean>();

toggle() {
if (this.disabled()) return;
this.checkedChange.emit(!this.checked());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<div class="bui-layout">
<!-- Sidebar -->
<div class="bui-sidebar">
<div class="icon-button">🔧</div>
<div class="separator"></div>
<div class="icon-button">📷</div>
<div class="icon-button">🖨️</div>
<div class="icon-button">📑</div>
<div class="icon-button">☀</div>
<div class="icon-button">🌐</div>
<div class="separator"></div>
<div class="icon-button active">🔧</div>
<div class="icon-button">📐</div>
<div class="icon-button">🧪</div>
<div class="icon-button">🔗</div>
<div class="icon-button">🔺</div>
<div class="icon-button">🔴</div>
<div class="icon-button">🏁</div>
</div>

<!-- Main Panel -->
<div class="bui-main">
<!-- Breadcrumb / Header -->
<div class="bui-panel-top">
<div class="breadcrumb-item">
<span class="icon">⬜</span> Cube <span class="arrow">▼</span>
</div>
<span class="separator">›</span>
<div class="breadcrumb-item">
<span class="icon">🔧</span> Modifiers
</div>
<div class="spacer"></div>
<div class="icon-button">📌</div>
</div>

<!-- Content -->
<div class="bui-panel-content">
<button class="add-modifier-btn">
<span class="icon">+</span> Add Modifier
</button>

<!-- Modifier Card -->
<div class="bui-modifier-card">
<!-- Modifier Header -->
<div class="bui-modifier-header">
<span class="expand-icon">▼</span>
<span class="modifier-icon">🌀</span>
<span class="modifier-title">Subdivision</span>

<div class="modifier-toggles">
<button class="toggle-btn" title="On Cage">📐</button>
<button class="toggle-btn active" title="Edit Mode">✏️</button>
<button class="toggle-btn active" title="Realtime">🖥️</button>
<button class="toggle-btn active" title="Render">📷</button>
</div>

<div class="separator-vertical"></div>
<button class="close-btn">✕</button>
<button class="drag-btn">⋮⋮</button>
</div>

<!-- Modifier Body -->
<div class="bui-modifier-body">
<!-- Type Toggle -->
<div class="type-toggle-row">
<div class="toggle-group">
<button class="toggle-option active">Catmull-Clark</button>
<button class="toggle-option">Simple</button>
</div>
</div>

<!-- Levels -->
<div class="control-row">
<label>Levels Viewport</label>
<bui-number-input [value]="levelsViewport()" (valueChange)="levelsViewport.set($event)" [min]="0" [max]="6"></bui-number-input>
<div class="status-dot"></div>
</div>
<div class="control-row">
<label>Render</label>
<bui-number-input [value]="levelsRender()" (valueChange)="levelsRender.set($event)" [min]="0" [max]="6"></bui-number-input>
<div class="status-dot"></div>
</div>

<!-- Optimal Display -->
<div class="control-row checkbox-row">
<div class="spacer-label"></div>
<bui-checkbox [checked]="optimalDisplay()" (checkedChange)="optimalDisplay.set($event)" label="Optimal Display"></bui-checkbox>
<div class="status-dot hidden"></div>
</div>

<!-- Advanced Section -->
<bui-section label="Advanced">
<div class="control-row checkbox-row">
<div class="spacer-label"></div>
<bui-checkbox [checked]="useLimitSurface()" (checkedChange)="useLimitSurface.set($event)" label="Use Limit Surface"></bui-checkbox>
<div class="status-dot"></div>
</div>

<div class="control-row">
<label>Quality</label>
<bui-number-input [value]="quality()" (valueChange)="quality.set($event)" [min]="1" [max]="10"></bui-number-input>
<div class="status-dot"></div>
</div>

<div class="control-row">
<label>UV Smooth</label>
<bui-select [options]="uvSmoothOptions" [value]="uvSmooth()" (valueChange)="uvSmooth.set($event)"></bui-select>
<div class="status-dot"></div>
</div>

<div class="control-row">
<label>Boundary Smooth</label>
<bui-select [options]="boundarySmoothOptions" [value]="boundarySmooth()" (valueChange)="boundarySmooth.set($event)"></bui-select>
<div class="status-dot"></div>
</div>

<div class="control-row checkbox-row">
<div class="spacer-label"></div>
<bui-checkbox [checked]="useCreases()" (checkedChange)="useCreases.set($event)" label="Use Creases"></bui-checkbox>
<div class="status-dot"></div>
</div>

<div class="control-row checkbox-row">
<div class="spacer-label"></div>
<bui-checkbox [checked]="useCustomNormals()" (checkedChange)="useCustomNormals.set($event)" label="Use Custom Normals" [disabled]="true"></bui-checkbox>
<div class="status-dot"></div>
</div>
</bui-section>

</div>
</div>

</div>

<!-- Footer -->
<div class="bui-footer">
<div class="footer-left">
<span>🖱️ Select</span>
<span>🔄 Rotate View</span>
</div>
<div class="footer-right">
<span>5.2.0 Alpha</span>
<span>💾</span>
</div>
</div>

</div>
</div>
Loading
Loading