* Added tooltips to toolbar buttons

* Refactored component styles for easier use with CSS selectors
This commit is contained in:
Lucas Dower 2023-06-26 14:20:33 +01:00
parent 2145c815f9
commit 8fb58815b9
6 changed files with 147 additions and 58 deletions

View File

@ -3,20 +3,21 @@ import * as path from 'path';
import { ASSERT } from '../../util/error_util';
import { UIUtil } from '../../util/ui_util';
import { ConfigComponent } from './config';
import { AppIcons } from '../icons';
export class FileComponent extends ConfigComponent<File, HTMLDivElement> {
private _loadedFilePath: string;
private _loadedFilePath: string | null;
public constructor() {
super();
this._loadedFilePath = '';
this._loadedFilePath = null;
}
protected override _generateInnerHTML() {
return `
<div class="input-file struct-prop" id="${this._getId()}">
<input type="file" accept=".obj,,.glb" style="display: none;" id="${this._getId()}-input">
${this._loadedFilePath}
${this._loadedFilePath ?? 'No file chosen'}
</div>
`;
}
@ -62,8 +63,12 @@ export class FileComponent extends ConfigComponent<File, HTMLDivElement> {
}
protected override _updateStyles() {
const parsedPath = path.parse(this._loadedFilePath);
this._getElement().innerHTML = parsedPath.name + parsedPath.ext;
if (this._loadedFilePath) {
const parsedPath = path.parse(this._loadedFilePath);
this._getElement().innerHTML = parsedPath.name + parsedPath.ext;
} else {
this._getElement().innerHTML = '<i>No file chosen</i>';
}
UIUtil.updateStyles(this._getElement(), {
isHovered: this.hovered,

View File

@ -22,17 +22,20 @@ export class HeaderComponent extends BaseComponent<HTMLDivElement> {
this._githubButton = new ToolbarItemComponent({ id: 'gh', iconSVG: AppIcons.GITHUB })
.onClick(() => {
window.open('https://github.com/LucasDower/ObjToSchematic');
});
})
.setTooltip('Open GitHub repo');
this._bugButton = new ToolbarItemComponent({ id: 'bug', iconSVG: AppIcons.BUG })
.onClick(() => {
window.open('https://github.com/LucasDower/ObjToSchematic/issues');
});
})
.setTooltip('Open GitHub issues');
this._discordButton = new ToolbarItemComponent({ id: 'disc', iconSVG: AppIcons.DISCORD })
.onClick(() => {
window.open('https://discord.gg/McS2VrBZPD');
});
})
.setTooltip('Open Discord server');
}
// Header element shouldn't be

View File

