Front-end side of the gplan-integrated A axis (B3).
- a-axis-view.{js,pug}: dedicated settings page that mounts the
AAxisSettings Svelte component and lives at #a-axis in the V09
settings rail.
- AAxisSettings.svelte: aux.json-backed form (axis letter, port,
homing direction, soft limits, ATC pin map, etc.) with master
Save integration via 'onefin:save-all'.
- main.ts + SettingsView.svelte: register AAxisSettings in the
Svelte component map; SettingsView no longer embeds the W axis
fieldset.
- settings-shell-view: 'A Axis' rail entry; route to a-axis-view.
- app.js: extend settings family to include 'a-axis'; broadcast
onefin:save-all from the master Save button.
- control-view: Home All button waits for the gantry cycle to
finish before firing Home A on a non-virtual setup; A jog
buttons; aux_jog/aux_home/aux_jog_incr methods.
- control-view.pug: A row in the DRO (with set-position + zero +
home actions), A- / A+ tiles in the jog grid (gated on
w.enabled || a.enabled), legacy W row kept for installs that
haven't migrated to the gplan integration.
- style.styl: dro-axis.axis-w color.
176 lines
7.7 KiB
JavaScript
176 lines
7.7 KiB
JavaScript
"use strict";
|
|
|
|
// Wrapper that adds a left-rail navigator around the settings family
|
|
// of views (Settings, Admin General, Admin Network, Tool, IO, Motor,
|
|
// Macros, Help, Cheat Sheet). The inner view is selected by the URL
|
|
// hash (parsed in app.js) and exposed as $root.sub_tab.
|
|
|
|
// Vue 1 has trouble making child components reactive to `$root.sub_tab`
|
|
// changes (whether via computed, watch, or prop binding through
|
|
// `<component :is>`). The shell instead listens to `hashchange`
|
|
// directly and parses the hash itself, mirroring app.js's logic, then
|
|
// keeps a local data prop `sub` that the template binds to. This is
|
|
// the only path that updates the rail's `:class` reactively.
|
|
module.exports = {
|
|
template: "#settings-shell-view-template",
|
|
props: ["config", "template", "state", "index"],
|
|
|
|
components: {
|
|
"settings-view-inner": require("./settings-view"),
|
|
"admin-general-view": require("./admin-general-view"),
|
|
"admin-network-view": require("./admin-network-view"),
|
|
"motor-view": require("./motor-view"),
|
|
"tool-view": require("./tool-view"),
|
|
"io-view": require("./io-view"),
|
|
"macros-view": require("./macros"),
|
|
"help-view": require("./help-view"),
|
|
"a-axis-view": require("./a-axis-view"),
|
|
"cheat-sheet-view": {
|
|
template: "#cheat-sheet-view-template",
|
|
data: function () {
|
|
return { showUnimplemented: false };
|
|
},
|
|
},
|
|
},
|
|
|
|
data: function () {
|
|
return {
|
|
sub: this.$root.sub_tab || "settings",
|
|
ridx: this.$root.index, // local copy of the motor index
|
|
// Whether the controller config has streamed in. The Svelte
|
|
// settings views crash on first paint with the placeholder
|
|
// config (settings.units / settings.easy-adapter / motion.*
|
|
// are all undefined). Gate the inner mount on this flag.
|
|
config_ready: false,
|
|
rail_items: [
|
|
{ sub: "settings", href: "#settings", icon: "fa-display", label: "Display & Units" },
|
|
{ sub: "probing", href: "#probing", icon: "fa-bullseye", label: "Probing" },
|
|
{ sub: "gcode", href: "#gcode", icon: "fa-code", label: "G-code & Motion" },
|
|
{ sub: "macros", href: "#macros", icon: "fa-keyboard", label: "Macros" },
|
|
{ sub: "cheat-sheet", href: "#cheat-sheet", icon: "fa-book", label: "G-code Cheat Sheet" },
|
|
{ sub: "admin-network", href: "#admin-network", icon: "fa-network-wired", label: "Network" },
|
|
{ sub: "admin-general", href: "#admin-general", icon: "fa-shield-halved", label: "General / Firmware" },
|
|
{ sub: "tool", href: "#tool", icon: "fa-bolt", label: "Spindle & Tool" },
|
|
{ sub: "io", href: "#io", icon: "fa-plug", label: "I/O" },
|
|
{ section: "Motors" },
|
|
{ sub: "motor", motor: 0, href: "#motor:0", icon: "fa-arrows-up-down-left-right", label: "Motor 0" },
|
|
{ sub: "motor", motor: 1, href: "#motor:1", icon: "fa-arrows-up-down-left-right", label: "Motor 1" },
|
|
{ sub: "motor", motor: 2, href: "#motor:2", icon: "fa-arrows-up-down-left-right", label: "Motor 2" },
|
|
{ sub: "motor", motor: 3, href: "#motor:3", icon: "fa-arrows-up-down-left-right", label: "Motor 3" },
|
|
// Auxiliary axis (auxcnc ESP32 - exposed to gplan as A).
|
|
// Mounts the AAxisSettings Svelte component on its own page.
|
|
{ sub: "a-axis", href: "#a-axis", icon: "fa-arrows-up-down", label: "A Axis" },
|
|
{ section: " " },
|
|
{ sub: "help", href: "#help", icon: "fa-circle-question", label: "Help" },
|
|
],
|
|
};
|
|
},
|
|
|
|
ready: function () {
|
|
this._onHash = () => this.refresh_from_hash();
|
|
window.addEventListener("hashchange", this._onHash);
|
|
this.refresh_from_hash();
|
|
this._configPoll = setInterval(() => {
|
|
const c = this.$root && this.$root.config;
|
|
const ready = !!(c && c.full_version && c.full_version !== "<loading>"
|
|
&& c.settings && typeof c.settings === "object");
|
|
if (ready !== this.config_ready) this.config_ready = ready;
|
|
}, 200);
|
|
},
|
|
|
|
attached: function () {
|
|
// Vue 1 fires `attached` whenever the component is inserted into
|
|
// the DOM (which happens on every route change because the outer
|
|
// <component :is> recreates the instance). Re-bind the listener
|
|
// here so it works even after detach/attach cycles.
|
|
if (!this._onHash) {
|
|
this._onHash = () => this.refresh_from_hash();
|
|
}
|
|
window.addEventListener("hashchange", this._onHash);
|
|
this.refresh_from_hash();
|
|
},
|
|
|
|
detached: function () {
|
|
if (this._onHash) {
|
|
window.removeEventListener("hashchange", this._onHash);
|
|
}
|
|
},
|
|
|
|
beforeDestroy: function () {
|
|
if (this._onHash) {
|
|
window.removeEventListener("hashchange", this._onHash);
|
|
}
|
|
if (this._configPoll) clearInterval(this._configPoll);
|
|
},
|
|
|
|
methods: {
|
|
refresh_from_hash: function () {
|
|
const hash = location.hash.substr(1) || "settings";
|
|
const parts = hash.split(":");
|
|
this.sub = parts[0] || "settings";
|
|
this.ridx = parts[1] !== undefined ? parts[1] : -1;
|
|
},
|
|
|
|
is_active: function (item) {
|
|
if (!item || item.section) return false;
|
|
if (item.sub !== this.sub) return false;
|
|
if (item.sub === "motor") {
|
|
return "" + item.motor === "" + this.ridx;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
on_rail_click: function (item, ev) {
|
|
if (!item) return;
|
|
// Always preventDefault on rail clicks. Letting the browser
|
|
// anchor-scroll to <div id="settings"> etc. inside .app-body
|
|
// can pull the .app-head out of view; we drive navigation
|
|
// ourselves through location.hash and our hashchange handler.
|
|
if (ev && ev.preventDefault) ev.preventDefault();
|
|
|
|
if (item.anchor) {
|
|
// Soft-link rail items use a #settings hash plus an in-page
|
|
// anchor scroll once the Svelte page has mounted. We scroll
|
|
// ONLY the .settings-content overflow container by setting
|
|
// its scrollTop directly — element.scrollIntoView() walks all
|
|
// ancestor scroll containers and can tug the .app-body / html
|
|
// layout, which under tablet mode pulls the fixed header out
|
|
// of view.
|
|
if (location.hash !== item.href) location.hash = item.href;
|
|
this._a_axis_focus = (item.sub === "a-axis");
|
|
const reset = () => {
|
|
// Force any inadvertent ancestor scroll back to 0 before
|
|
// we move .settings-content explicitly.
|
|
window.scrollTo(0, 0);
|
|
const body = document.querySelector(".app-body");
|
|
if (body) body.scrollTop = 0;
|
|
document.documentElement.scrollTop = 0;
|
|
};
|
|
setTimeout(() => {
|
|
reset();
|
|
const el = document.getElementById(item.anchor);
|
|
const scroller = document.querySelector(".settings-content");
|
|
if (el && scroller) {
|
|
const elTop = el.getBoundingClientRect().top;
|
|
const scTop = scroller.getBoundingClientRect().top;
|
|
scroller.scrollTop = scroller.scrollTop + (elTop - scTop) - 12;
|
|
}
|
|
// Re-assert ancestor scroll = 0 in case the assignment above
|
|
// moved things.
|
|
requestAnimationFrame(reset);
|
|
}, 320);
|
|
} else {
|
|
this._a_axis_focus = false;
|
|
if (location.hash !== item.href) location.hash = item.href;
|
|
// Reset .app-body scroll so each route starts at the top.
|
|
const body = document.querySelector(".app-body");
|
|
if (body) body.scrollTop = 0;
|
|
}
|
|
},
|
|
|
|
showShutdownDialog: function () {
|
|
SvelteComponents.showDialog("Shutdown");
|
|
},
|
|
},
|
|
};
|