start ui refactor

This commit is contained in:
Stewart Allen 2024-07-28 22:39:04 -04:00
commit 1e31d6ef0d
4 changed files with 238 additions and 404 deletions

View file

@ -1352,6 +1352,10 @@ const api = exports({
window.open("https://www.paypal.com/paypalme/gridspace3d");
},
gridspace() {
window.open("https://grid.space");
},
kirimoto() {
window.location = "/kiri";
},

View file

@ -341,34 +341,158 @@ api.settings = function() {
// create html elements
function ui_build() {
const trash = FontAwesome.icon({ prefix: "fas", iconName: "trash" }).html[0];
const eye_open = FontAwesome.icon({ prefix: "fas", iconName: "eye" }).html[0];
const eye_closed = FontAwesome.icon({ prefix: "fas", iconName: "eye-slash" }).html[0];
let { file, selection, mode, tool, sketch, prefs, add } = api;
let trash = FontAwesome.icon({ prefix: "fas", iconName: "trash" }).html[0];
let eye_open = FontAwesome.icon({ prefix: "fas", iconName: "eye" }).html[0];
let eye_closed = FontAwesome.icon({ prefix: "fas", iconName: "eye-slash" }).html[0];
// app name and drop menu
h.bind($('app-info'), [
h.div({ id: "app-name", _: "Mesh:Tool" }),
h.div({ id: "app-menu" }, [
h.div({ _: "help", onclick: api.help }),
h.hr(),
// h.div({ _: "version", onclick: api.version }),
h.div({ _: "donate", onclick: api.donate }),
h.div({ _: "slicer", onclick: api.kirimoto }),
h.hr(),
h.div({ id: "app-vers", _: mesh.version }),
])
// top left drop menus
h.bind($('top-left'), [
h.div({ class: "menu" }, [
h.div('File'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Import', onclick: file.import }, [
h.input({
id: "import", type: "file", class: ["hide"], multiple: true,
onchange(evt) { broker.send.load_files(evt.target.files) }
})
]),
h.div({ _: 'Export', onclick: file.export }),
h.hr(),
h.div({ _: "Slicer", onclick: api.kirimoto }),
h.hr(),
h.div({ _: 'Close', onclick: window.close }),
])
]),
h.div({ class: "menu sketch-on" }, [
h.div('Edit'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Add Circle', onclick() { sketch.selected.one?.add.circle() } }),
h.div({ _: 'Add Rectangle', onclick() { sketch.selected.one?.add.rectangle() } }),
h.hr(),
h.div({ _: 'Add Sketch', onclick: add.sketch }),
h.hr(),
h.div({ _: 'Delete', onclick: api.selection.delete }),
])
]),
h.div({ class: "menu sketch-off" }, [
h.div('Edit'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Add Cylinder', onclick() { add.cylinder() } }),
h.div({ _: 'Add Cube', onclick() { add.cube() } }),
h.div({ _: 'Add Gear', onclick() { add.gear() } }),
h.hr(),
h.div({ _: 'Add Sketch', onclick: add.sketch }),
devel ? h.div({ _: 'Add Vertices', onclick: add.input }) : undefined,
h.hr(),
h.div({ _: 'Delete', onclick: api.selection.delete }),
])
]),
h.div({ class: "menu" }, [
h.div('View'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Bounds', onclick() { selection.boundsBox({ toggle: true }) } }),
h.div({ _: 'Normals', onclick() { api.normals() } }),
h.div({ _: 'Wireframes', onclick() { api.wireframe() } }),
h.hr(),
h.div({ _: 'Gridlines', onclick() { api.grid() } }),
h.div({ _: 'Messages', onclick() { api.log.toggle({ spinner: false }) } }),
])
]),
h.div({ class: "menu" }, [
h.div('Mode'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Sketch', id: "mode-sketch", onclick: mode.sketch }),
h.hr(),
h.div({ _: 'Object', id: "mode-object", onclick: mode.object }),
h.div({ _: 'Tool', id: "mode-tool", onclick: mode.tool }),
h.hr(),
h.div({ _: 'Surface', id: "mode-surface", onclick: mode.surface }),
h.div({ _: 'Face', id: "mode-face", onclick: mode.face }),
h.div({ _: 'Edge', id: "mode-edge", onclick: mode.edge }),
])
]),
h.div({ class: "menu sketch-on" }, [
h.div('Object'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Group', onclick: sketch.arrange.group }),
h.div({ _: 'Ungroup', onclick: sketch.arrange.ungroup }),
h.hr(),
h.div({ _: 'Center', onclick: sketch.arrange.center }),
h.hr(),
h.div({ _: 'Flip Horizontal', onclick: sketch.arrange.fliph }),
h.div({ _: 'Flip Vertical', onclick: sketch.arrange.flipv }),
h.hr(),
h.div({ _: 'Raise', onclick: sketch.arrange.up }),
h.div({ _: 'Lower', onclick: sketch.arrange.down }),
h.div({ _: 'Raise to Top', onclick: sketch.arrange.top }),
h.div({ _: 'Lower to Bottom', onclick: sketch.arrange.bottom }),
])
]),
h.div({ class: "menu sketch-off" }, [
h.div('Object'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Regroup', onclick: tool.regroup }),
h.div({ _: 'Isolate', onclick: tool.isolate }),
h.hr(),
h.div({ _: 'Duplicate', onclick: tool.duplicate }),
h.div({ _: 'Mirror', onclick: tool.mirror }),
h.div({ _: 'Merge', onclick: tool.merge }),
h.div({ _: 'Split', onclick: call.edit_split }),
h.hr(),
h.div({ _: 'Union', onclick: tool.union }),
h.div({ _: 'Subtract', onclick: tool.subtract }),
h.div({ _: 'Intersect', onclick: tool.intersect }),
h.div({ _: 'Difference', onclick: tool.difference }),
h.hr(),
h.div({ _: 'Copy Circle', onclick: api.pattern.circle }),
h.div({ _: 'Copy Grid', onclick: api.pattern.grid }),
])
]),
h.div({ class: "menu sketch-off" }, [
h.div('Faces'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Flip Normals', onclick: tool.invert }),
h.div({ _: 'Analyze', onclick: tool.analyze }),
// h.div({ _: 'Patch', onclick: tool.repair }),
// h.div({ _: 'rebuild', onclick: tool.rebuild }),
h.div({ _: 'Clean', onclick: tool.clean }),
])
]),
h.div({ class: "menu sketch-on" }, [
h.div('Shape'),
h.div({ class: "menu-items" }, [
h.div({ _: 'Duplicate', onclick: api.tool.duplicate }),
h.div({ _: 'Extrude', onclick: sketch.extrude }),
h.hr(),
h.div({ _: 'Union', onclick: sketch.boolean.union }),
h.div({ _: 'Intersect', onclick: sketch.boolean.intersect }),
h.div({ _: 'Difference', onclick: sketch.boolean.difference }),
h.hr(),
h.div({ _: 'Nest', onclick: sketch.boolean.nest }),
h.div({ _: 'Flatten', onclick: sketch.boolean.flatten }),
h.div({ _: 'Even Odd', onclick: sketch.boolean.evenodd }),
])
]),
h.div({ class: "menu" }, [
h.div('Help'),
h.div({ class: "menu-items" }, [
h.div({ _: 'About', onclick() { api.welcome(mesh.version) } }),
h.hr(),
h.div({ _: 'Documentation', onclick: api.help }),
h.div({ _: 'Official Site', onclick: api.gridspace }),
h.div({ _: "Donate", onclick: api.donate }),
h.hr(),
h.div({ _: 'Versions', onclick: api.version }),
])
]),
]);
// add a help button
// add help buttons
h.bind($('top-right'), [
h.div({ id: "top-version", onclick: api.version }, [
h.div({ class: "fas fa-hashtag" }),
]),
h.div({ id: "top-settings", onclick: api.settings }, [
h.div({ class: "fas fa-sliders-h" }),
]),
h.div({ id: "top-help", onclick: api.help }, [
h.div({ class: "far fa-question-circle" }),
h.div({ class: "fas fa-gear" }),
h.div('Settings')
]),
]);
@ -390,185 +514,17 @@ function ui_build() {
h.div({ id: 'spinner', class: 'spin' })
]),
// display and action areas
h.div({ id: 'modes' }),
h.div({ id: 'actions' }),
h.div({ id: 'grouplist'}),
h.div({ id: 'selectlist'}),
h.div({ id: 'logger', onmouseover() { log.show() } }),
h.div({ id: 'download', class: "hide" })
]);
let { modes, actions, grouplist, selectlist, logger } = bound;
let { actions, grouplist, selectlist, logger } = bound;
// create slid in/out logging window
h.bind(logger, [ h.div({ id: 'logtext' }) ]);
// a few shortcuts to api calls
let { file, selection, mode, tool, sketch, prefs, add } = api;
// top/center mode selector
h.bind(modes, [ h.div([
h.button({ _: 'object', id: "mode-object", onclick() { mode.object() } }),
h.button({ _: 'tool', id: "mode-tool", onclick() { mode.tool() } }),
]), h.div([
h.button({ _: 'surface', id: "mode-surface", onclick() { mode.surface() } }),
h.button({ _: 'face', id: "mode-face", onclick() { mode.face() } }),
h.button({ _: 'edge', id: "mode-edge", onclick() { mode.edge() } }),
]), h.div([
h.button({ _: 'sketch', id: "mode-sketch", onclick() { mode.sketch() } }),
]) ]);
// create hotkey/action menu (top/left)
h.bind(actions, [
h.div([
// create and bind file loading elements
h.div({ _: "file", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'import', onclick: file.import }, [
h.input({
id: "import", type: "file", class: ["hide"], multiple: true,
onchange(evt) { broker.send.load_files(evt.target.files) }
})
]),
h.button({ _: 'export', onclick: file.export }),
])
]),
h.div({ class: "sketch-off" }, [
h.div({ _: "add", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'cylinder', onclick() { add.cylinder() } }),
h.button({ _: 'cube', onclick() { add.cube() } }),
h.button({ _: 'gear', onclick() { add.gear() } }),
h.hr(),
h.button({ _: 'sketch', onclick() { add.sketch() } }),
devel ? h.button({ _: 'input', onclick: add.input }) : undefined
])
]),
h.div({ class: "sketch-on" }, [
h.div({ _: "add", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'circle', onclick() { add.circle() } }),
h.button({ _: 'rectangle', onclick() { add.rectangle() } }),
// h.button({ _: 'gear', onclick() { add.gear() } }),
h.hr(),
h.button({ _: 'sketch', onclick() { add.sketch() } }),
])
]),
h.div([
h.div({ _: "view", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'bounds', onclick() { selection.boundsBox({ toggle: true }) } }),
h.button({ _: 'normals', onclick() { api.normals() } }),
h.button({ _: 'gridlines', onclick() { api.grid() } }),
h.button({ _: 'wireframe', onclick() { api.wireframe() } }),
])
]),
h.div({ class: "sketch-off" }, [
h.div({ _: "boolean", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'union', onclick: tool.union }),
h.button({ _: 'subtract', onclick: tool.subtract }),
h.button({ _: 'intersect', onclick: tool.intersect }),
h.button({ _: 'difference', onclick: tool.difference }),
])
]),
h.div({ class: "sketch-on" }, [
h.div({ _: "arrange", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'group', onclick: sketch.arrange.group }),
h.button({ _: 'ungroup', onclick: sketch.arrange.ungroup }),
h.hr(),
h.button({ _: 'center', onclick: sketch.arrange.center }),
h.hr(),
h.button({ _: 'flip-h', onclick: sketch.arrange.fliph }),
h.button({ _: 'flip-v', onclick: sketch.arrange.flipv }),
h.hr(),
h.button({ _: 'down', onclick: sketch.arrange.down }),
h.button({ _: 'up', onclick: sketch.arrange.up }),
h.hr(),
h.button({ _: 'top', onclick: sketch.arrange.top }),
h.button({ _: 'bottom', onclick: sketch.arrange.bottom }),
])
]),
h.div({ class: "sketch-on" }, [
h.div({ _: "boolean", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'union', onclick: sketch.boolean.union }),
h.button({ _: 'intersect', onclick: sketch.boolean.intersect }),
h.button({ _: 'difference', onclick: sketch.boolean.difference }),
h.hr(),
h.button({ _: 'even/odd', onclick: sketch.boolean.evenodd }),
h.button({ _: 'nest', onclick: sketch.boolean.nest }),
h.button({ _: 'flatten', onclick: sketch.boolean.flatten }),
])
]),
h.div({ class: "sketch-off" }, [
h.div({ _: "pattern", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'circular', onclick() { api.pattern.circle() } }),
h.button({ _: 'grid', onclick() { api.pattern.grid() } }),
])
]),
// h.div({ class: "sketch-on" }, [
// h.div({ _: "pattern", class: "head" }),
// h.div({ class: "back"}),
// h.div({ class: "pop"}, [
// h.button({ _: 'circular', onclick() { sketch.pattern.circle() } }),
// h.button({ _: 'grid', onclick() { sketch.pattern.grid() } }),
// ])
// ]),
h.div({ class: "sketch-on" }, [
h.div({ _: "extrude", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'sketch', onclick() { sketch.extrude() } }),
h.button({ _: 'selection', onclick() { sketch.extrude({ selection: true }) } }),
])
]),
h.div({ class: "sketch-off" }, [
h.div({ _: "models", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'regroup', onclick: tool.regroup }),
h.span(),
h.button({ _: 'duplicate', onclick: tool.duplicate }),
h.button({ _: 'mirror', onclick: tool.mirror }),
h.button({ _: 'merge', onclick: tool.merge }),
h.button({ _: 'split', onclick: call.edit_split }),
h.span(),
h.button({ _: 'delete', onclick: api.selection.delete }),
])
]),
h.div({ class: "sketch-off" }, [
h.div({ _: "faces", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'invert\nnormals', onclick: tool.invert }),
h.span(),
h.button({ _: 'delete', onclick: api.selection.delete }),
])
]),
h.div({ class: "sketch-off" }, [
h.div({ _: "repair", class: "head" }),
h.div({ class: "back"}),
h.div({ class: "pop"}, [
h.button({ _: 'analyze', onclick: tool.analyze }),
h.button({ _: 'isolate', onclick: tool.isolate }),
h.button({ _: 'patch', onclick: tool.repair }),
// h.button({ _: 'rebuild', onclick: tool.rebuild }),
h.button({ _: 'clean', onclick: tool.clean }),
])
]),
]);
// for group/model/box/area/mesh dashboard grids
function grid(v1, v2, side = [ "pos", "rot"], top = [ "X", "Y", "Z" ], root) {
let v = 'val', l = 'lbl';

View file

@ -1,11 +1,6 @@
:root {
--fg-gray: #495057;
--bg-top: rgba(255,255,255,0.55);
--bg-smoke: rgba(0,0,0,0.25);
--dark-bg-smoke: rgba(255,255,255,0.25);
--hover-menu: white;
--dark-hover-menu: #888;
--gradbar: linear-gradient(to right, #ddd, #666, #ddd);
--menu-blue: #007aff;
--menu-back: #ddd;
}
@font-face {
@ -15,39 +10,10 @@
/** dark overrides */
.dark #logger, .dark #modal_frame, .dark #actions > div, .dark #grouplist > div, .dark #actions .pop {
.dark #logger, .dark #modal_frame, .dark #actions > div, .dark #grouplist > div {
border: 1px solid #999;
}
.dark #actions > div {
background-color: rgba(255,255,255,0.6);
}
.dark #top-right > div {
color: black;
}
.dark #top-right > div:hover {
color: white;
}
.dark #app-info:hover {
background-color: var(--dark-hover-menu);
}
.dark #app-info:hover {
background-color: var(--dark-bg-smoke);
}
.dark #app-menu {
background-color: var(--dark-hover-menu);
border: 1px solid black;
}
.dark #app-menu > div:hover {
background-color: var(--dark-bg-smoke);
}
/** defaults */
a, a:hover, a:visited {
@ -57,6 +23,7 @@ a, a:hover, a:visited {
}
body, div {
font-family: system-ui, Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
position: relative;
display: flex;
padding: 0;
@ -93,60 +60,6 @@ input {
top: 0;
}
#app-info {
top: 50%;
left: 50%;
padding-left: 10px !important;
padding-right: 10px !important;
transform: translateX(-50%) translateY(-50%);
flex-direction: column;
align-items: center;
user-select: none;
}
#app-name {
flex-direction: column;
align-items: center;
width: 100%;
}
#app-info:hover {
border-left: 1px solid rgba(0,0,0,0.5);
border-right: 1px solid rgba(0,0,0,0.5);
background-color: var(--bg-smoke);
}
#app-info:hover #app-menu {
display: flex;
}
#app-menu {
display: none;
font-size: smaller;
position: absolute;
align-items: stretch;
flex-direction: column;
background-color: var(--hover-menu);
white-space: nowrap;
border: 1px solid rgba(0,0,0,0.5);
border-top: 1px dashed !important;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px;
padding: 3px;
width: calc(100% - 6px);
top: 100%;
gap: 3px;
}
#app-menu > div {
flex-direction: column;
align-items: center;
}
#app-menu > div:hover {
background-color: var(--bg-smoke);
}
#app-body {
flex-grow: 1;
}
@ -181,17 +94,27 @@ input {
overflow: hidden;
}
.dark #top {
color: #eee;
background-color: rgba(80,80,80,0.75);
}
#top {
z-index: 50;
color: #000;
font-size: smaller;
flex-direction: row;
font-family: 'Russo One', sans-serif;
background-color: var(--bg-top);
background-color: rgba(255,255,255,0.55);
}
.dark #top-sep {
background-image: linear-gradient(to right, #666, #aaa, #666);
}
#top-sep {
z-index: 2;
height: 1px;
background-image: var(--gradbar);
background-image: linear-gradient(to right, #ddd, #666, #ddd);
}
#top > div {
@ -244,25 +167,76 @@ button:hover {
height: 5px;
}
#top-right {
#top-left {
margin-left: 5px;
flex-direction: row;
padding: 0 !important;
}
.menu {
cursor: default;
align-self: stretch;
align-items: center;
padding: 5px 12px 5px 12px;
border-radius: 6px;
white-space: nowrap;
user-select: none;
}
.dark .menu hr {
border-top: 0.5px solid rgba(255,255,255,0.5);
}
.menu:hover, .menu-items > div:hover {
background-color: var(--menu-blue);
}
.menu:hover .menu-items {
display: flex;
}
.dark .menu-items {
background: #666;
border: 1px solid #999;
}
.menu-items {
display: none;
position: absolute;
padding: 3px;
bottom: 0;
right: 3px;
top: 0;
gap: 8px;
flex-direction: column;
border: 1px solid #bbb;
border-radius: 6px;
background: #eee;
padding: 4px;
top: 100%;
left: 0;
gap: 6px;
}
.menu-items > div {
border-radius: 6px;
padding: 4px 8px 4px 8px;
}
#top-mid {
flex-grow: 1;
}
#top-right {
flex-direction: row;
margin-right: 5px;
padding: 0 !important;
}
#top-right > div {
flex-direction: column;
justify-content: center;
gap: 8px;
align-self: stretch;
align-items: center;
flex-direction: row;
color: #888;
}
#top-right > div:hover {
color: black;
padding: 5px 12px 5px 12px;
border-radius: 6px;
cursor: default;
user-select: none;
white-space: nowrap;
}
/** specific modal dialogs */
@ -301,9 +275,9 @@ button:hover {
grid-template-columns: 1fr 50px;
}
.addgear hr {
/* .addgear hr {
width: 100%;
}
} */
.addgear button, .addgear hr, .addgear code {
grid-column: span 2;
@ -487,11 +461,11 @@ button:hover {
border-bottom: 1px dashed gray;
}
button.selected {
.menu .selected {
background-color: rgba(0,255,0,0.5);
}
button.selected:hover {
.menu .selected:hover {
background-color: rgba(0,255,0,0.8);
}
@ -499,27 +473,6 @@ button.selected:hover {
color: #000 !important;
}
/** modes menu top/center **/
#modes {
position: absolute;
gap: 2px;
top: 0;
left: 50%;
transform: translateX(-50%);
}
#modes > div {
align-items: center;
justify-content: center;
background-color: rgba(255,255,255,0.5);
border: 1px solid rgba(0,0,0,0.5);
border-radius: 3px;
margin-top: 2px;
padding: 3px;
gap: 2px;
}
/** slide in/out logging window **/
#logger {
@ -557,85 +510,6 @@ button.selected:hover {
border: 3px solid rgba(255,255,255,0.1);
}
/** action and keyboard menu **/
#actions {
flex-direction: column;
position: absolute;
margin: 3px 0 0 3px;
left: 0;
top: 0;
gap: 1px;
}
#actions .pop {
display: none;
position: absolute;
align-items: center;
justify-content: center;
flex-direction: column;
transform: translateX(100%);
right: -3px;
top: -1px;
gap: 3px;
margin: 0;
padding: 3px;
border-radius: 3px;
border: 1px solid gray;
background-color: rgba(255,255,255,0.5);
}
#actions .back {
position: absolute;
top: 0;
left: 0;
right: -4px;
bottom: 0;
}
.pop button {
width: 100%;
}
.pop span {
margin: 1px;
height: 1px;
width: 80%;
background-color: rgba(0,0,0,0.5);
}
.dark #actions > div:hover {
background-color: #bbb;
}
#actions > div:hover {
background-color: #eee;
}
#actions > div:hover .pop {
display: flex;
}
#actions > div {
flex-direction: column;
padding: 5px;
gap: 2px;
}
#actions .head {
color: #444;
align-items: center;
justify-content: center;
font-family: 'Russo One', monospace;
font-weight: lighter;
font-size: smaller;
border-radius: 3px;
}
#actions .head:hover {
user-select: none;
}
/** group/model buttons top/right of workspace **/
#grouplist {

View file

@ -24,7 +24,7 @@
<div id="container"></div>
<div id="top">
<div id="top-left"></div>
<div id="app-info"></div>
<div id="top-mid"></div>
<div id="top-right"></div>
</div>
<div id="top-sep"></div>