@ -12,6 +12,7 @@ export type TToolbarItemParams = {
export class ToolbarItemComponent extends BaseComponent<HTMLDivElement> {
private _iconSVG: SVGSVGElement;
private _label: string;
private _tooltip: string | null;
private _onClick?: () => void;
private _isActive: boolean;
private _grow: boolean;
@ -33,6 +34,7 @@ export class ToolbarItemComponent extends BaseComponent<HTMLDivElement> {
}
this._label = '';
this._tooltip = null;
}
public setGrow() {
@ -90,6 +92,11 @@ export class ToolbarItemComponent extends BaseComponent<HTMLDivElement> {
return this;
}
public setTooltip(text: string) {
this._tooltip = text;
return this;
}
public generateHTML() {
if (this._grow) {
return `
@ -98,11 +105,20 @@ export class ToolbarItemComponent extends BaseComponent<HTMLDivElement> {
</div>
`;
} else {
return `
<div class="struct-prop container-icon-button" style="aspect-ratio: 1;" id="${this._getId()}">
if (this._tooltip === null) {
return `
<div class="struct-prop container-icon-button " style="aspect-ratio: 1;" id="${this._getId()}">
${this._iconSVG.outerHTML} ${this._label}
</div>
`;
} else {
return `
<div class="struct-prop container-icon-button hover-text" style="aspect-ratio: 1;" id="${this._getId()}">
${this._iconSVG.outerHTML} ${this._label}
<span class="tooltip-text left">${this._tooltip}</span>
</div>
`;
}
}
}

View File

@ -378,7 +378,8 @@ export class UI {
})
.isEnabled(() => {
return Renderer.Get.getModelsAvailable() >= MeshType.TriangleMesh;
}),
})
.setTooltip('View mesh'),
'voxelMesh': new ToolbarItemComponent({ id: 'voxelMesh', iconSVG: AppIcons.VOXEL })
.onClick(() => {
Renderer.Get.setModelToUse(MeshType.VoxelMesh);
@ -388,7 +389,8 @@ export class UI {
})
.isEnabled(() => {
return Renderer.Get.getModelsAvailable() >= MeshType.VoxelMesh;
}),
})
.setTooltip('View voxel mesh'),
'blockMesh': new ToolbarItemComponent({ id: 'blockMesh', iconSVG: AppIcons.BLOCK })
.onClick(() => {
Renderer.Get.setModelToUse(MeshType.BlockMesh);
@ -398,7 +400,8 @@ export class UI {
})
.isEnabled(() => {
return Renderer.Get.getModelsAvailable() >= MeshType.BlockMesh;
}),
})
.setTooltip('View block mesh'),
},
componentOrder: ['mesh', 'voxelMesh', 'blockMesh'],
},
@ -413,14 +416,16 @@ export class UI {
})
.isEnabled(() => {
return Renderer.Get.getActiveMeshType() !== MeshType.None;
}),
})
.setTooltip('Toggle grid'),
'axes': new ToolbarItemComponent({ id: 'axes', iconSVG: AppIcons.AXES })
.onClick(() => {
Renderer.Get.toggleIsAxesEnabled();
})
.isActive(() => {
return Renderer.Get.isAxesEnabled();
}),
})
.setTooltip('Toggle axes'),
'night-vision': new ToolbarItemComponent({ id: 'night', iconSVG: AppIcons.BULB })
.onClick(() => {
Renderer.Get.toggleIsNightVisionEnabled();
@ -430,7 +435,8 @@ export class UI {
})
.isEnabled(() => {
return Renderer.Get.canToggleNightVision();
}),
})
.setTooltip('Toggle night vision'),
},
componentOrder: ['grid', 'axes', 'night-vision'],
},
@ -445,7 +451,8 @@ export class UI {
})
.isActive(() => {
return Renderer.Get.isSliceViewerEnabled();
}),
})
.setTooltip('Toggle slice viewer'),
'plus': new ToolbarItemComponent({ id: 'plus', iconSVG: AppIcons.PLUS })
.onClick(() => {
Renderer.Get.incrementSliceHeight();
@ -453,7 +460,8 @@ export class UI {
.isEnabled(() => {
return Renderer.Get.isSliceViewerEnabled() &&
Renderer.Get.canIncrementSliceHeight();
}),
})
.setTooltip('Decrement slice'),
'minus': new ToolbarItemComponent({ id: 'minus', iconSVG: AppIcons.MINUS })
.onClick(() => {
Renderer.Get.decrementSliceHeight();
@ -461,7 +469,8 @@ export class UI {
.isEnabled(() => {
return Renderer.Get.isSliceViewerEnabled() &&
Renderer.Get.canDecrementSliceHeight();
}),
})
.setTooltip('Increment slice'),
},
componentOrder: ['slice', 'plus', 'minus'],
},
@ -476,15 +485,18 @@ export class UI {
'zoomOut': new ToolbarItemComponent({ id: 'zout', iconSVG: AppIcons.MINUS })
.onClick(() => {
ArcballCamera.Get.onZoomOut();
}),
})
.setTooltip('Zoom out'),
'zoomIn': new ToolbarItemComponent({ id: 'zin', iconSVG: AppIcons.PLUS })
.onClick(() => {
ArcballCamera.Get.onZoomIn();
}),
})
.setTooltip('Zoom in'),
'reset': new ToolbarItemComponent({ id: 'reset', iconSVG: AppIcons.CENTRE })
.onClick(() => {
ArcballCamera.Get.reset();
}),
})
.setTooltip('Reset camera'),
},
componentOrder: ['zoomOut', 'zoomIn', 'reset'],
},
@ -496,14 +508,16 @@ export class UI {
})
.isActive(() => {
return ArcballCamera.Get.isPerspective();
}),
})
.setTooltip('Perspective camera'),
'orthographic': new ToolbarItemComponent({ id: 'orth', iconSVG: AppIcons.ORTHOGRAPHIC })
.onClick(() => {
ArcballCamera.Get.setCameraMode('orthographic');
})
.isActive(() => {
return ArcballCamera.Get.isOrthographic();
}),
})
.setTooltip('Orthographic camera'),
},
componentOrder: ['perspective', 'orthographic'],
},

View File

@ -14,31 +14,24 @@ export namespace UIUtil {
}
export function clearStyles(element: HTMLElement) {
element.classList.remove('style-inactive-disabled');
element.classList.remove('style-inactive-enabled');
element.classList.remove('style-inactive-hover');
element.classList.remove('style-active-disabled');
element.classList.remove('style-active-enabled');
element.classList.remove('style-active-hover');
element.classList.remove('disabled');
element.classList.remove('hover');
element.classList.remove('active');
}
export function updateStyles(element: HTMLElement, style: TStyleParams) {
clearStyles(element);
let styleToApply = `style`;
styleToApply += style.isActive ? '-active' : '-inactive';
if (style.isEnabled) {
if (style.isHovered) {
styleToApply += '-hover';
} else {
styleToApply += '-enabled';
}
} else {
styleToApply += '-disabled';
if (style.isActive) {
element.classList.add('active');
}
element.classList.add(styleToApply);
if (!style.isEnabled) {
element.classList.add('disabled');
}
if (style.isHovered && style.isEnabled) {
element.classList.add('hover');
}
}
}

View File

@ -253,6 +253,9 @@ select {
transition: width 0.2s;
}
.struct-prop {
display: flex;
align-items: center;
@ -264,42 +267,49 @@ select {
border-style: solid;
}
.style-inactive-disabled {
.struct-prop.disabled {
border-color: var(--gray-500);
color: var(--text-dark);
background: var(--gray-400);
cursor: inherit;
}
.style-inactive-enabled {
border-color: var(--gray-600);
color: var(--text-standard);
background: var(--gray-500);
}
.style-inactive-hover {
.struct-prop.hover {
border-color: var(--gray-700);
color: var(--text-light);
background: var(--gray-600);
cursor: pointer;
}
.style-active-disabled {
.struct-prop:not(.disabled):not(.hover) {
border-color: var(--gray-600);
color: var(--text-standard);
background: var(--gray-500);
}
.struct-prop.active.disabled {
border-color: var(--blue-450);
color: var(--text-dim);
background: var(--blue-400);
cursor: inherit;
}
.style-active-enabled {
border-color: var(--blue-600);
color: var(--text-bright);
background: var(--blue-500);
}
.style-active-hover {
.struct-prop.active.hover {
border-color: var(--blue-700);
color: var(--text-bright);
background: var(--blue-600);
cursor: pointer;
}
.struct-prop.active:not(.disabled):not(.hover) {
border-color: var(--blue-600);
color: var(--text-bright);
background: var(--blue-500);
}
.h-div {
height: 0px;
border-radius: 2px;
@ -348,7 +358,7 @@ select {
justify-content: center;
flex-grow: 1;
}
.spinbox-value.style-inactive-hover {
.spinbox-value .inactive .hover {
cursor: e-resize;
}
@ -676,4 +686,52 @@ a {
.hide {
display: none;
}
.tooltip-text {
visibility: hidden;
opacity: 0;
position: absolute;
z-index: 1;
font-size: var(--font-size-small);
color: var(--text-light);
background-color: var(--gray-600);
padding: 5px 10px;
border-radius: 5px;
border: 1px solid var(--gray-700);
transition: 0.15s;
pointer-events: none;
box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 16px;
white-space: nowrap;
}
.hover-text:hover:not(.disabled) .tooltip-text {
visibility: visible;
opacity: 1;
}
.top {
top: -40px;
left: -50%;
}
.bottom {
top: 25px;
left: -50%;
}
.left {
top: 1px;
right: 120%;
}
.right {
top: 2px;
left: 120%;
}
.hover-text {
position: relative;
}